type
Post
status
Published
date
Mar 16, 2016
slug
summary
tags
开发
category
技术分享
icon
password
GitBook for 《禅与 Objective-C 编程艺术》 笔记。
Golden Path
- (void)someMethod { if (![someOther boolValue]) { return; } //Do something important }
错误处理
通过参数返回 error 的引用,使用这样的方法时应当检查方法的返回值,而非 error 的引用。
NSError *error = nil; if (![self trySomethingWithError:&error]) { // Handle Error }
一些苹果的 API 在成功的情况下会对 error 参数(如果它非 NULL)写入垃圾值(garbage values),所以如果检查 error 的值可能导致错误 (甚至崩溃)。
命名
常量加类名前缀
static const NSTimeInterval ZOCSignInViewControllerFadeOutAnimationDuration = 0.4;
方法名少用 and
- (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;
类
类名应该以三个大写字母作为前缀(双字母前缀为 Apple 的类预留),说明性的部分放在前缀和父类名的在中间(如:
ZOCTimelineViewController
)。初始化
推荐的代码组织方式是将 dealloc 方法放在实现文件的最前面(直接在 @synthesize 以及 @dynamic 之后),init 应该跟在 dealloc 方法后面。如果有多个初始化方法, 指定初始化方法 (designated initializer) 应该放在最前面,间接初始化方法 (secondary initializer) 跟在后面,这样更有逻辑性。
指定初始化器:
NS_DESIGNATED_INITIALIZER
#define ZOC_UNAVAILABLE_INSTEAD(msg) __attribute__((unavailable("Invoke the designated initializer, use ["msg "].")))#define ZOC_UNAVAILABLE(msg) __attribute__((unavailable(msg)))
@interface ZOCNewsViewController : UIViewController - (instancetype)initWithNews:(ZOCNews *)news NS_DESIGNATED_INITIALIZER; - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil ZOC_UNAVAILABLE_INSTEAD("initWithNews:"); - (instancetype)init ZOC_UNAVAILABLE_INSTEAD("initWithNews:"); @end @implementation ZOCNewsViewController - (id)initWithNews:(ZOCNews *)news { // call to the immediate superclass's designated initializer (调用直接超类的 designated initializer) self = [super initWithNibName:nil bundle:nil]; if (self) { _news = news; } return self; } // Override the immediate superclass's designated initializer (重载直接父类的 designated initializer) - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { // call the new designated initializer return [self initWithNews:nil]; } @end
属性
@property (nonatomic, readwrite, copy) NSString *name;
- 属性的参数顺序:原子性,读写 和 内存管理
- setter 方法中 KVO 通知(willChangeValueForKey, didChangeValueForKey) 会被自动执行。
- 懒加载是 KVO 不友好的,因为其在 getter 中 change value
- 永远不要在 init 方法(以及其他初始化方法)里面用 getter 和 setter 方法
方法
- 善用断言
- 私有方法不要用
_
前缀,这是 apple 保留前缀
相等性
- 你需要同时实现 isEqual 和 hash 方法。如果两个对象是被 isEqual 认为相等的,它们的 hash 方法需要返回一样的值。但是如果 hash 返回一样的值,并不能确保他们相等。
- 用
isEqualTo<#class-name-without-prefix#>:
这样的格式实现一个相等性检查方法。
Categories
- category 方法前加小写前缀以及下划线,比如
(id)zoc_myCategoryMethod
。
protocols
- 抽象接口
NSNotification
- 用类名前缀作为这个通知名字的前缀。
- 用一个 Did/Will 这样的动词以及用 “Notifications” 后缀来命名这个通知。
// Foo.h extern NSString * const ZOCFooDidBecomeBarNotification // Foo.m NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";
代码组织
利用代码块
NSURL *url = ({ NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, endpoint]; [NSURL URLWithString:urlString]; });
善用 Pragma
- (void)dealloc { /* ... */ } - (instancetype)init { /* ... */ } #pragma mark - View Lifecycle (View 的生命周期) - (void)viewDidLoad { /* ... */ } - (void)viewWillAppear:(BOOL)animated { /* ... */ } - (void)didReceiveMemoryWarning { /* ... */ } #pragma mark - Getter & Setter (自定义访问器) - (void)setCustomProperty:(id)value { /* ... */ } - (id)customProperty { /* ... */ } #pragma mark - IBActions - (IBAction)submitData:(id)sender { /* ... */ } #pragma mark - Public - (void)publicMethod { /* ... */ } #pragma mark - Private - (void)zoc_privateMethod { /* ... */ } #pragma mark - UITableViewDataSource - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ } #pragma mark - ZOCSuperclass // ... 重载来自 ZOCSuperclass 的方法 #pragma mark - NSObject - (NSString *)description { /* ... */ }
明确编译器警告和错误
#error Whoa, buddy, you need to check for zero here!#warning Dude, don't compare floating point numbers like this!
公开方法属性要有注释
对象通讯
block
- (void)downloadObjectsAtPath:(NSString *)path completion:(void(^)(NSArray *objects, NSError *error))completion { if (objects) { // do something with the data } else { // some error occurred, 'error' variable should not be nil by contract } }
- 若 objects 不为 nil,则 error 必须为 nil
- 若 objects 为 nil,则 error 必须不为 nil
注意避免 block 可能引起的循环引用问题,采用[@weakify/@strongify](https://github.com/jspahrsummers/libextobjc/blob/master/extobjc/EXTScope.h)
委托和数据源
- 代理方法必须以调用者(即委托者)作为第一个参数
- 避免双重引用
- 考虑用多重委托代替 NSNotifications。LBDelegateMatrioska
面向切面编程
Aspect Oriented Programming (AOP,面向切面编程),在 Objective-C 的世界里,这意味着使用运行时的特性来为指定的方法追加切面 。切面所附加的行为可以是这样的:
- 在类的特定方法调用前运行特定的代码
- 在类的特定方法调用后运行特定的代码
- 增加代码来替代原来的类的方法的实现
Aspects 库