1、使用预编译指令#pragma mark组织代码
(1) 视图或控制器生命周期相关的代码
#pragma mark - Life Cycle
- (void)dealloc {}
- (instancetype)init {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)viewDidAppear:(BOOL)animated {}
- (void)viewWillDisappear:(BOOL)animated {}
- (void)viewDidDisappear:(BOOL)animated {}
(2)通知相关的代码
#pragma mark - Notifications
- (void)addNotifications {}
- (void)removeNotifications {}
- (void)applicationDidEnterBackgroundNotification:(NSNotification *)notification {}
(3)事件响应相关代码
#pragma mark - Event Responses
- (void)actionForSettingButton:(UIButton *)button {}
- (void)actionForTapGestureRecognizer:(UITapGestureRecognizer *)tap {}
(4)协议相关的代码
#pragma mark - UIScrollViewDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {}
(5)私有的辅助方法
#pragma mark - Helper
- (NSString *)titleForItemAtIndex:(NSUInteger)index {}
(6)Setter与Getter方法
#pragma mark - Accessors
- (UIImageView *)imageView
{
if (_imageView) { return _imageView; }
_imageView = [UIImageView new];
return _imageView;
}
2、注释
(1)头文件对外公开的属性、方法、变量、枚举、常量使用统一的注释(快捷键alt + command + /)
/**
按钮上显示的标题
*/
@property (nonatomic, copy) NSString *title;
/**
图片编辑完成回调方法
@param vc 图片编辑控制器
@param result 图片编辑结果
*/
- (void)photoEditViewController:(TuSDKICViewController *)vc didCompleteWithResult:(TuSDKResult *)result;
(2)实现文件中关键代码或需要特别说明的代码要添加注释
3、属性声明
属性修饰符书写顺序:原子性,读写权限,内存管理,别名。另外注意weak、copy、readonly修饰符的使用。
@property (nonatomic, weak) id<UIScrollViewDelegate> delegate;
@property (nonatomic, assign) NSUInterger selectedIndex;
@property (nonatomic, readonly) UIView *view;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) void (^completionHandler)();
@property (nonatomic, strong) NSArray<UIImageView *> *imageViews;
4、instancetype
对于明确返回类实例的方法,其返回类型应使用instancetype代替id类型。
5、命名
方法命名时谨慎使用前缀alloc/init/copy/mutableCopy/new/set/get
编译器约定,对于alloc/init/copy/mutableCopy/new这几个家族的方法,声明后面默认加NS_RETURNS_RETAINED标识;而其他方法默认添加NS_RETURNS_NOT_RETAINED标识。
系统自动生成的属性设置方法是以set开头的,另外可能对KVC查找键名称会有影响。
根据苹果推荐的命名规范,一般避免使用get作为方法名前缀。
6、使用预编译指令忽略特定的警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
//确定这里不会发生内存泄漏时,使用预编译指令忽略警告
[myObj performSelector:mySelector withObject:name];
#pragma clang diagnostic pop
7、注意block的循环引用
(1)block本身没有被作为属性引用的情况
[UIView animateWithDuration:0.25 animations:^{
self.view.alpha = 0;
}];
NSInteger (^setIndexBlock)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
self.index = a + b;
return a + b;
};
setIndexBlock(1, 2);
(2)block被作为属性引用的情况1
比如block只会在主线程执行,或block内部只有一条语句:
__weak typeof(self) weakSelf = self;
backButton.clickBlock = ^{
[weakSelf popViewControllerAnimated:YES];
};
(3)block被作为属性引用的情况2
比如block同时被多个对象引用,并且在子线程执行,则有可能在block执行过程中weakSelf变成nil:
__weak typeof(self) weakSelf = self;
myObj.myBlock = ^{
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf)
{
[strongSelf doSomething];
[strongSelf doSomethingElse];
}
};
(4)主动解除循环引用
对于大部分情况,通过弱引用可以避免发生循环引用。但有些场合由于需求不能使用弱引用,即要求被引用的对象在block执行完成前不允许被释放,这种情况只要能保证block一定会被执行到,则可以在block内部主动解除循环引用。
self.myBlock = ^{
NSLog(@"%@", self);
self.myBlock = nil;
};
(5)其他
两个常用的宏定义:
#define weakify(o) __weak typeof(o) weakObj = o;
#define strongify(o) __strong typeof(o) strongObj = o;
weakify(self)
self.myBlock = ^{
NSLog(@"%@", weakObj);
strongify(weakObj)
NSLog(@"%@", strongObj);
};
8、避免if语句多层嵌套
// 不推荐写法
if (condition1)
{
if (condition2)
{
if (condition3)
{
//doSomething
}
}
}
// 推荐写法
if (!condition1) { return; }
if (!condition2) { return; }
if (!condition3) { return; }
//doSomething
9、工程目录组织结构
一般工程目录主要结构:
- MyProject:项目代码
- MyProjectTests:单元测试代码
- Frameworks:系统框架
- Pods:第三方框架
项目代码目录MyProject:
- Main
包括main文件、AppDelegate、定制的ViewController基类、TabBarController等整个应用结构相关,与具体功能无关的代码文件。
- Common
放置应用中全局功能相关的代码文件,比如启动闪屏、登录、帐号系统、消息推送等。
- Module1..n
通常按照TabBar上功能模块划分的一个模块。
- Categories
放置所有的分类。
- Utilities
放置工具类,比如网络工具、数据库工具、缓存管理工具等。
- Venders
放置所有第三方库文件。
- Resources
放置全局的资源类文件,比如plist文件、图片资源、视频音频文件、pch文件、JSON文件等。
每个功能模块下,比如Module1,可以根据不同子功能建立不同的子目录。目录中的文件包括该功能相关的全部文件,不包括一些全局或公共的文件,目录内不再划分view/viewController/model。目的是将代码尽量按照功能进行组织,而不是按照类型。另外,工程中的group应与实际目录一一对应。
参考资料:
《禅与Objective-C编程艺术》https://objc-zen-book.books.yourtion.com/
《纽约时报移动团队Objective-C编码规范》https://github.com/NYTimes/objective-c-style-guide/blob/master/README_zh-Hans.md
《Effective Objective-C 2.0》