苹果发布了免费的 Swif 应用程序开发课程

苹果宣布为社区大学生和高中生推出一套全新的为期一年的 App 开发课程,现在已经上架 iBook 商店。这门课程将向学生们传授使用苹果 Swift 开发应用程序的一些要素。

根据外媒 MacRumors 的报道,苹果公司今日发布了一个新的应用程序开发课程,旨在为那些想要在快速增长的 App 经济中寻求职业发展的学生提供帮助,这个课程可以在 iBooks Store 中免费下载。

这个全年课程名为“应用程序开发与 Swift”,它可以向学生们传授使用苹果旗下日益流行的编程语言 Swift 开发应用程序的一些要素。苹果表示,学习这门课程的学生将学习编码和设计功能完备的应用程序,在软件开发和信息技术方面获得重要的工作技能。

苹果表示,从今年秋季开始,该公司将会为全美 6 个社区学院系统中的近 50 万名学生提供这门课程,其中包括阿拉巴马社区学院系统、哥伦布州立社区学院、哈里斯堡地区社区学院、休斯顿社区学院、梅萨社区学院以及圣马特奥社区学院。

库克解释了为什么苹果会打造这样一套开发课程:

“我们亲眼看到编程对个人以及整个美国经济的影响。App 经济和软件开发是美国增长最快的就业领域之一,我们很高兴为教育工作者和学生提供学习编程的工具”,库克说。“社区大学在帮助学生实现梦想方面发挥了关键作用,我们希望这些课程将会为所有年龄和背景的人打开门路,追求自己喜欢的事物。”

Swift Playground 已经被证明是一款强大的教学工具,自从发布以来有超过一百次下载。而且,超过 1000 所美国学校计划在秋天使用苹果的 Everyone Can Code 教学材料进行教学。能够将 Everyone Can Code 进行扩展至高年级学生将会让这整个项目对教育者比以往更具有吸引力。

苹果官方视频地址

苹果官方地址

欢呼声热烈 谷歌宣布 KOTLIN 成 ANDROID 开发一级语言


谷歌I/O大会一开始就宣布了Android设备超过20亿部的好消息,会议推进至此,果然Android才是大会的压轴大戏么?

首先,今年夏末应该就可以收到Android O的推送,当然,国内用户就先别想了,除非你一直都用原生的。其次,有两个关键词需要注意,一是“Fluid Experience(流畅体验)”,二是“Vitals(核心功能)”。

地图、Netflix、浏览器中的视频等将支持安卓O画中画功能,随后还会有更多应用支持画中画,相当于另一种形态的多任务处理,这是Fluid Experience(流畅体验)的一部分。另外,通知部分也有升级,所谓“Notification Dots(通知点)”有点像是iOS App右上角的提示标,但是长按的话会显示更多信息。

Android O也带来了更加智能化的信息填充系统,未来不仅仅是Chrome中的表格信息,App中的信息也可以自动填充了。另外,新增的”智能文本选择”,可用于更加效率的复制和粘贴。最后,名为“TensorFlow Lite”的机器学习功能专用于手机,将会在之后的一段时间上线,具体功能并未详细提及。

Vitals(核心功能)部分,谷歌更强调电池续航、安全性、启动时间和稳定性。安全性方面,Google Play商店采用机器学习,每天都会扫描500亿次应用程序,告知你手机上的App已经经过扫描,让安全性更具可视化。

启动时间方面,谷歌给出的数据是,在Pixel上,Android O的启动时间是上代的一半。Android O的运行也会更加高效,保持闲置后台回收;太多的后台程序会消耗电池电量, Android O将提供智能节点措施。针对开发者,Play Console Dashboard可以告诉开发者他们的应用耗电、闪退情况;告知开发者有多少用户受到不良应用侵犯,协同开发者一起为用户提供更好的App体验。

然而并没有太多能和用户挂钩的功能性升级,至少大会上提到的这些内容,如果不算那一波更新的emoji,我们目前很难有什么实质性的感受。

最后,谷歌宣布,将Kotlin语言作为安卓开发的一级编程语言。Kotlin由JetBrains公司开发,与Java 100%互通,并具备诸多Java尚不支持的新特性。谷歌称还将与JetBrains公司合作,为Kotlin设立一个非盈利基金会。

JetBrains在2010年首次推出Kotlin编程语言,并在次年将之开源。下一版的Android Studio(3.0)也将提供支持。

IOS开发中,INFO.PLIST配置用户隐私的保护

info.plist 配置【用户隐私的保护】

>= iOS10
Privacy – Bluetooth Peripheral Usage Description –> App需要您的同意,才能访问蓝牙
Privacy – Calendars Usage Description –> App需要您的同意,才能访问日历
Privacy – Camera Usage Description –> App需要您的同意,才能访问相机
Privacy – Microphone Usage Description –> App需要您的同意,才能访问麦克风
Privacy – Photo Library Usage Description –> App需要您的同意,才能访问相册
Privacy – Motion Usage Description –> App需要您的同意,才能访问运动与健身(运动使用)
Privacy – Health Update Usage Description -> 健康更新开发
Privacy – Health Share Usage Description -> 健康分享开发
Privacy – HomeKit Usage Description ->沟通和控制家庭自动化配件
Privacy – Contacts Usage Description   –> 通讯录权限
Privacy – Speech Recognition Usage Description –> 语音转文字权限、语音
Privacy – NSSiriUsageDescription –> Siri的权限
Privacy – Reminders Usage Description –> 提醒事项
Privacy – Media Libaray Usage Description -> 媒体资源库
Privacy – Music Usage Description –> 音乐
Privacy – Location When In Use Usage Description –>定位权限
Privacy – Location Always Usage Description  –>定位权限
Privacy – Location Usage Description –> 位置权限
Privacy – TV Provider Usage Description –> 电视供应商使用权限
Privacy – Video Subscriber Account Usage Description –> 视频用户账号使用权限
>=iOS8
 NSLocationAlwaysUsageDescription –>  允许永远可获取GPS的描述
 NSLocationWhenInUseUsageDescription –>  允许在前台使用时获取GPS的描述 

转 IOS应用程序间共享数据

我们知道iOS由于沙盒的存在,应用程序不能越过自己的区域去访问别的存储空间的内容,不过可能有许多场景我们需要在应用程序之间共享数据,比如多个应用共用用户名密码进行登录等。虽然我们不能直接通过文件系统来分享数据,不过还是有些方法可以实现,为了方便说明,这里同时创建了两个工程Example1和Example2,实现这两个app之间的信息共享,Example1负责写数据,Example2负责读数据,具体的demo代码可以到这里获取

UIPasteboard

剪贴板是应用程序之间传递数据的简单方式,建议不要使用全局的粘贴板,而是自己根据名字创建一个新的粘贴板,防止其它地方全局拷贝的影响。然后把需要共享的内容复制到粘贴板,粘贴板的内容可以是文本、URL、图片和UIColor等,另一个app就可以根据粘贴板的名字去读取相关的信息。

Example1设置粘贴板的内容:

    UIPasteboard *pasteboard = [UIPasteboard pasteboardWithName:@"myPasteboard" create:YES];
    pasteboard.string = @"myShareData";
Example2读取粘贴板的内容:

    UIPasteboard *pasteboard = [UIPasteboard pasteboardWithName:@"myPasteboard" create:NO];
    NSString *content = pasteboard.string;

Custom URL Scheme

URL Scheme能够让我们通过自定义的协议在应用程序间传递信息,当你想要发送数据给一个实现了自定义URL Scheme的应用时,只需要创建好合适格式的URL,然后调用openURL:方法,系统就会载入注册了该scheme的应用然后将你的URL传递给他,比如如下的代码,todolist是一个其它应用注册的scheme,通过openURL便可以将服务请求发送到该应用(自定义的URL Scheme要能够唯一标示该APP,如果你的URL Scheme跟别人冲突了,那么你的app就不一定会被调起,iOS并不保证调用哪个应用)。

NSURL *myURL = [NSURL URLWithString:@”todolist://newid=20″];
[[UIApplication sharedApplication] openURL:myURL];
注册URL Scheme
为了能够处理URL请求,我们需要先注册自定义的URL Scheme,只需要在Info tab下的URL Types添加即可,比如我们这里注册Example2的URL Scheme。

Identifier用于标示名称,为了唯一性可以采用反转域名的形式,另外我们设置URL Scheme为Example2,以及role为Viewer(Viewer表示只能读取改URL但不能修改,Editor可以对URL进行读写),这样Example2就能够接受类似”Example2:\”的URL请求了,可以在浏览器中输入”Example2:\”链接打开app。

处理URL请求
当有URL请求到来时,所有的URL都会传递给你的app delegate,我们需要实现application:openURL:sourceApplication:annotation:方法来接收处理对应的URL,所以假如你想通过URL方式来传递数据,只需要将数据添加到URL中即可,另外的通过解析该URL来获取对应的数据。

Eaxmple1发送携带参数的URL到Example2:

NSString *string = @”Example2://name=jiangbin#age=1″;
NSURL *url = [NSURL URLWithString:string];
[[UIApplication sharedApplication] openURL:url];
Example2处理URL请求并解析:

-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    if ([[url scheme] isEqualToString:@"Example2"]) {
        NSString *content = [url resourceSpecifier];
        //解析content获取数据
        //...
        return YES;
    }
    return NO;
}

Shared Keychain Access

iOS的keychain提供一种安全保存信息的方式,可以保存密码等数据,而且keychain中的数据不会因为你删除app而丢失,你可以在重新安装后继续读取keychain中的数据。通常每个应用程序只允许访问自己在keychain中保存的数据,不过假如你使用同一个证书的话,不同的app也可以通过keychain来实现应用间的数据共享。

为了实现keychain共享数据,我们需要开启Keychain Sharing,开启方法如下,然后添加设置相同的Keychain Group,不过别忘记了添加Security.framework。

Example1保存数据到keychain(为了简单使用SSKeychian)

  • (void)setKeyChain
    {
    [SSKeychain setPassword:@”shareData” forService:@”myservice” account:@”jiangbin”];
    }
    Example2读取数据
- (IBAction)getByKeychain:(id)sender
{
    NSString *myData = [SSKeychain passwordForService:@"myservice" account:@"jiangbin"];
}

App Groups

iOS8之后苹果加入了App Groups功能,应用程序之间可以通过同一个group来共享资源,app group可以通过NSUserDefaults进行小量数据的共享,如果需要共享较大的文件可以通过NSFileCoordinator、NSFilePresenter等方式。
开启app groups,需要添加一个group name,app之间通过这个group共享数据:

Example1根据group name设置内容:

- (void)setAppGroup{
    NSUserDefaults *myDefaults = [[NSUserDefaults alloc]
    initWithSuiteName:@"group.com.jiangbin.SharedData"];
    [myDefaults setObject:@"shared data" forKey:@"mykey"];
}

Example2根据group name读取数据

- (void)getByAppGroup
 {
    NSUserDefaults *myDefaults = [[NSUserDefaults alloc]
                                  initWithSuiteName:@"group.com.jiangbin.SharedData"];
    NSString *content = [myDefaults objectForKey:@"mykey"];
}

ios 原生通知 ReactNative


本地模块也可以给JavaScript发送事件通知。最直接的方式是使用eventDispatcher:
- (void)calendarEventReminderReceived:(NSNotification *)notification
{
//  NSString *eventName = notification.userInfo[@"name"];
   NSString *eventName = @"123";

  [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder"
                                               body:@{@"name": eventName}];
}

在JavaScript中可以这样订阅事件:

var subscription = DeviceEventEmitter.addListener(
  'EventReminder',
  (reminder) => {
    alert(reminder.name);
    // console.log(reminder.name)
  }
);

ios原生 源码

.h

#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"


@interface CalendarManager : NSObject<RCTBridgeModule>

@end

.m

#import "CalendarManager.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"

@interface CalendarManager ()

//@property (nonatomic,strong)bridge;
@end

@implementation CalendarManager

@synthesize bridge = _bridge;


RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(notification){
  
//    [[NSNotificationCenter defaultCenter] postNotificationName:@"EventReminder" object:nil userInfo:nil];
  [self calendarEventReminderReceived:nil];
}


- (void)calendarEventReminderReceived:(NSNotification *)notification
{
//  NSString *eventName = notification.userInfo[@"name"];
   NSString *eventName = @"123";

  [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder"
                                               body:@{@"name": eventName}];
}


@end

js 调用代码

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */



import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  TouchableOpacity,
  NativeAppEventEmitter,
  DeviceEventEmitter,
  View
} from 'react-native';

import CalendarManagerModel  from './CalendarManagerModel';


var subscription = DeviceEventEmitter.addListener(
  'EventReminder',
  (reminder) => {
    alert(reminder.name);
    // console.log(reminder.name)
  }
);

class RNProject extends Component {
  render() {
    return (
      <View style={styles.container}>
          <TouchableOpacity onPress={()=>{
            CalendarManagerModel.notification();
          }} >
            <Text style={styles.emptyTxt}>{
              '点击'
            }</Text>
       </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('RNProject', () => RNProject);

 

 

iOS 生成七牛 Token

最近决定使用七牛云存储来管理app 中的资源文件。先吐槽一下七牛的文档那叫一个坑啊! 看文档,下载demo 看源码 全都说一半,看半天不明所以,三言两语说不完。

文章主要还是说一说在使用七牛sdk 中踩过得坑吧。七牛文件管理原理在此就不多说了。

七牛SDK 对文件的上传下载都需要一个token的玩意。文档只是说token通过服务器获取。一脸懵懂 ,哪个服务器啊,自己的还是七牛的不明所以。文档上也根本没说客服端怎么生成token,说是不安全,但七牛也不反对客服端生成token,只是说不安全原因,推荐服务器生成回传。但即便这样你别惜字如金至少也要写的清楚点才叫文档吧。最后找了半天总算找到七牛在线的token生成器:戳这里

1720EFBE-B166-43CC-B4E3-5060A0E5A6B6

几个有用的地方已经标出,都是必有的。

ak和sk在空的密钥里可以找到。如图:

Screen+Shot+2016-05-19+at+4.01.46+PM

buckename是之前申请的域名,比如我申请的测试域名是test.qiniudn.com,那么buckename就是test。key是上传的文件名,虽然这里属性是可选,但作为使用者上传了肯定要有个名字以便以后获取下载。填写好需要的信息就可以生成uploadtoken,下图是生成的效果图

AE401719-4130-4AAA-B2A5-6EAA29A3E66F

然后就是demo的修改,整个demo只需要修改两个地方就可以上传到个人申请的空间,不需要修改任何url,七牛是按照buckename找到你申请的空间的

1.- (void)viewDidLoad方法中修改self.token的值,修改为上图生成的token,

2.- (IBAction)simpleUpload:(id)sender上传触发方法修改_filePath和key

[self.sUploader uploadFile:_filePath key:@”daohanglan@2x.png” extra:nil];

_filePath确定是要上传文件的路径,key是要上传文件的文件名,我上传的是一张图片,这里写的应该比较清楚。

上传完成之后可以查询自己的空间

 

cocospod 安装和使用

安装前准备

ruby 安装

要安装coocspod 首先需要安装ruby,可以先安装xcode,在安装macport 下载地址,最后执行命令 port install ruby

安装CocoaPods

1、安装

CocoaPods是用Ruby实现的,要想使用它首先需要有Ruby的环境。幸运的是OS X系统默认的已经可以运行Ruby了,因此我们只需要执行以下命令:

sudo gem install cocoapods

CocoaPods是以Ruby gem包的形式被安装的。在安装执行的过程中,可能会问我们是不是更新rake,输入y即可。这是因为rake gem包会在安装的过程中检查更细,如果有可用的新版本就会出现刚才的选项。

在安装进程结束的时候,执行命令:

pod setup  

顺利执行完成 安装就成功了!

 

安装过程中可能遇到的问题

执行完install命令半天没反应

或报错

$ sudo gem install cocoapods
Password:
ERROR:  Could not find a valid gem 'cocoapods' (>= 0), here is why:
          Unable to download data from https://rubygems.org/ - Errno::ETIMEDOUT: Operation timed out - connect(2) (https://rubygems.org/latest_specs.4.8.gz)

这有可能是因为Ruby的默认源使用的是cocoapods.org,国内访问这个网址有时候会有问题,网上的一种解决方案是将远替换成淘宝的,替换方式如下:

第一步执行:

gem sources --remove https://rubygems.org/

返回结果:

https://rubygems.org/ removed from sources

第二步执行命令:

gem sources -a https://ruby.taobao.org/

返回:

https://ruby.taobao.org/ added to sources

验证是否执行成功

gem sources -l

结果:

*** CURRENT SOURCES ***

https://ruby.taobao.org/

 

升级gem

gem是管理Ruby库和程序的标准包,如果它的版本过低也可能导致安装失败,解决方案自然是升级gem,执行下述命令即可:

 sudo gem update --system  
sudo gem install cocoapods  

 

升级CocoaPods

升级很简单,再次执行安装命令即可:

 sudo gem install cocoapods  

需要注意的是,如果安装的时候使用了sudo,升级的时候一样需要使用该关键字,不然升级完了以后又会出现路径不匹配问题。

AFNetworking getting json value. URL doesn’t work

AFNetworking 请求时错误

Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable content-type: text/html" UserInfo=0x7fd35da285d0 {com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x7fd35da37ea0> { URL: https://crm-staging.xiaoshouyi.com/mobile/pack/get-all-packs.action?_vs=1.0&appType=0&appVersion=1.0&inhouse=0&source=2 } { status code: 200, headers {

    "Cache-Control" = "no-cache";

    Connection = "keep-alive";

    "Content-Encoding" = gzip;

    "Content-Type" = "text/html;charset=UTF-8";

    Date = "Fri, 22 Apr 2016 08:23:14 GMT";

    Expires = "Thu, 01 Jan 1970 00:00:00 GMT";

    Pragma = "no-cache";

    Server = "nginx/1.8.0";

    "Set-Cookie" = "c-user=407138; Domain=.ingageapp.com; Path=/, JSESSIONID=AFDA5A7AB06D822234E740D5F9E3D0CE; Path=/mobile/; HttpOnly";

    "Transfer-Encoding" = Identity;

    Vary = "Accept-Encoding";

} }, NSErrorFailingURLKey=https://crm-staging.xiaoshouyi.com/mobile/pack/get-all-packs.action?_vs=1.0&appType=0&appVersion=1.0&inhouse=0&source=2, com.alamofire.serialization.response.error.data=<7b227363 6f646522 3a223022 2c22626f 6479223a 7b227365 6c656374 4964223a 31303031 2c227061 636b4c69 7374223a 5b7b2269 64223a31 35363839 382c226e 616d6522 3a224e65 77417070 222c2269 636f6e22 3a22706c 6174666f 726d5f69 636f5f79 696e6778 69616f22 7d2c7b22 6964223a 31303031 2c226e61 6d65223a 2243524d 222c2269 636f6e22 3a22706c 6174666f 726d5f69 636f5f63 726d227d 5d7d7d>, NSLocalizedDescription=Request failed: unacceptable content-type: text/html}

解决办法:

Serilization->AFURLResponseSerialization.m, 行数 215,

修改

以前代码

//    self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

  self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html", nil];

 

开发中遇到的路径问题

在工程师开发代码时或多或少都会遇到获取指定文件的路劲问题:

获取路劲一般有2种方法 相对路径和绝对路径

相对路径和绝对路径,往往都是初学者最困惑的知识点之一。

相对路径和绝对路径的定义:

(1)相对路径,就是在同一个网站下,不同文件之间的的位置定位。引用的文件是相对当前网页的位置而言的,根据这个相对位置得出相对路径。

(2)绝对路径,指的是完整的路径。

我画了一张简单路劲图表:

屏幕快照 2016-04-08 下午4.39.23

 

在找路劲时有几个符号,几个符号的含义:
/‘代表网站根目录:”/Images/SampleImage.jpg”
~‘代表应用程序根目录,~ 运算符只能为服务器控件识别,并且位于服务器代码中.不能将 ~ 运算符用于客户端元素. (根 root目录)
..‘代表上一级目录:”/Root/1/2″
.‘代表当前目录:”/Root/1/2/3″

‘../../’代表返回上上层目录 /Root

按照上图如果要找到 路劲中C 文件相对路劲写法

‘../../A/B/C’

 

 

 

iOS高级编程规范


iOS高级编程规范

* * *

# &lt;a name="1" id="1"&gt;iOS架构设计与分层&lt;/a&gt;

架构的几个原则,根据优先次序高低排列:

1.(逻辑)拆分越细越好
  1. 依赖关细越少越好
  2. 交互越少越好 … (交互是指项目中页面逻辑跳转并非交互设计) 这样做有如下几点好处:
  3. 对于大中型软件,层次越多,每一层就更单纯,更容易维护。
    `*   团队成员只需了解一小部分业务,就能顺利进行开发。
    *   相对底层的模块,可以更好的重用。
    *   层次分的越多,开发者对抽象的理解就更深入。
    
    `
* * *

项目工程目录MVC架构
  • 建立Request文件夹,存放网络请求
  • 建立Model工程 DataSource对于数据的抽象,不需要知道它是从网络、数据库还是缓存中得到的。 存放数据存储管理类 网络请求解析封装类
  • 建立Libs(Library)文件夹,存放第三方库
  • 建立Tools文件夹,存放自己封装的类库 通常在Tools文件下还会分为 扩展(categories)类与 自定义封装类
  • 建立Constant文件,放在底层目录,用于 存放常量, 宏定义文件, 全局单例等
  • 建立Views文件夹,分TableCell,与工程View 存放列表Cell,item, 工程所需View
  • 建立BaseViewController,放在底层公共目录
  • 建立ViewControllers文件夹,存放所有的ViewControl 每个Controller页面同样可有对应的管理文件
  • 在SupportingFiles文件夹下建立 Images、Music和Video等相关文件夹, 分别存放图片、 音频、 视频等资源。 示例:
* * *

iOS命名规则

* * *

代码命名基础知识
<pre>`**[苹果官方问的命名规范][1]**

**命名规范**

1、类名、协议名:遵循大驼峰命名法; 2、常量:这里的常量指的是宏(#define)、枚举(enum)、常量(const)等,使用小写”k“作为前缀,名称遵循大驼峰命名法。 3、方法
`</pre>
  • 方法名和方法参数遵循相同的规则,使用小写开头的小驼峰法;
  • 方法名和参数尽量读起来像是一句话;
  • 方法名不允许使用“get“前缀;
  • -或+与返回类型间留一个空格,但参数列表之间不要留间隔;
  • 如果参数过多,推荐每个参数各占一行; 4、变量: 类成员变量,属性,局部变量,使用小写开头的小驼峰法,其中类成员变量在名称最后加一个下划线,比如:myLovalVariable, myInstanceVariable ;变量名的名称尽量可以推测其用途,具有描述性。 例如:
    `    -(void)doSomethingWithString:(NSString

    )theString(NSInteger _)theInteger { … } ; ` 示例:

    <a name=”2.2″ id=”2.2″>代码命名原则</a>

    1.1 清晰

    尽量清晰又简洁,无法两全时清晰更重要 例如: 通常不应缩写名称,即使方法名很长也应完整拼写 你可能认为某个缩写众所周知,但其实未必,特别是你周围的开发者语言文化背景不同时 有一些历史悠久的缩写还是可以使用的 API命名避免歧义,例如一个方法名有多种理解

  • 1.2一致

    尽力保持Cocoa编程接口命名一致 如果有疑惑,请浏览当前头文件或者参考文档 当某个类的方法使用了多态时,一致性尤其重要 不同类里,功能相同的方法命名也应相同

    1.3 避免自引用(self Reference)

    命名不应自引用 这里的自引用指的是在词尾引用自身 Mask与Notification忽略此规则

    <a name=”2.3″ id=”2.3″>前缀</a>

    前缀是编程接口命名的重要部分,它们区分了软件的不同功能区域: 前缀可以防止第三方开发者与Apple的命名冲突 同样可以防止Apple内部的命名冲突 前缀有指定格式 它由二到三个大写字母组成,不使用下划线和子前缀 命名类、协议、函数、常量和typedef结构体时使用前缀 方法名不使用前缀(因为它存在于特定类的命名空间中) 结构体字段不使用前缀

    <a name=”2.4″ id=”2.4″>书写约定</a>

    在命名API元素时, 使用驼峰命名法(如runTheWordsTogether),并注意以下书写约定: 小驼峰命名法(CamelCase):第一个单词小写字母开头,其他单词首字母大写; 大驼峰命名法(PascalCase):所有首字母大写。

    方法名

    小写第一个字母,大写之后所有单词的首字母,不使用前缀 如果方法名以一个众所周知的大写缩略词开始,该规则不适用 避免使用下划线作为前缀,有意思私人的方法名(使用下划线字符作为一个实例变量名称的前缀是允许的) 。苹果公司保留使用这种约定。由第三方使用可能会导致命名空间冲突;他们可能会无意中覆盖现有的私有方法有一个属于自己的类,这会具有灾难性的后果。查看约定私有方法的建议,遵循私有API 。 如:

    `fileExistsAtPath:isDirectory:`

    对于函数和常量的名称,使用相同的前缀相关类和大写的​​单词首字母。

    `NSRunAlertPanel
     NSCellDisabled`

    #### 标点符号 由多个单词组成的名称,别使用标点符号作为名称的一部分 分隔符(下划线、破折号等)也不能使用 避免使用下划线作为私有方法的前缀,Apple保留这一方式的使用 强行使用可能会导致命名冲突,即Apple已有的方法被覆盖,这会导致灾难性后果 实例变量使用下划线作为前缀还是允许的 ## <a name=”2.5″ id=”2.5″>类和协议的名称</a> class class的名称应该包含一个名词,用以表明这个类是什么(或者做了什么),并拥有合适的前缀 如NSString、NSDate、NSScanner、UIApplication、UIButton 不关联class的protocol 大多数protocol聚集了一堆相关方法,并不关联class 这种protocol使用ing形式以和class区分开来 ![](http://192.168.0.102:7000/iOS/iOS/uploads/c940e72fa65be20b237e9e62ff193863/link-6.png) 关联class的protocol 一些protoco聚集了一堆无关方法,并试图与某个class关联在一起,由这个class来主导 这种protocol与class同名 如NSObject protocol ## <a name=”2.6″ id=”2.6″>头文件</a> 头文件的命名极其重要,因为它可以指出头文件包含的内容: 声明一个孤立的class或protocol 将声明放入单独的文件 使头文件名与声明的class/protocol相同 ![](http://192.168.0.102:7000/iOS/iOS/uploads/1cbbd16b788f47476100b105b5616d42/headerFile.png) 声明关联的class或protocol 将关联的声明(class/category/protocol)放入同一个头文件 头文件名与主要的class/category/protocol相同 图中能 NSString 包含了NSString 和NSMutableString Classes NSLock.h 包含NSLocking protocol 和NSLock,NSConditionLock,NSrecursiveLock Classes![](http://192.168.0.102:7000/iOS/iOS/uploads/4a9bc6226f8c686a9b8a7f9c423c3bdb/headerFile1.png) Framework头文件 每个framework都应该有一个同名头文件 Include了框架内其他所有头文件 ![](http://192.168.0.102:7000/iOS/iOS/uploads/1a4284f3baf9625c1d40e6a54a9d2d07/headerFile2.png) 添加API到另一个framework 如果要在一个framework中为另一个framework的class catetgory添加方法,加上单词“Additions” 如Application Kit的NSBundleAdditions.h 文件 关联的函数、数据类型 如果你有一组关联的函数、常量、结构体或其他数据类型,将它们放到一个名字合适的头文件中 如Application Kit的NSGraphics.h ## <a name=”2.7″ id=”2.7″>命名方法</a> #### 1.通用原则 以小写字母开始,之后单词的首字母大写 以众所周知的缩写开始可以大写,如TIFF、PDF 私有方法可以加前缀 如果方法代表对象接收的动作,以动词开始 不要使用 do 或 does 作为名字的一部分,因为助动词在这里很少有实际意义 同样的,也别在动词之前使用副词和形容词 如果方法表示让对象执行一个动作,使用动词打头来命名,注意不要使用 do , does 这 种多余的关键字,动词本身的暗示就足够了:

    ` // 动词打头的方法表示让对象执行一个动作
    *   (void)invokeWithTarget:(id)target;
    
    • (void)selectTabViewItem:(NSTabViewItem *)tabViewItem; &lt;/pre&gt; 如果方法是为了获取对象的一个属性值,直接用属性名称来命名这个方法,注意不要添加 get 或者其他的动词前缀: 应该,使用属性名来命名方法 &lt;pre&gt; – (NSSize)cellSize; &lt;/pre&gt; 不应该,添加了多余的动词前缀 &lt;pre&gt; – (NSSize)calcCellSize;
    • (NSSize)getCellSize; &lt;/pre&gt; 对于有多个参数的方法,务必在每一个参数前都添加关键词,关键词应当清晰说明参数的作用 &lt;pre&gt; // 应该,保证每个参数都有关键词修饰
    • (void)sendAction:(SEL)aSelector toObject:(id)anObject forAllCells:(BOOL)flag; // 不应该,遗漏关键词
    • (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; // 应该
    • (id)viewWithTag:(NSInteger)aTag; // 不应该,关键词的作用不清晰
    • (id)taggedView:(int)aTag; &lt;/pre&gt; 不要用 and 来连接两个参数,通常 and 用来表示方法执行了两个相对独立的操作(从设计上来说,这时候应 &lt;pre&gt; // 应该,不要使用 “and” 来连接参数
    • (int)runModalForDirectory:(NSString )path andFile:(NSString )name andTypes:(NSArray *)fileTypes; // 不应该,使用 “and” 来表示两个相对独立的操作
    • (BOOL)openFile:(NSString )fullPath withApplication:(NSString )appName andDeactivate:(BOOL)flag; `

      <a name=”2.8″ id=”2.8″>方法的参数</a>

      方法的参数命名也有一些需要注意的地方 :

  • 和方法名类似,参数的第一个字母小写,后面的每一个单词首字母大写
    *   不要再方法名中使用类似 pointer,ptr这样的字眼去表示指针,参数本身的类型足以说明
    *   不要使用只有一两个字母的参数名
    *   不要使用简写,拼出完整的单词
    
    • 下面列举了一些常用参数名:
      ...action:(SEL)aSelector
      ...alignment:(int)mode
      ...atIndex:(int)index
      ...content:(NSRect)aRect
      ...doubleValue:(double)aDouble
      ...floatValue:(float)aFloat
      ...font:(NSFont *)fontObj
      ...frame:(NSRect)frameRect
      ...intValue:(int)anInt
      ...keyEquivalent:(NSString *)charCode
      ...length:(int)numBytes
      ...point:(NSPoint)aPoint
      ...stringValue:(NSString *)aString
      ...tag:(int)anInt
      ...target:(id)anObject
      ...title:(NSString *)aString

      <a name=”2.9″ id=”2.9″>委托方法</a>

      委托方法 (或委派方法) 是指那些对象调用在其委托 (如果委托实现他们) 发生某些事件时。他们有一种独特的形式,这同样适用于调用对象的数据源中的方法: 开始通过确定发送消息的对象的类的名称: 省略了前缀的类名和首字母小写

      ` - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
          *   (BOOL)application:(NSApplication _)sender openFile:(NSString _)filename; `&lt;/pre&gt; 类名称省略前缀,第一个字母是小写。 除非该方法仅有一个参数,发件人一个冒号被贴的类别名称 (参数对委托对象的引用)。 以发送消息的对象开始的规则不适用下列两种情况 只有一个sender参数的方法 &lt;pre&gt;` - (BOOL)applicationOpenUntitledFile:(NSApplication _)sender; `&lt;/pre&gt; 响应notification的方法(方法的唯一参数就是notification) &lt;pre&gt;` - (void)windowDidChangeScreen:(NSNotification _)notification; `&lt;/pre&gt; 使用单词 did 和 will 来通知delegate "did"will"用于调用以通知委托, did 表示某些事已发生 will 表示某些事将要发生 &lt;pre&gt;` - (void)browserDidScroll:(NSBrowser *)sender;
      
      • (NSUndoManager )windowWillReturnUndoManager:(NSWindow )window; &lt;/pre&gt; 虽然您可以使用"did"或"will"方法被调用时,要问要做代表另一个对象的委托,"should"是首选。 &lt;pre&gt; – (BOOL)windowShouldClose:(id)sender; ` 询问delegate是否可以执行某个行为时可以使用 did 或 will,不过 should 更完美

        <a name=”3.0″ id=”3.0″>集合方法</a>

        为了管理集合中的元素,集合应该有这几个方法

  • (void)addElement:(elementType)anObj;
  • (void)removeElement:(elementType)anObj;
  • (NSArray )elements;例如:
    ` - (void)addLayoutManager:(NSLayoutManager

    )obj;

    *   (void)removeLayoutManager:(NSLayoutManager *)obj;
    
    • (NSArray )layoutManagers; &lt;/pre&gt; 如果集合是无序的,返回一个NSSet比NSarray更好 如果需要在集合中的特定位置插入元素,使用类似下面的方法 &lt;pre&gt; – (void)insertLayoutManager:(NSLayoutManager )obj atIndex:(int)index;
    • (void)removeLayoutManagerAtIndex:(int)index; &lt;/pre&gt; 有对与的实现细节要牢记集合方法: 这些方法通常意味着插入对象的所有权,因此添加或插入他们的代码必须保留它们,并删除他们的代码也必须释放他们。 如果插入的对象需要有指针恢复为主要对象,你这样做 (通常) 一套...方法设置回指针,但不会保留。在 insertLayoutManager:atIndex: NSLayoutManager 类的方法,就是这样在这些方法中: &lt;pre&gt; – (void)setTextStorage:(NSTextStorage *)textStorage;
    • (NSTextStorage )textStorage; &lt;/pre&gt; 你通常不会将 setTextStorage: 直接,但可能要重写它。 收集方法的上述约定的另一个例子来自 NSWindow 类: &lt;pre&gt; – (void)addChildWindow:(NSWindow )childWin ordered:(NSWindowOrderingMode)place;
    • (void)removeChildWindow:(NSWindow *)childWin;
    • (NSArray *)childWindows;
    • (NSWindow *)parentWindow;
    • (void)setParentWindow:(NSWindow *)window; `

      <a name=”3.1″ id=”3.1″>getter和setter方法( Accessor Methods )</a>

      getter和setter方法是指用来获取和设置类属性值的方法,属性的不同类型,对应着不同的存取方法规范:

      ` // 属性是一个名词时的存取方法范式
    • (type)noun;
    • (void)setNoun:(type)aNoun; // 例子
    • (NSString *)title;
    • (void)setTitle:(NSString *)aTitle; // 属性是一个形容词时存取方法的范式
    • (NSString *)title;
    • (void)setTitle:(NSString *)aTitle; // 例子
    • (BOOL)isAdjective;
    • (void)setAdjective:(BOOL)flag; // 属性是一个动词时存取方法的范式
    • (BOOL)verbObject;
    • (void)setVerbObject:(BOOL)flag; // 例子
    • (BOOL)showsAlpha;
    • (void)setShowsAlpha:(BOOL)flag; &lt;/pre&gt; getter和setter方法时不要将动词转化为被动形式来使用: &lt;pre&gt; // 正确
    • (void)setAcceptsGlyphInfo:(BOOL)flag;
    • (BOOL)acceptsGlyphInfo; // 错误,不要使用动词的被动形式
    • (void)setGlyphInfoAccepted:(BOOL)flag;
    • (BOOL)glyphInfoAccepted; `

      <a name=”3.2″ id=”3.2″>私有方法</a>

      不要使用下划线作为私有方法的前缀,Apple保留这一使用方式 因为若是你的私有方法名已被Apple使用,覆盖它将会产生极难追踪的BUG 如果继承自大型Cocoa框架(如UIView),请确保子类的私有方法名与父类不一样 可以为私有方法加一个前缀,如公司名或项目名:XX_ 例如你的项目叫做Byte Flogger,那么前缀可能是:BF_addObject 总之,为子类的私有方法添加前缀是为了不覆盖其父类的私有方法

      <a name=”3.3″ id=”3.3″>函数</a>

      函数的命名类似方法,但有两点要注意 你使用的类和常量拥有相同的前缀 前缀后的首字母大写 许多函数名以描述其作用的动词开始

      NSHighlightRect
      NSDeallocateObjec

      查询属性的函数有进一步的命名规则 如果函数返回首个参数的属性,省略动词

      unsigned int NSEventMaskFromType(NSEventType type)
      float NSHeight(NSRect aRect)

      如果通过reference返回了值,使用 “Get”

      const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp)

      如果返回的是boolean值,应该灵活使用动词

      BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)

      <a name=”A1″ id=”A1″>声明属性和实例变量</a>

      <a name=”A1.1″ id=”A1.1″>Property</a>

      Property命名规则与第二章accessor methods一样(因为两者紧密联系) 如果property表示为一个名词或动词,格式如下 @property (…) 类型 名词/动词 ;

      @property (strong) NSString *title;
      @property (assign) BOOL showsAlpha;

      如果property表示为一个形容词 可省略 ”is” 前缀 但要指定getter方法的惯用名称

      @property (assign, getter=isEditable) BOOL editable;

      <a name=”A1.2″ id=”A1.2″>实例变量</a>

      通常不应该直接访问实例变量 init、dealloc、accessor methods等方法内部例外 实例变量以下划线 “_” 开始 确保实例变量描述了所存储的属性

      @implementation MyClass {
      BOOL _showsTitle;
      }

      如果想要修改property的实例变量名,使用 @synthesize语句

      @implementation MyClass
      @synthesize showsTitle=_showsTitle;

      为一个class添加实例变量时,有几点需要注意: 避免声明公有实例变量 开发者关注的应该是对象接口,而不是其数据细节 你可以通过使用property来避免声明实例变量 如果需要声明实例变量,指定关键字@private 或 @protected 如果你希望子类可以直接访问某个实例变量,使用 @protected 关键字 如果一个实例变量是某个类可访问的属性,确保写了accessor methods 如果有可能,还是使用property

      <a name=”A2″ id=”A2″>常量</a>

      <a name=”A2.1″ id=”A2.1″>枚举常量</a>

      使用枚举来关联一组integer常量 枚举常量和typedef遵循函数的命名规范,下面的例子是 NSMatrix.h

      typedef enum _NSMatrixMode {
      NSRadioModeMatrix = 0,
      NSHighlightModeMatrix = 1,
      NSListModeMatrix = 2,
      NSTrackModeMatrix = 3
      } NSMatrixMode;

      Typedef 标记 (在上述示例中的 _NSMatrixMode) 是不必要的注意。 你可以为bit masks之类的东西创建一个匿名枚举

      enum {
      NSBorderlessWindowMask = 0,
      NSTitledWindowMask = 1 &amp;amp;lt;&amp;amp;lt; 0,
      NSClosableWindowMask = 1 &amp;amp;lt;&amp;amp;lt; 1,
      NSMiniaturizableWindowMask = 1 &amp;amp;lt;&amp;amp;lt; 2,
      NSResizableWindowMask = 1 &amp;amp;lt;&amp;amp;lt; 3
      };

      <a name=”A2.2″ id=”A2.2″>使用const创建的常量</a>

      使用const关键字的常量 使用const关键字来创建一个float常量 你可以使用const关键字来创建一个与其他常量不相关的integer常量,否则,使用枚举 使用const关键字的常量也遵循函数的命名规则

      const float NSLightGray;

      <a name=”A2.3″ id=”A2.3″>其他类型的常量</a>

      通常不应使用 #define 预编译指令来创建常量 integer常量,使用枚举 float常量,使用 const 修饰符 对 #define 预编译指令,大写所有字母 比如 DEBUG 判断

      #ifdef DEBUG

      注意的时,cocoa官网宏 字首和字尾都有双下划线

      __MACH__

      定义NSString常量来作为Notification和Key值 这样做可以有效防止拼写错误

      APPKIT_EXTERN NSString *NSPrintCopies;

      <a name=”A3″ id=”A3″>通知和异常</a>

      <a name=”A3.1″ id=”A3.1″>通知</a>

      Notification的格式 类/头文件名+进行状态+通知名称+Notification

      [Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification

      如:

      NSApplicationDidBecomeActiveNotification
      NSWindowDidMiniaturizeNotification
      NSTextViewDidChangeSelectionNotification
      NSColorPanelColorDidChangeNotificatio

      <a name=”A3.2″ id=”A3.2″>异常</a>

      异常格式 头+定义名称+Exception

      [Prefix] + [UniquePartOfName] + Exception

      例如

      NSColorListIOException
      NSColorListNotEditableException
      NSDraggingException
      NSFontUnavailableException
      NSIllegalSelectorException

      <a name=”A3.3″ id=”A3.3″>ios缩写</a>

      <a name=”A4″ id=”A4″>iOS代码规范</a>

      指针 “ * ” 号的位置

      如:

      NSString *varName;

      <a name=”A4.1″ id=”A4.1″>代码书写格式</a>

  • 使用空格而不是制表符 Tab 不要在工程里使用 Tab 键,使用空格来进行缩进。在 Xcode > Preferences > Text Editing 将 Tab 和自动缩进都设置为 2 个空格。(Google 的标准是使用两个空格来缩进,但这里还是推荐使用 Xcode 默认的设置。)方法大括号和其他大括号(if/else/switch/while 等.)总是在同一行语句打开但在新行中关闭。
    应该
     if (user.isHappy) {
     //Do something
     } else {
     //Do something else
     }
    不应该:
     if (user.isHappy)
     {
     //Do something
     }
     else {
     //Do something else
     }

    在方法之间应该有且只有一行,这样有利于在视觉上更清晰和更易于组织。在方法内的空白应该分离功能,但通常都抽离出来成为一个新方法。 优先使用auto-synthesis。但如果有必要,@synthesize和@dynamic应该在实现中每个都声明新的一行。 应该避免以冒号对齐的方式来调用方法。因为有时方法签名可能有3个以上的冒号和冒号对齐会使代码更加易读。请不要这样做,尽管冒号对齐的方法包含代码块,因为Xcode的对齐方式令它难以辨认。

    ` 应该
     // blocks are easily readable
     [UIView animateWithDuration:1.0 animations:^{
     // something
     } completion:^(BOOL finished) {
     // something
     }];
    `
    ` 不应该
     // colon-aligning makes the block indentation hard to read
     [UIView animateWithDuration:1.0
     animations:^{
     // something
     }
     completion:^(BOOL finished) {
     // something
     }];
    `

    ## <a name=”A4.2″ id=”A4.2″>每一行的最大长度</a> 在 Xcode > Preferences > Text Editing > Page guide at column: 中将最大行长设置为80,过长的一行代码将会导致可读性问题。 参考图: ![架构](http://192.168.0.102:7000/iOS/iOS/uploads/2379c1d6f6e288127f4c338b82e9442f/2EDA2C84-6E08-4CDC-AC8C-337F73CF7F79.png) ## <a name=”A4.3″ id=”A4.3″>书写断行</a> 一个典型的 Objective-C 函数应该是这样的:

    ` - (void)writeVideoFrameWithData:(NSData*)frameData timeStamp:(int)timeStamp {
     ...
     }
    `

    在 – 和 (void) 之间应该有一个空格,第一个大括号 { 的位置在函数所在行的末尾,同样应该有一个空格。(我司的 C 语言规范要求是第一个大括号单独占一行,但考虑到 OC 较长的函数名和苹果 SDK 代码的风格,还是将大括号放在行末。) 如果一个函数有特别多的参数或者名称很长,应该将其按照 : 来对齐分行显示:

    ` -(id)initWithModel:(IPCModle)model
     connectType :(IPCConnectType)connectType
     resolution :(IPCResolution)resolution
     authName :(NSString *)authName
     password :(NSString *)password
     /Animate the icon
     animationTimer = [[NSTimer scheduledTimerWithTimeInterval:ANIMATION_DELAY
     target:self
     selector:@selector(animateIcon:)
     userInfo:nil
     repeats:YES] retain];
     if (some_long_condition &amp;&amp;
     some_other_long_condition ||
     some_completely_different_long_condition)
    `

    如果方法名比参数名短,每个参数占用一行,至少缩进4个字符,且为垂直对齐(而非使用冒号对齐)。如:

    ` - (void)short:(GTMFoo *)theFoo
     longKeyword:(NSRect)theRect
     evenLongerKeyword:(float) theInterval {
     ...
     }
    `

    ## <a name=”A4.4″ id=”A4.4″>函数调用</a> 函数调用的格式和书写差不多,可以按照函数的长短来选择写在一行或者分成多行:

    ` // 写在一行
     [myObject doFooWith:arg1 name:arg2 error:arg3];
     // 分行写,按照 ':' 对齐
     [myObject doFooWith:arg1
     name:arg2
     error:arg3];
     // 和方法的声明一样,如果无法使用冒号对齐时,每个参数一行、缩进4个字符、垂直对其(而非使用冒号对齐)。如:
     [myObj short:arg1
     longKeyword:arg2
     evenLongerKeyword:arg3
     error:arg4];
    `

    以下写法是错误的:

    ` // 错误,要么写在一行,要么全部分行
     [myObject doFooWith:arg1 name:arg2
     error:arg3];
     [myObject doFooWith:arg1
     name:arg2 error:arg3];
     // 错误,按照 ':' 来对齐,而不是关键字
     [myObject doFooWith:arg1
     name:arg2
     error:arg3];
    `

    ## <a name=”A4.5″ id=”A4.5″>@public 和 @private 标记符</a> @public 和 @private 标记符应该以一个空格来进行缩进:

    ` @interface MyClass : NSObject {
     @public
     ...
     @private
     ...
     }
     @end
    `

    ## <a name=”A4.6″ id=”A4.6″>预编译常量/宏</a> 和ANSI C类似, 预编译定义必须全部大写。

    ` #define MAX_ENTRIES 20
     #ifdef ENABLE_BINDINGS_SUPPORT
    `

    常量是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用static来声明而不是使用#define,除非显式地使用宏。整形常数用枚举,浮点型常数用const修饰 。

    ` 应该
     static NSString * const RWTAboutViewControllerCompanyName = @"RayWenderlich.com";
     static CGFloat const RWTImageThumbnailHeight = 50.0;
     不应该
     #define CompanyName @"RayWenderlich.com"
     #define thumbnailHeight 2
    `

    注意由编译器定义的宏,有前后各俩个下划线 。例如:**MACH**; ## <a name=”A4.7″ id=”A4.7″>枚举类型/宏</a> 当使用enum时,推荐使用新的固定基本类型规格,因为它有更强的类型检查和代码补全。现在SDK有一个宏NS_ENUM()来帮助和鼓励你使用固定的基本类型。 例如

    ` typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
     RWTLeftMenuTopItemMain,
     RWTLeftMenuTopItemShows,
     RWTLeftMenuTopItemSchedule
     };
    `

    你也可以显式地赋值(展示旧的k-style常量定义):

    ` typedef NS_ENUM(NSInteger, RWTGlobalConstants) {
     RWTPinSizeMin = 1,
     RWTPinSizeMax = 5,
     RWTPinCountMin = 100,
     RWTPinCountMax = 500,
     };
    `

    旧的k-style常量定义应该避免除非编写Core Foundation C的代码。 不应该:

    ` enum GlobalConstants {
     kMaxPinSize = 5,
     kMaxPinCount = 500,
     };
    `

    ## Case语句 大括号在case语句中并不是必须的,除非编译器强制要求。当一个case语句包含多行代码时,大括号应该加上。

    ` switch (condition) {
     case 1:
     // ...
     break;
     case 2: {
     // ...
     // Multi-line example using braces
     break;
     }
     case 3:
     // ...
     break;
     default:
     // ...
     break;
     }
    `

    有很多次,当相同代码被多个cases使用时,一个fall-through应该被使用。一个fall-through就是在case最后移除’break’语句,这样就能够允许执行流程跳转到下一个case值。为了代码更加清晰,一个fall-through需要注释一下。

    ` switch (condition) {
     case 1:
     // ** fall-through! **
     case 2:
     // code executed for values 1 and 2
     break;
     default:
     // ...
     break;
     }
     当在switch使用枚举类型时,'default'是不需要的。
     例如:
     RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;
     switch (menuType) {
     case RWTLeftMenuTopItemMain:
     // ...
     break;
     case RWTLeftMenuTopItemShows:
     // ...
     break;
     case RWTLeftMenuTopItemSchedule:
     // ...
     break;
     }
    `

    ## <a name=”A4.8″ id=”A4.8″>布尔值</a> Objective-C使用YES和NO。因为true和false应该只在CoreFoundation,C或C++代码使用。既然nil解析成NO,所以没有必要在条件语句比较。不要拿某样东西直接与YES比较,因为YES被定义为1和一个BOOL能被设置为8位。 这是为了在不同文件保持一致性和在视觉上更加简洁而考虑。 应该

    ` if (someObject) {}
     if (![anotherObject boolValue]) {}
    `

    不应该

    ` if (someObject == nil) {}
     if ([anotherObject boolValue] == NO) {}
     if (isAwesome == YES) {} // Never do this.
     if (isAwesome == true) {} // Never do this.
    `

    如果BOOL属性的名字是一个形容词,属性就能忽略”is”前缀,但要指定get访问器的惯用名称。例如:

    ` @property (assign, getter=isEditable) BOOL editable;
    `

    ## <a name=”A4.9″ id=”A4.9″>条件语句</a> 条件语句主体为了防止出错应该使用大括号包围,即使条件语句主体能够不用大括号编写(如,只用一行代码)。这些错误包括添加第二行代码和期望它成为if语句;还有,even more dangerous defect可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。除此之外,这种风格与其他条件语句的风格保持一致,所以更加容易阅读。 应该

    ` if (!error) {
     return success;
     }
    `

    不应该

    ` if (!error)
     return success;
    `

    ` if (!error) return success;
    `

    ## <a name=”A5.0″ id=”A5.0″>三元操作符(条件判断语句)</a> 当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。 Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。 应该

    ` NSInteger value = 5;
     result = (value != 0) ? x : y;
     BOOL isHorizontal = YES;
     result = isHorizontal ? x : y;
    `

    不应该

    ` result = a &gt; b ? x = c &gt; d ? c : d : y;
    `

    ## <a name=”A5.1″ id=”A5.1″>Init方法</a> 在初始化方法中,不要将变量初始化为“0”或“nil”,那是多余的 内存中所有的新创建的对象(isa除外)都是0,所以不需要重复初始化为“0”或“nil” Init方法应该遵循Apple生成代码模板的命名规则,返回类型应该使用instancetype而不是id。

    ` - (instancetype)init {
     self = [super init];
     if (self) {
     // ...
     }
     return self;
     }
    `

    ## <a name=”A5.2″ id=”A5.2″>类构造方法</a> 当类构造方法被使用时,它应该返回类型是instancetype而不是id。这样确保编译器正确地推断结果类型。[参考资料][3] 代码更加一致,可读性更强。它们返回相同的东西,这一点一目了然。 应该

    ` 1 :
     @interface Airplane
     + (instancetype)airplaneWithType:(RWTAirplaneType)type;
     @end
     2:
     - (instancetype)initWithBar:(NSInteger)bar;
     3:
     + (instancetype)fooWithBar:(NSInteger)bar;
    `

    不应该

    ` - (id)initWithBar:(NSInteger)bar;
     + (id)fooWithBar:(NSInteger)bar;
    `

    ## <a name=”A5.3″ id=”A5.3″>CGRect函数</a> 当访问CGRect里的x, y, width, 或 height时,应该使用CGGeometry函数而不是直接通过结构体来访问。引用Apple的CGGeometry 官方介绍 在这个参考文档中所有的函数,接受CGRect结构体作为输入,在计算它们结果时隐式地标准化这些rectangles。因此,你的应用程序应该避免直接访问和修改保存在CGRect数据结构中的数据。相反,使用这些函数来操纵rectangles和获取它们的特性。 应该

    ` CGRect frame = self.view.frame;
     CGFloat x = CGRectGetMinX(frame);
     CGFloat y = CGRectGetMinY(frame);
     CGFloat width = CGRectGetWidth(frame);
     CGFloat height = CGRectGetHeight(frame);
     CGRect frame = CGRectMake(0.0, 0.0, width, height);
    `

    不应该

    ` CGRect frame = self.view.frame;
     CGFloat x = frame.origin.x;
     CGFloat y = frame.origin.y;
     CGFloat width = frame.size.width;
     CGFloat height = frame.size.height;
     CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };
    `

    ## <a name=”A5.4″ id=”A5.4″>错误处理</a> 当方法通过引用来返回一个错误参数,判断返回值而不是错误变量。 应该:

    ` NSError *error;
     if (![self trySomethingWithError:&amp;error]) {
     // Handle Error
     }
    `

    不应该

    ` NSError *error;
     [self trySomethingWithError:&amp;error];
     if (error) {
     // Handle Error
     }
    `

    在成功的情况下,有些Apple的APIs记录垃圾值(garbage values)到错误参数(如果non-NULL),那么判断错误值会导致false负值和crash。 这会防止重复引用造成crashes,或者调用alloc与init造成单例并不唯一 ## <a name=”A5.5″ id=”A5.5″>下划线</a> 当使用属性时,实例变量应该使用self.来访问和改变。这就意味着所有属性将会视觉效果不同,因为它们前面都有self.。 但有一个特例:在初始化方法里,实例变量(例如,_variableName)应该直接被使用来避免getters/setters潜在的副作用。 局部变量不应该包含下划线。 ## <a name=”A5.6″ id=”A5.6″>协议( Protocols )</a> 在书写协议的时候注意用 <> 括起来的协议和类型名之间是没有空格的,比如IPCConnectHandler(), 这个规则适用所有书写协议的地方,包括函数声明、类声明、实例变量等等:

    ` @interface MyProtocoledClass : NSObject {
     @private
     id&lt;MyFancyDelegate&gt; _delegate;
     }
     @property (nonatomic,assign)id&lt;MyFancyDelegate&gt;delegate;
     //或者写成这样
     - (void)setDelegate:(id)aDelegate;
     @end
    `

    如果类声明中包含多个protocal,每个protocal占用一行,缩进2个字符。如:

    ` @interface CustomViewController : ViewController &lt;
     AbcDelegate,
     DefDelegate
     &gt; {
     ...
     }
    `

    ## <a name=”A5.7″ id=”A5.7″>闭包( Blocks )</a> 根据 block 的长度,有不同的书写规则: 较短的 block 可以写在一行内。如果分行显示的话, block 的右括号 }应该和调用block那行代码的第一个非空字符对齐。 block 内的代码采用 4 个空格 的缩进。 如果 block 过于庞大,应该单独声明成一个变量来使用。 ^ 和 ( 之间, ^ 和 { 之间都没有空格,参数列表的右括号 ) 和 { 之间有一个空格。

    ` operation setCompletionBlock:^{ [self onOperationDone]; }];
     // 分行书写的 block ,内部使用 4 空格缩进
     [operation setCompletionBlock:^{
     [self.delegate newDataAvailable];
     }];
    // 使用 C 语言 API 调用的 block 遵循同样的书写规则
     dispatch_async(_fileIOQueue, ^{
     NSString* path = [self sessionFilePath];
     if (path) {
     // ...
     }
     });
    // 较长的 block 关键字可以缩进后在新行书写,注意 block 的右括号 '}' 和调用 block 那行代码的第一个非空字符对齐
     [[SessionService sharedService]
     loadWindowWithCompletionBlock:^(SessionWindow *window) {
     if (window) {
     [self windowDidLoad:window];
     } else {
     [self errorLoadingWindow];
     }
     }];
    // 较长的 block 参数列表同样可以缩进后在新行书写
     [[SessionService sharedService]
     loadWindowWithCompletionBlock:
     ^(SessionWindow *window) {
     if (window) {
     [self windowDidLoad:window];
     } else {
     [self errorLoadingWindow];
     }
     }];
    // 庞大的 block 应该单独定义成变量使用
     void (^largeBlock)(void) = ^{
     // ...
     };
     [_operationQueue addOperationWithBlock:largeBlock];
     // 在一个调用中使用多个 block ,注意到他们不是像函数那样通过 ':' 对齐的,而是同时进行了 4 个空格的缩进
     [myObject doSomethingWith:arg1
     firstBlock:^(Foo *a) {
     // ...
     }
     secondBlock:^(Bar *b) {
     // ...
     }];
    `

    ## 数据结构的语法 应该使用可读性更好的语法来构造 NSArray , NSDictionary 等数据结构,避免使用冗长的 alloc,init 方法。 如果构造代码写在一行,需要在括号两端留有一个空格,使得被构造的元素于与构造语法区分开来:

    `
    `

    // 正确,在语法糖的 “[]” 或者 “{}” 两端留有空格 NSArray _array = @[ [foo description], @”Another String”, [bar description] ]; NSDictionary _dict = @{ NSForegroundColorAttributeName : [NSColor redColor] }; // 不正确,不留有空格降低了可读性 NSArray_ array = @[[foo description], [bar description]]; NSDictionary_ dict = @{NSForegroundColorAttributeName: [NSColor redColor]};

    ` 如果构造代码不写在一行内,构造元素需要使用两个空格来进行缩进,右括号 ] 或者 } 写在新的一行,并且与调用语法糖那行代码的第一个非空字符对齐:
    `

    NSArray _array = @[ @”This”, @”is”, @”an”, @”array” ]; NSDictionary _dictionary = @{ NSFontAttributeName : [NSFont fontWithName:@”Helvetica-Bold” size:12], NSForegroundColorAttributeName : fontColor };

    `
    构造字典时,字典的 Key 和 Value 与中间的冒号 : 都要留有一个空格,多行书写时,也可以将 Value 对齐:
    `

    // 正确,冒号 ‘:’ 前后留有一个空格 NSDictionary _option1 = @{ NSFontAttributeName : [NSFont fontWithName:@”Helvetica-Bold” size:12], NSForegroundColorAttributeName : fontColor }; // 正确,按照 Value 来对齐 NSDictionary _option2 = @{ NSFontAttributeName : [NSFont fontWithName:@”Arial” size:12], NSForegroundColorAttributeName : fontColor }; // 错误,冒号前应该有一个空格 NSDictionary _wrong = @{ AKey: @”b”, BLongerKey: @”c”, }; // 错误,每一个元素要么单独成为一行,要么全部写在一行内 NSDictionary _alsoWrong= @{ AKey : @”a”, BLongerKey : @”b” }; // 错误,在冒号前只能有一个空格,冒号后才可以考虑按照 Value 对齐 NSDictionary *stillWrong = @{ AKey : @”b”, BLongerKey : @”c”, };

    `
    &lt;a name="A5.8" id="A5.8"&gt;代码组织&lt;/a&gt;
    ` ---
     在函数分组和protocol/delegate实现中使用#pragma mark -来分类方法,要遵循以下一般结构:
    `</pre>
    # pragma mark - Lifecycle
    `
  • (instancetype)init {}
  • (void)dealloc {}
  • (void)viewDidLoad {}
  • (void)viewWillAppear:(BOOL)animated {}
  • (void)didReceiveMemoryWarning {}

    pragma mark – Custom Accessors

  • (void)setCustomProperty:(id)value {}
  • (id)customProperty {}

    pragma mark – IBActions

  • (IBAction)submitData:(id)sender {}

    pragma mark – Public

  • (void)publicMethod {}

    pragma mark – Private

  • (void)privateMethod {}

    pragma mark – Protocol conformance

    pragma mark – UITextFieldDelegate

    pragma mark – UITableViewDataSource

    pragma mark – UITableViewDelegate

    pragma mark – NSCopying

  • (id)copyWithZone:(NSZone *)zone {}

    pragma mark – NSObject

  • (NSString )description {} `` &amp;lt;a name="A5.9" id="A5.9"&amp;gt;单例模式&amp;lt;/a&amp;gt;&lt;pre&gt; 单例对象应该使用线程安全模式来创建共享实例。 标准单例示例 <pre>` static AppContext * dataEngineInstance=nil;
    *   (AppContext*)sharedInstance{ static dispatch_once_t onceToken; dispatch_once(&amp;amp;amp;onceToken, ^{ dataEngineInstance=[[[self class] alloc] init]; }); return dataEngineInstance; }
    
    • (instancetype)allocWithZone:(NSZone )zone{ if(dataEngineInstance==nil){ dataEngineInstance=[super allocWithZone:zone]; } return dataEngineInstance; } -(instancetype)copyWithZone:(NSZone )zone{ return dataEngineInstance; }
    • (instancetype)init{ @synchronized(dataEngineInstance) { return dataEngineInstance; } return dataEngineInstance; } `</pre>

      &lt;a name=”6″ id=”6″&gt;注释规范&lt;/a&gt;

      每一个文件都必须写文件注释,文件注释通常包含 `

  • 文件所在模块
  • 作者信息
  • 历史版本信息
  • 版权信息
  • 文件包含的内容,作用 Xcode注释插件 [VVDocumenter-Xcode][4] 一段良好文件注释的例子:
    /*******************************************************************************
     Copyright (C), 2011-2013, Andrew Min Chang
     File name: AMCCommonLib.h
     Author: Andrew Chang (Zhang Min)
     E-mail: LaplaceZhang@126.com
     Description:
     This file provide some covenient tool in calling library tools. One can easily include
     library headers he wants by declaring the corresponding macros.
     I hope this file is not only a header, but also a useful Linux library note.
     History:
     2012-??-??: On about come date around middle of Year 2012, file created as "commonLib.h"
     2012-08-20: Add shared memory library; add message queue.
     2012-08-21: Add socket library (local)
     2012-08-22: Add math library
     2012-08-23: Add socket library (internet)
     2012-08-24: Add daemon function
     2012-10-10: Change file name as "AMCCommonLib.h"
     2012-12-04: Add UDP support in AMC socket library
     2013-01-07: Add basic data type such as "sint8_t"
     2013-01-18: Add CFG_LIB_STR_NUM.
     2013-01-22: Add CFG_LIB_TIMER.
     2013-01-22: Remove CFG_LIB_DATA_TYPE because there is already AMCDataTypes.h
     Copyright information:
     This file was intended to be under GPL protocol. However, I may use this library
     in my work as I am an employee. And my company may require me to keep it secret.
     Therefore, this file is neither open source nor under GPL control.
     ********************************************************************************/

    文件注释的格式通常不作要求,能清晰易读就可以了,但在整个工程中风格要统一。

    <a name=”6″.1 id=”6.1″>头文件注释</a>

    header : 头文件基本信息。这个用在每个源代码文件的头文件的最开头。 例如:

    `/*!
     @header 这里的信息应该与该源代码文件的名字一致
     @abstract 关于这个源代码文件的一些基本描述
     @author Kevin Wu (作者信息)
     @version 1.00 2012/01/20 Creation (此文档的版本信息)
     */`

    ## <a name=”6.2″ id=”6.2″>类注释</a> class: 类信息。此注释用在类声明的开头。 例如:

    `/*!
     @class
     @abstract 这里可以写关于这个类的一些描述。
     */
     @interface MyClass : NSObject {
     }`

    ## <a name=”6.3″ id=”6.3″>属性注释</a> property: property的相关注释。

    `/*!
     @property
     @abstract 这里可以写关于这个Property的一些基本描述。
     */
     @property (nonatomic,readonly) NSString *helloDocText_;`

    或者

    `/** 这里可以写关于这个Property的一些基本描述。 */
     @property (nonatomic,readonly) NSString *helloDocText_;`

    建议使用第二种方式,这样在类中引用是可以看到当前属性注释信息 ## <a name=”6.4″ id=”6.4″>函数(方法)注释</a> method: 函数(方法)的相关注释。 例如:

    ` /_!
     @method
     @abstract 这里可以写一些关于这个方法的一些简要描述
     @discussion 这里可以具体写写这个方法如何使用,注意点之类的。如果你是设计一个抽象类或者一个
     共通类给给其他类继承的话,建议在这里具体描述一下怎样使用这个方法。
     @param text 文字 (这里把这个方法需要的参数列出来)
     @param error 错误参照
     @result 返回结果
     _/
    *   (BOOL)showText:(NSString _)text error:(NSError *_)error; `
    

    <a name=”6.5″ id=”6.5″>枚举注释</a>

    enum: enum的相关注释。 例如:

    `/!
    @enum
    @abstract 关于这个enum的一些基本信息
    @constant 枚举变量描述信息
    @constant 枚举变量描述信息
    ...
    /
    typedef NS_ENUM(NSInteger, HelloDocEnumDocDemo){
    HelloDocEnumDocDemoTagNumberPopupView = 100,
    HelloDocEnumDocDemoTagNumberOKButton,
    ...
    };

<a name=”6.6″ id=”6.6″>协议注释</a>

category: category的相关注释。 例如:

` /_!
@category
@abstract NSString的Category
_/
@interface KevinNSString (NSString)

<a name=”6.7″ id=”6.7″>委托注释</a>

protocol: protocol的相关注释 例如:

` /_!
 @protocol
 @abstract 这个HelloDoc类的一个protocol
 @discussion 具体描述信息可以写在这里
 _/
 @protocol HelloDocDelegate <NSObject>

<a name=”7″ id=”7″>项目管理工具(git,SVN)使用</a>

git 工具

mac 版本: GitHub Desktop

SourceThree 及时更是,多同步管理平台上的代码 在ios 项目多人联合开发容易出现代码合并冲突问题,在项目开发上如果添加了新文件或者修改工程设置都会改动 .xcodeproj文件,当修改了工程文件时应及时提交 如遇到了代码冲突应该先查看冲突原因再合并。

<a name=”8″ id=”8″>iOS更多编码规范参考资料参考资料:</a>

googol object-c编码规范

  1. [Google Objective-C Style Guide][5] 官网 object-c编码规范
  2. [Coding Guidelines for Cocoa][6] [1]: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingBasics.html#//apple_ref/doc/uid/20001281-BBCHBFAH [2]: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.pdf [3]: http://nshipster.com/instancetype/ [4]: https://github.com/onevcat/VVDocumenter-Xcode [5]: https://google-styleguide.googlecode.com/svn/trunk/objcguide.xml?showone=Line_Length#Line_Length [6]: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.pdf![2EDA2C84-6E08-4CDC-AC8C-337F73CF7F79]