为什么要有编码规范这么一说呢?你设想一下如果张三写代码一个风格,李四一个风格,当张三去看李四的时候他会极度的不舒服,反过来李四去看张三的代码的时候也会感觉非常的糟糕。所以他们两个人遵循同一个规则的时候,再去看对方的代码的时候就不会不适应,因此指定一个大家都遵循的代码编程风格就至关重要了。对于iOS开发,苹果官方就制定了一套编码规范。作为iOS开发者遵循苹果制定的编码规范写代码,那么在看苹果的官方代码的时候就觉得很轻松了。
命名
这点非常重要!!!没有重复三遍,但是依然是最重要的!!!如果你不是一开始就跟着项目走而是在中途或者是需要你维护一个别人留下的项目的时候。如果项目是经过几手,而且没有统一的命名风格,而且还没有注释。这时候你面对项目,你的内心是崩溃的。如果可以把写这些代码的揪出来,我保证你会把他打的不像人样!好的命名胜过N多的注释!
命名的一般性原则
清晰性
- 要清晰且简短,但是要注意不要因为简短放弃简短,清晰是首位。
- 名称通常不缩写,即使名称很长也要拼写完全。(你有可能比较讨厌苹果那长长的方法名,但是你看到方法就知道是干嘛的,连文档都省了,因此OC也有自文档语言的美誉)。
- 避免有歧义的命名。歧义是一个造成误会的罪魁祸首。
一致性
代码命名的一致性能带给阅读代码者非常的阅读体验,能让人一眼就看出这大概是什么。
- 尽可能使用与Cocoa编程接口命名保持一致的名称。如果你不太确定某个命名的一致性,请浏览一下头文件或参考文档中的范例。
- 在使用多态方法的类中,命名的一致性非常重要。在不同类中实现相同功能的方法应该具有相同的名称。
不要自我指涉
自我指涉的英文是:self-reference,如果你不明白什么是自我指涉,给你讲个故事吧。从前有座山,山上有座庙,庙里有个和尚,和尚说,从前有座山……
- 首先名称不要自我指涉,看下面的代码。
NSString //okey
NSStringObject //自我指涉
- 掩码(可使用位操作进行组合)和用作通知名称的常量不受该约定限制。
前缀
前缀不仅可以防止和苹果的命名发生冲突,而且也可以区分软件的功能范围,比如UIKit里面的都以UI开头,看到UI开头的类我们就能知道是UI相关的。
- 前缀有规定的格式。它由两到三个大写字符组成,不能使用下划线与子前缀。
- 命名class,protocol,structure,函数,常量时使用前缀;命名成员方法时不使用前缀,因为方法已经在它所在类的命名空间种;同理,命名结构体字段时也不使用前缀。
书写约定
书写约定能规范命名,不然你会看到其他语言的影子,比如以前写C++的你就会发现他会带一股浓浓的C++味道。OC也应该有OC的味道。来看看OC对于书写有哪些约定吧!
- 对于包含多个单词的名称,不要使用标点符号作为名称的一部分或作为分隔符(下划线,破折号等)。大写每个单词的首字符并将这些单词连续拼写在一起。并且要注意一下限制:
- 方法名小写第一个单词的首字符,大写后续所有单词的首字符。方法名不使用前缀。
- 函数名和常量名使用与其关联类相同的前缀,并且要大写前缀后面所有单词的首字符。
- 避免使用下划线来表示名称的私有属性。苹果公司保留该方式的使用。如果第三方这样使用可能 会导致命名冲突,他们可能会在无意中用自己的方法覆盖掉已有的私有方法,这会导致严重的后 果。(苹果是不建议用私有变量的,这一点在用runtime解决一些问题的时候常常用到,因此得注意。在命名的时候不要把其他语言的习惯带到OC中)�。
类与协议命名要注意的事儿
- 类名应包含一个明确述该类(或类的对象)是什么或做什么的名词。类名要有合适的前缀。
- 协议应该根据对方法的行为分组方式来命名。
关于头文件
- 声明孤立的类或协议:将孤立的类或协议声明放置在单独的头文件中,该头文件名称与类或协议同名。
- 声明相关联的类或协议:将相关联的声明(类,类别及协议) 放置在一个头文件中,该头文件名称与 主要的类/类别/协议的名字相同。
- 包含框架头文件:每个框架应该包含一个与框架同名的头文件,该头文件包含该框架所有公开的头文 件。(写一个库的时候一般得这么干,你去看看苹果的库或者出名的苦,里面有一个和库同名的头文件,里面全是其他头文件的引用)。
- 为已有框架中的某个类扩展 API:如果要在一个框架中声明属于另一个框架某个类的范畴类的方法, 该头文件的命名形式为:原类名+“Additions”。
- 相关联的函数与数据类型:将相联的函数,常量,结构体以及其他数据类型放置到一个头文件中,并
以合适的名字命名。
关于方法命名
除了上述要注意的,还有一些小的问题也需要注意。
- 表示对象行为的方法,名称以动词开头。
- 名称中不要出现 do 或 does,因为这些助动词没什么实际意义。也不要在动词前使用副词或形容词修饰。
- 如果方法返回方法接收者的某个属性,直接用属性名称命名。不要使用 get,除非是间接返回一个或 多个值。
- 参数要用述该参数的关键字命名。
- 参数前面的单词要能述该参数。
- 不要使用and来连接用属性作参数关键字。一般不用and,除非满足下面这条。
- 如果方法述两种独立的行为,使用and来串接它们。
关于访问方法
访问方法就是我们常说的setter和getter方法。那么在写(或重写)访问方法的时候要注意些什么呢?
- 如果属性是用名词述的,则命名格式为:
- (void) setNoun:(type)aNoun;
- (type) noun;
- 如果属性是用形容词述的,则命名格式为:
- (void) setAdjective:(BOOL)flag;
- (BOOL) isAdjective;
- 如果属性是用动词述的,则命名格式为:(动词要用现在时时态)
- (void) setVerbObject:(BOOL)flag;
- (BOOL) verbObject;
- 不要使用动词的过去分词形式作形容词使用.
- 可以使用情态动词(can,should,will等)来高清晰性,但不要使用do或does.
- 只有在方法需要间接返回多个值的情况下,才使用get.
关于委托方法
在开发中会经常用到代理设计模式,但是代理方法也是有一定的命名规则的。委托方法的这些规则也使用于
- 名称以标示发送消息的对象的类名开头,省略类名的前缀并小写类第一个字符。经常用的tabview的代理方法或者数据源方法都是tabview开头的。
- 冒号紧跟在类名之后(随后的那个参数表示委派的对象)。该规则不适用于只有一个 sender 参数的 方法。
- 面的那条规则也不适用于响应通知的方法。在这种情况下,方法的唯一参数表示通知对象。
- (void) windowDidChangeScreen:(NSNotification *)notification;
- 用于通知委托对象操作即将发生或已经发生的方法名中要使用did或will.
- 用于询问委托对象可否执行某操作的方法名中可使用did或will,但最好使用should.
关于集合方法
集合方法命名有如下一些限制和约定
- 如果集合中的元素无序,返回 NSSet,而不是 NSArray。
- 如果将元素插入指定位置的功能很重要,则需具备如下方法:
- (void) insertElement:(elementType)anObj atIndex:(int)index;
- (void) removeElementAtIndex:(int)index;
集合方法的实现要考虑如下细节:
- 集合类方法通常负责管理元素的所有者关系,在 add 或 insert 的实现代码里会 retain 元素,在 remove 的实现代码中会 release 元素.
- 当被插入的对象需要持有指向集合对象的指针时,通常使用 set... 来命名其设置该指针的方法,且不 要 retain 集合对象。
关于方法的参数
- 第一条是最基本的,参数小写第一个单词首字母,大写后面单词的首字母。
- 不要在参数名中使用 pointer 或 ptr,让参数的类型来说明它是指针。
- 避免使用 one, two,...,作为参数名。这样的参数都不好意思用,鬼知道这代表什么。
- 尽量不要缩写,多写几个字母又不会死人。
关于私有方法
- 不要使用下划线表示自己私有方法名称的前缀,Apple保留了这种做法。
- 若要继承 Cocoa framework 中一个超大的类(如:NSView),并且想要使你的私有方法名称与基类 中的区别开来,你可以为你的私有方法名称添加你自己的前缀。这个前缀应该具有唯一性,如基于你 公司的名称,或工程的名称,并以“XX_”形式给出。比如你的工程名为"Byte Flogger",那么就可以是 “BF_addObject:”。这一点在我的博文分析别人的开源库的时候就发现就有人这么做。
关于函数的命名
在OC中是可以使用C函数的,所以这里的函数指的是C函数。
- 函数命名与方法命名相似,但有两点不同:
- 它们有前缀,其前缀与你使用的类和常量的前缀相同
- 大写前缀后紧跟的第一个单词首字符
- 大多数函数名称以动词开头,这个动词述该函数的行为。
实例变量与数据类型的命名
实例变量
在为类添加实例变量的时候需要注意以下几点:
- 避免创建public变量
- 使用@private,@protected显式限定实例变量的访问权限
- 确保实例变量名简明扼要地述了它所代表的属性
- 如果实例变量被设计为可被访问的,确保编写了访问方法
枚举常量
- 使用枚举来定义一组相关的整数常量
- 枚举常量与其typedef遵守函数命名规则
- 位掩码常量可以使用不具名枚举
其他常量
- 通常不使用 #define来创建常量。如上面所述,整数常量请使用枚举,浮点数常量请使用const
- 使用大写字母来定义预处理编译宏。如:#ifdef DEBUG
- 编译器定义的宏名首尾都有双下划线。如:MACH
- 为 notification 名及 dictionary key 定义字符串常量,从而能够利用编译器的拼写检查,减少书写错误
通知
- 如果一个类有委托,那它的大部分通知可能由其委托的委托方法来处理。这些通知的名称应该能够反应其 响应的委托方法。比如,当应用程序交 NSApplicationDidBecomeActiveNotification 通知时,全局 NSApplication 对象的委托会注册从而能够接收 applicaitonDidBecomeActive: 消息。
通知由具有如下形式的全局 NSString 对象标识:
[相关联类的名称] + [Did 或 Will] + [UniquePartOfName] + Notification
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification
开发中的一些小贴士
在initialize
类方法中,能够编写实现一些延迟执行且只被一次的代码,initialize
类方法是由运行时系统在该类响应任何其他消息之前调用的。典型的应用是在其中设置类的版本信息。运行时系统向每个类发送initialize
消息,即使该类没有实现initialize
,也会调用其基类的某个initialize
方法。因此一个类的initialize
方法可能会因为存在继承类的缘故被执行多次。因此有必要使用一定的技巧来防止只执行一次的代码被多次执行。如:NSFoo
类的initialize
方法实现可能如下:
+ (id) initialize{
if (self == [NSFoo class]){
//初始化代码
}
return self;
}
要注意不应当显式调用initialize方法。如果你需要激活initialize方法,使用[NSFoo self]形式的调用。
小结
习惯是平时一点点养成的,只要坚持就会有好的习惯。