Objective-C 编码规范
- 原文作者 : raywenderlich.com Team
- 原文出自 : raywenderlich.com Objective-C编码规范
- 译者 : Sailor Ga
在多人开发同一个app时,如果每个人的Objective-C编码风格都不一样,这样不易于保持代码一致性和难以Code Review。所以我在公司原有规范基础上结合 The official raywenderlich.com Objective-C style guide这篇关于Objective-C编码风格的文章,觉得可以结合起来作为我们Objective-C的编码标准,所以写了这篇文章。
写在最前头
我们是 iOS开发工程师 不是IOS也不是ios
我们用的语言是 Objective-C 和 Swift需求是暂时的,只有变化才是永恒的,面向变化编程,而不是面向需求编程。
不要过分追求技巧,降低程序的可读性。
简洁的代码可以让bug无处藏身。要写出明显没有bug的代码,而不是没有明显bug的代码。先把眼前的问题解决掉,解决好,再考虑将来的扩展问题。
The official raywenderlich.com Objective-C style guide
这篇编码风格指南概括了raywenderlich.com的编码规范,可能有些删减或修改。
介绍
我们制定Objective-C编码规范的原因是我们能够在我们的书,教程和初学者工具包的代码保持优雅和一致。即使我们有很多不同的作者来完成不同的书籍。
这里编码规范有可能与你看到的其他Objective-C编码规范不同,因为它主要是为了打印和web的易读性
关于作者
这编码规范的创建是由很多来自raywenderlich.com团队成员在Nicholas Waynik的带领下共同完成的。团队成员有: Soheil Moayedi Azarpour, Ricardo Rendon Cepeda, Tony Dahbura, Colin Eberhardt, Matt Galloway, Greg Heo, Matthijs Hollemans, Christopher LaPollo, Saul Mora, Andy Pereira, Mic Pringle, Pietro Rea, Cesare Rocchi, Marin Todorov, Nicholas Waynik, 和 Ray Wenderlich
我们也非常感谢 New York Times 和 Robots & Pencils’ Objective-C编码规范的作者。这两个编码规范为本指南的创建提供很好的起点。
背景
这里有些关于编码风格Apple官方文档,如果有些东西没有提及,可以在以下文档来查找更多细节:
- The Objective-C Programming Language
- Cocoa Fundamentals Guide
- Coding Guidelines for Cocoa
- iOS App Programming Guide
目录
- 语言
- 命名规范
- 代码组织
- 空格
- 注释
- 命名
- 方法
- 变量
- 属性特性
- public和private标记符
- 协议(protocols)
- 数据结构的语法糖
- 点符号语法
- 字面值
- 常量
- 枚举类型
- Case语句
- 私有属性
- 布尔值
- 条件语句
- init方法
- 类构造方法
- CGRect函数
- 黄金路径
- 错误处理
- 单例模式
- 换行符
- xcode工程
语言
使用US英语,不要使用拼音命名.
应该:
UIColor *myColor = [UIColor whiteColor];
不应该:
UIColor *myColour = [UIColor whiteColor];
命名规范
驼峰法,也是Objective-C社区的标准
驼峰法分小驼峰法和大驼峰法;
小驼峰法:除第一个单词之外,其他单词首字母大写;
大驼峰法相比小驼峰法,大驼峰法把第一个单词的首字母也大写了.
多用Tab少用空格
代码组织
在函数分组和protocol/delegate实现中使用 #pragma mark -
来分类方法,要遵循以下一般结构:
#pragma mark - 生命周期
- (instancetype)init {}
- (void)dealloc {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}
#pragma mark - 自定义SET\GET访问器
- (void)setCustomProperty:(id)value {}
- (id)customProperty {}
#pragma mark - 动作事件
- (IBAction)submitData:(id)sender {}
#pragma mark - 公共方法
- (void)publicMethod {}
#pragma mark - 私有方法
- (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 {}
空格
- 缩进使用4个空格即1个Tab,确保在Xcode偏好设置来设置。
- 方法大括号和其他大括号(
if
/else
/switch
/while
等.) 总是在同一行语句打开但在新行中关闭。
应该:
if (user.isHappy) {
//做一点事
} else {
//另外做一点事
}
不应该:
if (user.isHappy)
{
//做一点事
}
else {
//另外做一点事
}
- 在方法之间应该有且只有一行,这样有利于在视觉上更清晰和更易于组织。在方法内的空白应该分离功能,但通常都抽离出来成为一个新方法。
- 优先使用auto-synthesis。但如果有必要,
@synthesize
和@dynamic
应该在实现中每个都声明新的一行。 - 应该避免以冒号对齐的方式来调用方法。因为有时方法签名可能有3个以上的冒号以冒号对齐会使代码更加不易读。请不要这样做,尽管冒号对齐的方法包含代码块,因为Xcode的对齐方式令代码难以辨认。
应该:
// 块很容易读取
[UIView animateWithDuration:1.0 animations:^{
// 做一点事
} completion:^(BOOL finished) {
// 做一点事
}];
不应该:
// 冒号对齐使得块缩进难以阅读
[UIView animateWithDuration:1.0
animations:^{
// 做一点事
}
completion:^(BOOL finished) {
// 做一点事
}];
注释
提及规范,不得不马上提醒代码的注释问题!很多开发者注释过于粗糙,有些甚至都没有注释习惯,导致代码可读性差,版本迭代或是需求变更的时候不能及时定位到具体代码这很头疼……
说在前头
你不是一个人在战斗 你不是一个人在战斗 你不是一个人在战斗
重要的事情说三遍
给别人留条活路、请保持写代码就有注释的好习惯
Model要有注释 View要有注释 Controller要有注释 工具类要有注释 常量要有注释 等等…
建议多使用注释快捷键 cmd+alt+/
也可以使用插件VVDocumenter等
普通注释例:
// 关注DMS相关话题
[[SAIDMSManage sharedInstance] subscribe:model.id complete:^(NSArray *topics) {
FLOG(@"+++++++++++++++关注(订阅)话题成功");
}];
.h文件 注释例:
/**
* 推流地址
*/
@property (copy, nonatomic) NSString<Optional> *rtmpPush;
也可以:
/// 推流地址
@property (copy, nonatomic) NSString<Optional> *rtmpPush;
/**
Cell点击事件Block
@param listing 列表数据
*/
typedef void(^CellSelectRowBlock)(ListingModel *listing);
/**
* 头像
*/
@property (weak, nonatomic) IBOutlet UIButton *avatar;
/**
返回直播或录播列表
@param frame View 大小
@param category 类型
@return view
*/
- (instancetype)initSAIViewWithFrame:(CGRect)frame category:(NSString *)category;
/**
* Cell点击事件实例
*/
@property (copy, nonatomic) CellSelectRowBlock rowBlock;
/**
Cell点击事件回调
@param rowBlock CellSelectRowBlock block
*/
- (void)selectRowBlock:(CellSelectRowBlock)rowBlock;
.m文件 注释例:
/// 直播工具界面
@property (strong, nonatomic) PushToolsView *pushTools;
/// SDK相关
@property (strong, nonatomic) TXLivePush *publisher;
/// SDK配置
@property (strong, nonatomic) TXLivePushConfig *config;
也可以:
/**
* 直播工具界面
*/
@property (strong, nonatomic) PushToolsView *pushTools;
/**
* SDK相关
*/
@property (strong, nonatomic) TXLivePush *publisher;
/**
* SDK配置
*/
@property (strong, nonatomic) TXLivePushConfig *config;
在函数分组和protocol/delegate实现中使用 #pragma mark -
来分类方法
#pragma mark - 下拉刷新数据
- (void)fetchData{}
#pragma mark - 上拉加载数据
- (void)fetchMoreData{}
#pragma mark ----- TableView的代理方法 -----
#pragma mark 分组数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{}
#pragma mark 行数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{}
#pragma mark 每个Cell高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{}
#pragma mark 初始化Cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{}
#pragma mark 设置Cell内容
- (void)configLiveChildCell:(LiveChildViewCell *)cell atIndexPath:(NSIndexPath *)indexPath{}
#pragma mark Cell的点击事件
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{}
命名
Apple命名规则尽可能坚持,特别是与这些相关的memory management rules (NARC).
命名应该尽可能的清晰和简洁,但在 Objective-C 中,清晰比简洁更重要。由于 Xcode 强大的自动补全功能,我们不必担心名称过长的问题。
长的, 描述性的方法和变量命名是好的.
应该:
UIButton *settingsButton;
不应该:
UIButton *setBut;
三个字符前缀应该经常用在类和常量命名,但在Core Data的实体名中应被忽略
常量应该使用驼峰式命名规则,所有的单词首字母大写和加上与类名有关的前缀。
应该:
static NSTimeInterval const SAITutorialViewControllerNavigationFadeAnimationDuration = 0.3;
不应该:
static NSTimeInterval const fadetime = 1.7;
属性也是使用驼峰式,但首单词的首字母小写。对属性使用auto-synthesis,而不是手动编写@ synthesize语句,除非你有一个好的理由。
应该:
@property (copy, nonatomic) NSString *descriptiveVariableName;
不应该:
id varnm;
下划线
当使用属性时,实例变量应该使用self.
。来访问和改变。这就意味着所有属性将会视觉效果不同,因为它们前面都有self.
。
但有一个特例:在初始化方法里,实例变量(例如,_variableName)应该直接被使用来避免getters/setters潜在的副作用。
局部变量不应该包含下划线。
方法
在方法签名中,应该在方法类型(-/+ 符号)之后有一个空格。在方法各个段之间应该也有一个空格(符合Apple的风格)。在参数之前应该包含一个具有描述性的关键字来描述参数。
“and”这个词的用法应该保留。它不应该用于多个参数来说明,就像initWithWidth:height:
以下这个例子:
应该:
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
不应该:
-(void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height;
变量
变量尽量以描述性的方式来命名。单个字符的变量命名应该尽量避免,除了在 for()
循环。
星号表示变量是指针。例如,NSString *text
既不是 NSString* text
也不是 NSString * text
, 除了一些特殊情况下常量
私有变量 应该尽可能代替实例变量的使用。尽管使用实例变量是一种有效的方式,但更偏向于使用属性来保持代码一致性。
通过使用’back’属性(_variable,变量名前面有下划线)直接访问实例变量应该尽量避免,除了在初始化方法(init
, initWithCoder:
, 等), dealloc
方法和自定义的setters和getters。想了解关于如何在初始化方法和dealloc直接使用Accessor方法的更多信息,查看这里.
应该:
@interface SAITutorial : NSObject
@property (copy, nonatomic) NSString *tutorialName;
@end
不应该:
@interface SAITutorial : NSObject {
NSString *tutorialName;
}
属性特性
所有属性特性应该显式地列出来,有助于新手阅读代码。属性特性的顺序应该是storage、atomicity,与在Interface Builder连接UI元素时自动生成代码一致。
应该:
@property (weak, nonatomic) IBOutlet UIView *containerView;
@property (copy, nonatomic) NSString *tutorialName;
不应该:
@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic) NSString *tutorialName;
NSString应该使用 copy
而不是 strong
的属性特性。
为什么? 即使你声明一个 NSString
的属性,有人可能传入一个 NSMutableString
的实例,然后在你没有注意的情况下修改它。
应该:
@property (copy, nonatomic) NSString *tutorialName;
不应该:
@property (strong, nonatomic) NSString *tutorialName;
public和private标记符
@public 和 @private 标记符应该以一个空格来进行缩进:
@interface MyClass : NSObject {
@public
...
@private
...
}
@end
协议(protocols)
在书写协议的时候注意用 <> 括起来的协议和类型名之间是没有空格的,比如
IPCConnectHandler(), 这个规则适用所有书写协议的地方,包括函数声明、
类声明、实例变量等等:
@interface MyProtocoledClass : NSObject {
@private
id _delegate;
}
- (void)setDelegate:(id)aDelegate;
@end
数据结构的语法糖
应该使用可读性更好的语法糖来构造 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",
};
点符号语法
点语法是一种很方便封装访问方法调用的方式。当你使用点语法时,通过使用getter或setter方法,属性仍然被访问或修改。想了解更多,阅读 这里
点语法应该总是被用来访问和修改属性,因为它使代码更加简洁。[]符号更偏向于用在其他例子。
应该:
NSInteger arrayCount = [self.array count];
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
不应该:
NSInteger arrayCount = self.array.count;
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
字面值
NSString
, NSDictionary
, NSArray
, 和 NSNumber
的字面值应该在创建这些类的不可变实例时被使用。请特别注意 nil
值不能传入 NSArray
和 NSDictionary
字面值,因为这样会导致crash。
应该:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingStreetNumber = @10018;
不应该:
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018];
常量
常量是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用static
来声明而不是使用 #define
,除非显式地使用宏。
应该:
static NSString * const SAIAboutViewControllerCompanyName = @"RayWenderlich.com";
static CGFloat const SAIImageThumbnailHeight = 50.0;
不应该:
#define CompanyName @"RayWenderlich.com"
#define thumbnailHeight 2
枚举类型
当使用 enum
时,推荐使用新的固定基本类型规格,因为它有更强的类型检查和代码补全。现在SDK有一个宏 NS_ENUM()
来帮助和鼓励你使用固定的基本类型。
例如:
typedef NS_ENUM(NSInteger, SAILeftMenuTopItemType) {
SAILeftMenuTopItemMain,
SAILeftMenuTopItemShows,
SAILeftMenuTopItemSchedule
};
你也可以显式地赋值(展示旧的k-style常量定义):
typedef NS_ENUM(NSInteger, SAIGlobalConstants) {
SAIPinSizeMin = 1,
SAIPinSizeMax = 5,
SAIPinCountMin = 100,
SAIPinCountMax = 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’是不需要的。例如:
SAILeftMenuTopItemType menuType = SAILeftMenuTopItemMain;
switch (menuType) {
case SAILeftMenuTopItemMain:
// ...
break;
case SAILeftMenuTopItemShows:
// ...
break;
case SAILeftMenuTopItemSchedule:
// ...
break;
}
私有属性
私有属性应该在类的实现文件中的类扩展(匿名分类)中声明,命名分类(比如private
)应该从不使用除非是扩展其他类。匿名分类应该通过使用 <headerfile>+Private.h
文件的命名规则暴露给测试。
例如:
@interface SAIDetailViewController ()
@property (strong, nonatomic) GADBannerView *googleAdView;
@property (strong, nonatomic) ADBannerView *iAdView;
@property (strong, nonatomic) UIWebView *adXWebView;
@end
布尔值
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;
文字和例子从这里引用 Cocoa Naming Guidelines.
条件语句
条件语句主体为了防止出错应该使用大括号包围,即使条件语句主体能够不用大括号编写(如,只用一行代码)。这些错误包括添加第二行代码和期望它成为if语句;还有,even more dangerous defect 可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。除此之外,这种风格与其他条件语句的风格保持一致,所以更加容易阅读。
应该:
if (!error) {
return success;
}
不应该:
if (!error)
return success;
or
if (!error) return success;
三元操作符
当需要提高代码的清晰性和简洁性时,三元操作符?:
才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if
语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。
Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。
应该:
NSInteger value = 5;
result = (value != 0) ? x : y;
BOOL isHorizontal = YES;
result = isHorizontal ? x : y;
不应该:
result = a > b ? x = c > d ? c : d : y;
init方法
尽管很多时候能用 new 代替 alloc init 方法,但这可能会导致调试内存时出现不可预料的问题。Cocoa 的规范就是使用 alloc init 方法,使用 new 会让一些读者困惑。
init方法应该遵循Apple生成代码模板的命名规则。返回类型应该使用instancetype
而不是id
- (instancetype)init {
self = [super init];
if (self) {
// ...
}
return self;
}
查看关于instancetype的文章 Class Constructor Methods。
类构造方法
当类构造方法被使用时,它应该返回类型是instancetype
而不是id
。这样确保编译器正确地推断结果类型。
@interface Airplane
+ (instancetype)airplaneWithType:(SAIAirplaneType)type;
@end
关于更多instancetype信息,请查看 NSHipster.com。
CGRect函数
当访问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 };
黄金路径
当使用条件语句编码时,左手边的代码应该是”golden” 或 “happy”路径。也就是不要嵌套if
语句,多个返回语句也是OK。
应该:
- (void)someMethod {
if (![someOther boolValue]) {
return;
}
//做重要的事
}
不应该:
- (void)someMethod {
if ([someOther boolValue]) {
//做重要的事
}
}
错误处理
当方法通过引用来返回一个错误参数,判断返回值而不是错误变量。
应该:
NSError *error;
if (![self trySomethingWithError:&error]) {
// Handle Error
}
不应该:
NSError *error;
[self trySomethingWithError:&error];
if (error) {
// Handle Error
}
在成功的情况下,有些Apple的APIs记录垃圾值(garbage values)到错误参数(如果non-NULL),那么判断错误值会导致false负值和crash。
单例模式
单例对象应该使用线程安全模式来创建共享实例。
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
这会防止 possible and sometimes prolific crashes.
换行符
换行符是一个很重要的主题,因为它的风格指南主要为了打印和网上的可读性。
例如:
self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
一行很长的代码应该分成两行代码,下一行用两个空格隔开。
self.productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:productIdentifiers];
xcode工程
物理文件应该与Xcode工程文件保持同步来避免文件扩张。任何Xcode分组的创建应该在文件系统的文件体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。
尽可能在target的Build Settings打开”Treat Warnings as Errors,和启用以下 additional warnings 如果你需要忽略特殊的警告,使用 Clang’s pragma feature.
其他Objective-C编码规范
其他的编码规范: