分享

iOS代码规范

 没原创_去搜索 2015-09-22
http://blog.csdn.net/zhaoxy_thu/article/details/9090963



命名规范

类命名

l   首字母大写,之后每个单词首字母都大写

l   使用能够反映类功能的名词短语

l   文件和类同名

l   举例:BaseClient、ImageStore

特殊类命名

l   如果是视图控制器的子类应添加后缀“ViewController”或者“Controller”

l   如果是视图的子类应添加后缀“View”

l   如果是按钮的子类应添加后缀“Button”

……

l   举例:SettingsViewController、NavigationView

分类(类别)命名

l   与类命名相同,此外需添加要扩展的类名和“+”

l   举例:NSString+URLEncoding

协议(委托)命名

l   与类命名相同,此外需添加“Delegate”后缀

l   举例:ReplyViewDelegate

方法命名

l   首字母小写,之后每个单词首字母都大写

l   方法名使用动词短语

l   举例:- (void)setPostValue:(int)value

方法参数命名

l   首字母小写,之后每个单词首字母都大写

l   具有足够的说明性

l   不需要添加类型前缀

l   举例:- (void)sendUserInfo:(NSDictionary *)userInfo

变量命名

l   首字母小写,之后每个单词首字母都大写

l   具有足够的说明性

l   成员变量不需要添加“_m”前缀

l   成员变量添加“_”前缀

常量

l   常量(预定义,局部常量等)使用小写k开头的驼峰法

l   举例:kInvalidHandle , kWritePerm

l   枚举类型命名首字母大写,之后每个单词首字母都大写,最后加“s

l   枚举变量使用枚举类型去掉“s”作为前缀,每个单词首字母大写,中间不允许加下划线

l   举例:

typedef enum UIControlEvents{

UIControlEventTouchDown,

UIControlEventTouchUpInside

}UIControlEvents;

图片命名

l   使用英文,首字母大写,之后每个单词首字母都大写

l   添加模块名作为前缀,避免冲突

l   图片应该与类文件一样,按模块分组放置

分组命名

l   使用英文,首字母大写,之后每个单词首字母都大写

l   每个分组使用模块的名字

l   使用的开源库统一放在“Library”分组下

l   使用的公共组件统一放在“Common”分组下

l   视图控制器及AppDelegate统一放在“Controllers”分组下

书写规范

注释

l   文件都包含文件头,要说明文件名、作者、创建时间、变更记录

l   多人协作完成项目时,public接口的每个方法都应该添加关于函数,参数,返回值以及副作用的注释

l   if语句的判断条件复杂时,需要用注释说明判断内容

l   接口类(继承于BaseClient)的头文件每个方法前都应该注明方法的作用

方法

l   留一个空格在-或+和返回类型之间,但参数列表里的参数之间不要留间隔,每个冒号之前都必须有对该参数的说明词,如:

p - (void)doSomethingWithString:(NSString *)string number:(int)num

l   如果参数过多,推荐每个参数各占一行。使用多行的情况下,在参数前加冒号用于对齐,如:

p  -(void)doSomethingWith:(GTMFoo *)theFoo

                                          rect:(NSRect)theRect

                             interval:(float)theInterval

其他规范

l   操作符前后都要加空格

l   避免相同的代码段在多个地方出现

l   语句嵌套层次不得超过3层

l   每个实现文件建议在500行以内,不能超过1000,超过之后应考虑通过抽象类对代码进行重构

l   及时删除或注释掉无用的代码

l   UITableViewCell里面的network client都要委托出来

l   点击按钮之后需要切换按钮图片,当这两张图片没有关联时(例如一张图片相比另一张图片有选中效果),不应该设置为UIControlSelected

l   控件布局使用相对坐标

l   确定不使用的代码应该删除



===========================


正文: 
· 格式化代码  
? 指针“*”号的位置  
? 如:NSString *varName;  
? 空格 VS tabs  
? 只允许使用空格,将编辑器设置为1个TAB = 2个字符缩进  
? 每行的长度  
? 每行最多不得超过100个字符  
? 以15寸Macbook Pro的大小,每行100个字符时能最大化地同时容下编辑器和iPhone模拟器  
? Google的80字符的标准有点少,这导致过于频繁的换行(Objectve-C的代码一般都很长)  
? 通过 “Xcode => Preferences => TextEditing => 勾选Show Page Guide / 输入  
100 => OK” 来设置提醒  
? 方法的声明和定义  
? 在 - OR + 和返回值之间留1个空格,方法名和第一个参数间不留空格。如:  
- (void)doSomethingWithString:(NSString *)theString {  
...  
}  
? 当参数过长时,每个参数占用一行,以冒号对齐。如:  
- (void)doSomethingWith:(GTMFoo *)theFoo  
rect:(NSRect)theRect  
interval:(float)theInterval {  
...  
}  
? 如果方法名比参数名短,每个参数占用一行,至少缩进4个字符,且为垂直对齐(而非使用冒号  
对齐)。如:  
- (void)short:(GTMFoo *)theFoo  
longKeyword:(NSRect)theRect  
evenLongerKeyword:(float)theInterval {  
...  
}  
? 方法的调用  
? 调用方法沿用声明方法的习惯。例外:如果给定源文件已经遵从某种习惯,继续遵从那种习惯。  
? 所有参数应在同一行中,或者每个参数占用一行且使用冒号对齐。如:  
[myObject doFooWith:arg1 name:arg2 error:arg3];  
或  
[myObject doFooWith:arg1  
name:arg2  
error:arg3];  
? 和方法的声明一样,如果无法使用冒号对齐时,每个参数一行、缩进4个字符、垂直对其(而非  
使用冒号对齐)。如:  
[myObj short:arg1  
longKeyword:arg2  
evenLongerKeyword:arg3];  
? @public 和 @private  
? @public 和 @private使用单独一行,且缩进1个字符  
? Protocals  
? 类型标示符、代理名称、尖括号间不留空格。  
? 该规则同样适用于:类声明、实例变量和方法声明。如:  
@interface MyProtocoledClass : NSObject<NSWindowDelegate> {  
@private  
id<MyFancyDelegate> _delegate;  
}  
- (void)setDelegate:(id<MyFancyDelegate>)aDelegate;  
@end  
? 如果类声明中包含多个protocal,每个protocal占用一行,缩进2个字符。如:  
@interface CustomViewController : ViewController<  
AbcDelegate,  
DefDelegate  
> {  
...  
}  
· 命名  
? 类名  
? 类名(及其category name 和 protocal name)的首字母大写,写使用首字母大写的形式  
分割单词  
? 在面向特定应用的代码中,类名应尽量避免使用前缀,每个类都使用相同的前缀影响可读性。  
? 在面向多应用的代码中,推荐使用前缀。如:GTMSendMessage  
? Category Name  
? 待完善  
? 方法名  
? 方法名的首字母小写,且使用首字母大写的形式分割单词。方法的参数使用相同的规则。  
? 方法名+参数应尽量读起来像一句话(如:)。在这里查看苹果对方法命名的规范。  
? getter的方法名和变量名应相同。不允许使用“get”前缀。如:  
- (id) getDelegate; // 禁止  
- (id)delegate; // 对头  
? 本规则仅针对Objective-C代码,C++代码使用C++的习惯  
? 变量名  
? 变量名应使用容易意会的应用全称,且首字母小写,且使用首字母大写的形式分割单词  
? 成员变量使用“_”作为前缀(如:“NSString *_varName;”。虽然这与苹果的标准(使  
用“_”作为后缀)相冲突,但基于以下原因,仍使用“_”作为前缀。  
? 使用“_”作为前缀,更容易在有代码自动补全功能的IDE中区分“属性  
(self.userInfo)”和“成员变量(_userInfo)”  
? 常量(#define, enums, const等)使用小写“k”作为前缀,首字母大写来分割单词。如:  
kInvalidHandle  
· 注释  
? 待完善  
· Cocoa 和 Objective-C特有的规则  
? 成员变量使用 @private。如:  
@interface MyClass : NSObject {  
@private  
id _myInstanceVariable;  
}  
// public accessors, setter takes ownership  
- (id)myInstanceVariable;  
- (void)setMyInstanceVariable:(id)theVar;  
@end  
? Indentify Designated Initializer  
? 待完善  
? Override Desingated Initializer  
? 待完善  
? 初始化  
? 在初始化方法中,不要将变量初始化为“0”或“nil”,那是多余的  
? 内存中所有的新创建的对象(isa除外)都是0,所以不需要重复初始化为“0”或“nil”  
? 避免显式的调用 +new 方法  
? 禁止直接调用 NSObject 的类方法 +new,也不要在子类中重载它。使用alloc和init方法  
? 保持公共API的简洁性  
? 待完善  
? #import VS #include  
? 使用 #import 引入Ojbective-C和Ojbective-C++头文件,使用 #include 引入C和C++头  
文件  
? import根框架(root frameworks),而非各单个文件  
? 虽然有时我们仅需要框架(如Cocoa 或 Foundation)的某几个头文件,但引入根文件编译  
器会运行的更快。因为根框架(root frameworks)一般会预编译,所以加载会更快。再次强  
调:使用 #import 而非 #include 来引入Objective-C框架。如:  
#import <Foundation/NSArray.h> // 禁止  
#import <Foundation/NSString.h>  
...  
#import <Foundation/Foundation.h> // 对头  
? 创建对象时尽量使用autorelease  
? 创建临时对象时,尽量同时在同一行中 autorelease 掉,而非使用单独的 release 语句  
? 虽然这样会稍微有点慢,但这样可以阻止因为提前 return 或其他意外情况导致的内存泄露。  
通盘来看这是值得的。如:  
// 避免这样使用(除非有性能的考虑)  
MyController* controller = [[MyController alloc] init];  
// ... 这里的代码可能会提前return ...  
[controller release];  
// 这样更好  
MyController* controller = [[[MyController alloc] init] autorelease];  
? 先autorelease,再retain  
? 在为对象赋值时,遵从“先autorelease,再retain”  
? 在将一个新创建的对象赋给变量时,要先将旧对象release掉,否则会内存泄露。市面上有很  
多方法来handle这种情况,这里选择“先autorelease,再retain”的方法,这种方法不易引  
入error。注意:在循环中这种方法会“填满”autorelease pool,稍稍影响效率,但是  
Google和我( :P )认为这个代价是可以接受的。如:  
- (void)setFoo:(GMFoo *)aFoo {  
[foo_ autorelease]; // 如果foo_和aFoo是同一个对象(foo_ == aFoo),  
dealloc不会被调用  
foo_ = [aFoo retain];  
}  
? dealloc的顺序要与变量声明的顺序相同  
? 这有利于review代码  
? 如果dealloc中调用其他方法来release变量,将被release的变量以注释的形式标注清楚  
? NSString的属性的setter使用“copy”  
? 禁止使用retain,以防止意外的修改了NSString变量的值。如:  
- (void)setFoo:(NSString *)aFoo {  
[foo_ autorelease];  
foo_ = [aFoo copy];  
}  
或  
@property (nonatomic, copy) NSString *aString;  
? 避免抛出异常(Throwing Exceptions)  
? 待完善  
? 对 nil 的检查  
? 仅在有业务逻辑需求时检查 nil,而非为了防止崩溃  
? 向 nil 发送消息不会导致系统崩溃,Objective-C运行时负责处理。  
? BOOL陷阱  
? 将int值转换为BOOL时应特别小心。避免直接和YES比较  
? Objective-C中,BOOL被定义为unsigned char,这意味着除了 YES (1) 和 NO (0)外它  
还可以是其他值。禁止将int直接转换(cast or convert)为BOOL。  
? 常见的错误包括:将数组的大小、指针值或位运算符的结果转换(cast or convert)为  
BOOL,因为该BOOL值的结果取决于整型值的最后一位  
? 将整型值转换为BOOL的方法:使用三元运算符返回YES / NO,或使用位运算符(&&, ||, !)  
? BOOL、_Bool和bool之间的转换是安全的,但是BOOL和Boolean间的转换不是安全的,所以  
将Boolean看成整型值。  
? 在Objective-C中,只允许使用BOOL  
? 如:  
// 禁止  
- (BOOL)isBold {  
return [self fontTraits] & NSFontBoldTrait;  
}  
- (BOOL)isValid {  
return [self stringValue];  
}  
// 对头  
- (BOOL)isBold {  
return ([self fontTraits] & NSFontBoldTrait) ? YES : NO;  
}  
- (BOOL)isValid {  
return [self stringValue] != nil;  
}  
- (BOOL)isEnabled {  
return [self isValid] && [self isBold];  
}  
? 禁止直接将BOOL和YES/NO比较,如:  
// 禁止  
BOOL great = [foo isGreat];  
if (great == YES)  
...  
// 对头  
BOOL great = [foo isGreat];  
if (great)  
...  
? 属性  
? 命名:与去掉“_”前缀的成员变量相同,使用@synthesize将二者联系起来。如:  
// abcd.h  
@interface MyClass : NSObject {  
@private  
NSString *_name;  
}  
@property (copy, nonatomic) NSString *name;  
@end  
// abcd.m  
@implementation MyClass  
@synthesize name = _name;  
@end  
? 位置:属性的声明紧随成员变量块之后,中间空一行,无缩进。如上例所示  
? 严把权限:对不需要外部修改的属性使用readonly  
? NSString使用copy而非retain  
? CFType使用@dynamic, 禁止使用@synthesize  
? 除非必须,使用nonatomic  
· Cocoa Pattern  
? Delegate Pattern(委托)  
? delegate对象使用assign,禁止使用retain。因为retain会导致循环索引导致内存泄露,  
并且此类型的内存泄露无法被Instrument发现,极难调试  
? 成员变量命名为_delegate,属性名为delegate  
? Model/View/Controller  
? Model和View分离  
? 不多解释  
? Controller独立于View和Controller  
? 不要在与view相关的类中添加过多的业务逻辑代码,这让代码的可重用性很差  
? Controller负责业务逻辑代码,且Controller的代码与view尽量无关  
? 使用 @protocal 定义回调APIs,如果并非所有方法都是必须的,使用 @optional 标示  
· 其他  
? init方法和dealloc方法是是最常用的方法,所以将他们放在类实现的开始位置  
? 使用空格将相同的变量、属性对齐,使用换行分组


===========================

一、文档结构管理

1.建立Libraries文件夹,所有第三方库放入其中。

2.建立Utilities文件夹,自已封装的类放入其中。

3.建立Constants.h头文件,所有的常量定义于其中。Constants.h文件放入Main文件组里面。

4.每个功能块放入一个Group,在目录里建立实际文件夹管理。

5.程序资源文件放入Supporting Files文件夹中。如.plist、数据库资料等。

6.在Supporting Files文件夹下建立Image、Music和Video等相关文件夹。图片、音频、视频等资源分别放入其中。图片资源以程序逻辑框架建立相应实体文件夹管理,若多个功能块共用的,建立Common文件夹,放入其中。

二、编码规范
说明:为了不影响阅读,一个类的代码行数尽量不要超过300行;一个方法尽量不要超过30行。有超过的在重构的时候想办法分解。
 
1.每个.h文件最开始处用简短文字说明一下该类的功能逻辑。

2.引入其它类时,若要作为实例变量的在.h中引入。否则在.m中引入。

3.@interface与@implementation#import 之间空一行。@synthesize紧接着@implementation写。

4.a:声明实例变量一律以属性声明。

   b:其它类要访问的实例变量和方法在.h文件中声明,否则声明于.m文件中。

 c:实例变量及方法以功能块放在一起,实现一个功能的连续着放在一起,另一个功能的空一行开始声明。

 d:实例变量紧接@interface后开始,方法在实例变量后空一行开始。

 e:每个功能块可作简单说明,单个实例变量方法可不作说明,关键的或特殊的变量或方法单独作说明。注释放于变量声明上一行。

5.类中第个功能模块以 #pragma mark -  分隔,上空两行,下空一行。

6.NSLog在测试完成后一律删除。

7.对于系统的常用类作实例变量声明时加入后缀:

 UIViewController:VC

   UIImage:Img  UIImageView:ImgView  UIView:View  UILabel:Lbl

   UIButton:Btn  UINavigationBar:NBar   UIToolBar:TBar  UISearchBar:SBar

   UITextField:TextField  UITextView:TextView

   NSArray:Array       NSMutableArray:MArray

   NSDictionary:Dict  NSMutableDictionary:MDict

 NSString:Str         NSMutableString:MStr

   NSSet:Set       NSMutableSet:MSet

8.程序中变量、方法命名尽量能以字面意思表示功能,对于需要用注释来解释的部分代码,注释以如下格式表述:

/**

 * 方法或变量说明

 * @param 参数1说明(针对方法)

 * @param 参数2说明(针对方法)

 * @return 若方法有返回值则对返回值作说明

 */

 

三、Xcode配置

1.全部统一用ARC。

2.用git管理代码。

3.在项目.pch文件加入一段去除release的NSLog代码:

#ifndef __OPTIMIZE__

#define NSLog(...) NSLog(__VA_ARGS__)

#else

#define NSLog(...) {}

#endif




===========================

代码写的不够漂亮工整符合规范?大家的代码风格过于迥异阅读困难?

  作为一个程序猿来说我越来越感觉到养成一个良好的代码风格的重要性,总结一下OC编程中的一些代码规范(苹果官方推荐的)。

  参考资料:IOS Developer Library 对建议的代码规范的说明 ,我也发现有同学翻译成中文版本了。

类(Class)命名原则

  就拿“NSMutableArray”来说 

  NS --- 前缀(Prefix)我的理解是表示类所属的“包”,这里的NS就表示的是Cocoa的基础类库,取公司名项目名什么的。(NS是NextStep公司的简写,里面的渊源就不细说了- -)前缀的意义就在于比较直观的划分了类的所属和范围。像Cocos2d里的前缀就是CC,box2d里面是B2,UserInterface->UI,CoreFoundation->CF,CoreGraphics->CG。如果我也能写一个开源架构或模块的话,我应该会取“SY”或是“Sunny”吧 :)
  MutableArray --- 类名,简短明确的形容类所表示的内容。首字母大写,驼峰标示。另外值得注意的是,由于NSMutableArray是NSArray的扩展子类,本质上形容的都是Array,所以应该保留父类的识别字样“Array”,但NSObject到NSArray不应该起名成NSArrayObject,也是避免了语义的重叠。

 

成员变量与属性存取器(Accessor)命名原则

  这也是让我纠结了挺久,一直不懂为什么很多代码里面类成员变量出现了下划线"_",而属性存取方法@property声明时不加下划线,实现时@synthesize又令两个名字相等。 

@interface SunnyTest : NSObject 
{
    NSArray * _array;  
}
@property (nonatomic,retain) NSArray * array;
@end

 

@implementation SunnyTest
@synthesize array = _array;
@end

  这样做的原因很简单,就是不暴露实例的成员变量,外界只能用@property声明的存取名来访问成员变量,所以就用下滑线加以区分。

  但在类内方法访问成员变量时就直接使用带下划线的名字,个人认为不要用self.xxxxx来使用,因为使用存取器本来就是对外的,在内部使用可能会造成一些不必要的错误,比如:

self.array = [[NSArray alloc] init]; //内存泄露

  这时就会造成内存泄露,因为[[NSArray alloc] init]生成NSArray对象之后赋值给self.array,由于array的存取方法中包含retain,这会使retainCount变成2,而实际应该是1,内存泄露且很难发现。

  改成这样的写法也是苹果官方推荐的。

_array = [[NSArray alloc] init];

 

方法(Method)起名规则

  一个规范的方法读起来应该像一句完整的话,读过之后便知函数的作用。执行性的方法应该以动词开头,小写字母开头,返回性的方法应该以返回的内容开头,但之前不要加get。

 

- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
+ (id)arrayWithArray:(NSArray *)array;

  如果有参数,函数名应该作为第一个参数的提示信息,若有多个参数,在参数前也应该有提示信息(一般不必加and)

  一些经典的操作应该使用约定的动词,如initWith,insert,remove,replace,add等等。




===========================

Objective-C编码规范:26个方面解决iOS开发问题

黄金路径

当使用条件语句编码时,左手边的代码应该是"golden" 或 "happy"路径。也就是不要嵌套if语句,多个返回语句也是OK。

应该:

  1. - (void)someMethod {  
  2.   if (![someOther boolValue]) {  
  3.     return;  
  4.   }  
  5.   //Do something important  
  6. }  

不应该:

  1. - (void)someMethod {  
  2.   if ([someOther boolValue]) {  
  3.     //Do something important  
  4.   }  
  5. }  

错误处理

当方法通过引用来返回一个错误参数,判断返回值而不是错误变量。

应该:

  1. NSError *error;  
  2. if (![self trySomethingWithError:&error]) {  
  3.   // Handle Error  
  4. }  

不应该:

  1. NSError *error;  
  2. [self trySomethingWithError:&error];  
  3. if (error) {  
  4.   // Handle Error  
  5. }  

在成功的情况下,有些Apple的APIs记录垃圾值(garbage values)到错误参数(如果non-NULL),那么判断错误值会导致false负值和crash。

单例模式

单例对象应该使用线程安全模式来创建共享实例。

  1. + (instancetype)sharedInstance {  
  2.   static id sharedInstance = nil;  
  3.   static dispatch_once_t onceToken;  
  4.   dispatch_once(&onceToken, ^{  
  5.     sharedInstance = [[self alloc] init];  
  6.   });  
  7.   return sharedInstance;  
  8. }  

这会防止possible and sometimes prolific crashes

换行符

换行符是一个很重要的主题,因为它的风格指南主要为了打印和网上的可读性。

例如:

  1. self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];  

一行很长的代码应该分成两行代码,下一行用两个空格隔开。

  1. self.productsRequest = [[SKProductsRequest alloc]   
  2.   initWithProductIdentifiers:productIdentifiers];  

Xcode工程

物理文件应该与Xcode工程文件保持同步来避免文件扩张。任何Xcode分组的创建应该在文件系统的文件体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。

尽可能在target的Build Settings打开"Treat Warnings as Errors,和启用以下additional warnings。如果你需要忽略特殊的警告,使用Clang's pragma feature

其他Objective-C编码规范

如果我们的编码规范不符合你的口味,可以查看其他的编码规范:

文章来源:GitHub,作者: Team

译者简介:刘耀柱(@Sam_Lau_Dev),iOS Developer兼业余Designer,参与开发技术前线iOS项目翻译,个人博客:http://www.jianshu.com/users/256fb15baf75/latest_articles,GitHub:https://github.com/samlaudev




===========================





    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多