<禅与 Objective-C 编程艺术>阅读记录

1. nil检查

  • if(!boolValue)代替if(boolValue == nil)

例子:AMKElement判断非主线程

if (![NSThread isMainThread]) {
    [self performSelectorOnMainThread:@selector(doubleTap) withObject:nil waitUntilDone:YES];
    return;
}

2. 将次要分支写在前,并用return,防止一大坨代码包在if里面

推荐:

- (void)someMethod {
  if (![someOther boolValue]) {
      return;
  }
  //Do something important
}

不推荐:

- (void)someMethod {
  if ([someOther boolValue]) {
    //Do something important
  }
}

例子:

// 若不在主线程,则切到主线程再调用本方法
if (![NSThread isMainThread]) {
    [self performSelectorOnMainThread:@selector(doubleTap) withObject:nil waitUntilDone:YES];
    return;
}

// 在主线程
// 处理一大坨事情

3. 复杂的if语句,应该把判断条件提取出来设为一个BOOL变量

例子:

BOOL isMathced = [predicate evaluateWithObject:element];
if (isMathced) {
    UIView *view = (UIView *)element;
}
return isMathced;

4. 三元运算符

例子:

UILabel *label = (labels.count > 0 ? labels[0] : nil);
  • 当三元运算符的第一个分支就是判断条件时,建议写成:
    result = object ? : [self createObject];

例子:

UIWindow *window = _viewController.view.window ?: [[[UIApplication sharedApplication] delegate] window];

5.错误处理

推荐:

NSError *error = nil;
if (![self trySomethingWithError:&error]) {
    // Handle Error
}

不推荐:

NSError *error;
[self trySomethingWithError:&error];
if (error) {
    // Handle Error
}

错误例子:

  • <s>如上删除文件,在删除的过程中可能对error变量进行赋值,然后打印error,可知错误信息</s>
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
if (error) {
    NSLog(@"Move failed: %@", error);
}
  • 好吧我一开始理解错了,这个例子给的是NSError常规的用法,但是此条规则的本意是不推荐检查error的引用,而应该检查方法的返回值

正确例子:

NSError *error = nil;
if (![[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
    NSLog(@"Move failed: %@", error);
}

6.常量

  • 推荐使用驼峰写法
  • 建议用static声明为静态常量,不建议用define定义常量,除非你要定义宏

例子:

static const double kElementSufficientlyVisiblePercentage = 0.75;
  • 需要暴露给外部的常量,在头文件中用extern标示,并在实现文件中赋值

例子:

// xxx.h
extern NSString *const AMKJavaScriptWillStartLoadingNotification;
// xxx.m
NSString *const AMKJavaScriptWillStartLoadingNotification = @"AMKJavaScriptWillStartLoadingNotification";

7.方法名

  • 参数前加一个描述性的关键词
  • 不建议用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;

不推荐:

- (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;  // Never do this.

8.字面值

  • 建议用字面值创建不可变的NSString, NSDictionary, NSArray, 和 NSNumber 对象,不要将 nil 传进 NSArray 和 NSDictionary 里

推荐:

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @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 *buildingZIPCode = [NSNumber numberWithInteger:10018];
  • 对于可变版本,推荐使用 NSMutableArray, NSMutableString 等
  • @[] mutableCopy这种写法不推荐
  • 但是还是使用可变拷贝mutableCopy

推荐:

NSMutableArray *tmpList = [[NSMutableArray alloc] init];
NSMutableString *mutableString = [originalString mutableCopy];

不推荐:

NSMutableArray *tmpList = [@[] mutableCopy];

9.初始化方法

  • 指定初始化方法(designated initializer)
  • 标注哪一个初始化方法是designated,用编译器指令 __attribute__((objc_designated_initializer)) 这样如果新的designated initializer没有调用超类的designated initializer,就会警告

例子:

#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))

- (instancetype)init;
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;

10.instancetype V.S. id

这篇文章对两者的差别讲的蛮清楚的
http://blog.csdn.net/wzzvictory/article/details/16994913

  • 原来未知的返回类型对象,都 id 关键词;直到clang 3.5开始提供 instancetype 关键词
  • 关联返回类型:会返回一个方法所在类类型的对象,系统的alloc、new、init等是关联返回类型的,比如[NSArray alloc]返回的就是NSArray*[[NSArray alloc] init]返回的也是NSArray*
  • instancetype的作用,就是使那些非关联返回类型的方法返回所在类的类型
// 申明返回类型为id
@interface NSArray  
+ (id)constructAnArray;  
@end 

[NSArray constructAnArray]; // 得到的返回类型和申明的一样,即id

// 申明返回类型为instancetype
@interface NSArray  
+ (instancetype)constructAnArray;  
@end 

[NSArray constructAnArray]; // 得到的返回类型是所在类的类型,即NSArray*
  • 返回类类型的好处是,编译器能够在编译截断就判断返回类型能否实现
// 编译时会报错 : "No visible @interface for `NSArray` declares the selector `mediaPlaybackAllowsAirPlay`"
[[[NSArray alloc] init] mediaPlaybackAllowsAirPlay];
// 编译时不会报错
[[NSArray array] mediaPlaybackAllowsAirPlay];
  • instancetype 的优势是能返回所在类类型(id 只能返回未知类型)
  • id 的优势是既能当返回值,又能当参数(instancetype 只能当返回值)
  • 所以最好的做法就是,用 id 当参数,用 instancetype 当返回值(当然针对的是会返回类的实例的方法)
  • 这样做法一个额外的好处就是,可以一目了然知道哪些方法返回了类的实例

例子

- (instancetype)initWithAccessibilityElement:(id)accessibilityElement{
    self = [super init];
    if (self) {
        _accessibilityElement = accessibilityElement;
    }
    return self;
}

写到这里,例子很多是从@巴格的InAppMonkey中摘录出来的,不得不说,代码功底很好,几乎所有写法都是Best Practice,值得学习!!!

11. 懒加载

  • 当实例化一个对象耗费资源较多,就需要重写getter方法以延迟实例化,而不是在init方法里给对象分配内存

例子

- (NSString*)automationDirectory{
    if (!_automationDirectory) {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        _automationDirectory = [paths objectAtIndex:0];
        _automationDirectory = [_automationDirectory stringByAppendingPathComponent:@"TMUIAutomation"];
    }
    if (![[NSFileManager defaultManager] fileExistsAtPath:_automationDirectory]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:_automationDirectory withIntermediateDirectories:NO attributes:nil error:nil];
    }
    return _automationDirectory;
}

12. 参数断言

  • 你的方法可能要求一些参数来满足特定的条件(比如不能为nil),在这种情况下最好使用 NSParameterAssert() 来断言条件是否成立或是抛出一个异常

例子

- (NSString *)grey_printDescriptionForElement:(id)element atLevel:(NSUInteger)level {
    AMKSureNotStopReturnValue(nil)

    NSParameterAssert(element);
    NSMutableString *printOutput = [NSMutableString stringWithString:@""];
    
    //...
}

13. Category

  • 建议在category类名中使用前缀

例子

@interface NSString(AMK)

- (NSString *)amk_decodeHTMLCharacterEntities;
- (NSString *)amk_encodeHTMLCharacterEntities;

@end

14. Protocol

  • 这块 Zen 给的建议没有看明白

15. NSNotification

  • 当自定义NSNotification时,应该把通知的名字定义为一个字符串常量,然后在公开接口中将其声明为extern

例子

// AMKBridge.h
extern NSString *const AMKReloadNotification;
// AMKBridge.m
NSString *const AMKReloadNotification = @"AMKReloadNotification";

16. pragma

  • 当你使用ARC的时候,编译器帮你插入了内存管理相关的调用。但是这样可能产生一些烦人的事情。比如你使用 NSSelectorFromString 来动态地产生一个 selector 调用的时候,ARC不知道这个方法是哪个并且不知道应该用那种内存管理方法,你会被提示 performSelector may cause a leak because its selector is unknown
  • 如果你知道你的代码不会导致内存泄露,你可以通过加入这些代码忽略这些警告

例子

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [navBar.topItem.leftBarButtonItem.target performSelector:navBar.topItem.leftBarButtonItem.action withObject:navBar.topItem.leftBarButtonItem];
#pragma clang diagnostic pop
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容