美文网首页
《禅与 Objective-C 编程艺术》阅读笔记

《禅与 Objective-C 编程艺术》阅读笔记

作者: AprSnow | 来源:发表于2018-07-30 11:26 被阅读10次

    0x00

    不要嵌套 if 语句,使用多个 return 可以避免增加循环的复杂度,并提高代码的可读性。

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

    0x01

    当三元运算符的第二个参数(if 分支)返回和条件语句中已经检查的对象一样的对象的时候,下面的表达方式更灵巧:

    result = object ? : [self createObject];
    

    0x02

    有些方法通通过参数返回 error 的引用,使用这样的方法时应当检查方法的返回值,而非 error 的引用。

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

    0x03

    推荐使用常量来代替字符串字面值和数字,这样能够方便复用,而且可以快速修改而不需要查找和替换。常量应该用 static 声明为静态常量,而不要用 #define,除非它明确的作为一个宏来使用。

    static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
    static const CGFloat ZOCImageThumbnailHeight = 50.0f;
    

    0x04

    使用一个线程安全的模式来创建共享的实例。

    + (instancetype)sharedInstance
    {
       static id sharedInstance = nil;
       static dispatch_once_t onceToken = 0;
       dispatch_once(&onceToken, ^{
          sharedInstance = [[self alloc] init];
       });
       return sharedInstance;
    }
    

    0x05

    在你希望提供你自己的初始化函数的时候,你应该遵守三个步骤来保证正确的行为:

    1. 定义你的 designated initializer,确保调用了直接超类的 designated initializer
    2. 重载直接超类的 designated initializer。调用你的新的 designated initializer
    3. 为新的 designated initializer 写文档。

    0x06

    在文档中明确哪一个初始化方法是 designated 的,可以用编译器的指令 __attribute__((objc_designated_initializer)) 来标记意图。

    0x07

    应该总是使用 settergetter 方法访问属性,除了 initdealloc 方法。

    0x08

    总应该用 gettersetter ,因为:

    • 使用 setter 会遵守定义的内存管理语义(strong, weak, copy etc...) ,这个在 ARC 之前就是相关的内容。举个例子,copy 属性定义了每个时候你用 setter 并且传送数据的时候,它会复制数据而不用额外的操作。
    • KVO 通知(willChangeValueForKey, didChangeValueForKey) 会被自动执行。
    • 更容易 debug:你可以设置一个断点在属性声明上并且断点会在每次 getter / setter 方法调用的时候执行,或者你可以在自己的自定义 setter/getter 设置断点。
    • 允许在一个单独的地方为设置值添加额外的逻辑。

    0x09

    应该倾向于用 getter

    • 它是对未来的变化有扩展能力的(比如,属性是自动生成的)。
    • 它允许子类化。
    • 更简单的 debug (比如,允许拿出一个断点在 getter 方法里面,并且看谁访问了特别的 getter
    • 它让意图更加清晰和明确:通过访问 ivar _anIvar 你可以明确的访问 self->_anIvar.这可能导致问题。在 block 里面访问 ivar (你捕捉并且 retainself,即使你没有明确的看到 self 关键词)。
    • 它自动产生 KVO 通知。
    • 在消息发送的时候增加的开销是微不足道的。

    0x0A

    永远不要在 init 方法(以及其他初始化方法)里面用 gettersetter 方法,应当直接访问实例变量。

    0x0B

    同样在 dealloc 方法中同样需要被注意。

    0x0C

    任何可以用来用一个可变的对象设置的(比如 NSString, NSArray, NSURLRequest)属性的的内存管理类型必须是 copy 的。

    0x0D

    懒加载,当实例化一个对象需要耗费很多资源,或者配置一次就要调用很多配置相关的方法而你又不想弄乱这些方法时,我们需要重写 getter 方法以延迟实例化,而不是在 init 方法里给对象分配内存。通常这种操作使用下面这样的模板:

    - (NSDateFormatter *)dateFormatter {
      if (!_dateFormatter) {
        _dateFormatter = [[NSDateFormatter alloc] init];
            NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            [_dateFormatter setLocale:enUSPOSIXLocale];
            [_dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];//毫秒是SSS,而非SSSSS
      }
      return _dateFormatter;
    }
    

    0x0E

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

    0x0F

    当你要实现相等性的时候记住这个约定:你需要同时实现 isEqualhash 方法。如果两个对象是被 isEqual 认为相等的,它们的 hash 方法需要返回一样的值。但是如果 hash 返回一样的值,并不能确保他们相等。

    0x10

    应该在 category 方法前加上自己的小写前缀以及下划线,比如 - (id)zoc_myCategoryMethod

    0x11

    当实现一个 protocol 应该坚持里氏替换原则。这个原则是:你应该可以取代任意接口(也就是 Objective-C里的的 "protocol")实现,而不用改变客户端或者相关实现。

    0x12

    当定义自己的 NSNotification 的时候应该把通知的名字定义为一个字符串常量,就像暴露给其他类的其他字符串常量一样。应该在公开的接口文件中将其声明为 extern 的, 并且在对应的实现文件里面定义。

    0x13

    用一个 Did/Will 这样的动词以及用 "Notifications" 后缀来命名这个通知也是一个好的实践。

    // Foo.h
    extern NSString * const ZOCFooDidBecomeBarNotification
    
    // Foo.m
    NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";
    

    0x14

    利用代码块,一个 GCC 非常模糊的特性,以及 Clang 也有的特性是,代码块如果在闭合的圆括号内的话,会返回最后语句的值

    NSURL *url = ({
        NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, endpoint];
        [NSURL URLWithString:urlString];
    });
    

    0x15

    这个特性非常适合组织小块的代码,通常是设置一个类。他给了读者一个重要的入口并且减少相关干扰,能让读者聚焦于关键的变量和函数中。此外,这个方法有一个优点,所有的变量都在代码块中,也就是只在代码块的区域中有效,这意味着可以减少对其他作用域的命名污染。

    0x16

    #pragma mark - 是一个在类内部组织代码并且帮助你分组方法实现的好办法,建议使用 #pragma mark - 来分离:

    0x17

    如果描述超过一行,应改用长字符串文档:

    • /** 开始
    • 换行写一句总结的话,以 ? 或者 ! 或者.结尾。
    • 空一行
    • 在与第一行对齐的位置开始写剩下的注释
    • 最后用 */ 结束。
    /**
     This comment serves to demonstrate the format of a docstring.
    
     Note that the summary line is always at most one line long, and
     after the opening block comment, and each line of text is preceded
     by a single space.
    */
    

    0x18

    一个函数必须有一个字符串文档,除非它符合下面的所有条件:

    • 非公开
    • 很短
    • 显而易见

    0x19

    字符串文档应该描述函数的调用符号和语义,而不是它如何实现。

    0x1A

    当需要的时候,注释应该用来解释特定的代码做了什么。所有的注释必须被持续维护或者干脆就删掉。

    0x1B

    一个类的文档应该只在 .h 文件里用 Doxygen/AppleDoc 的语法书写。 方法和属性都应该提供文档。

    0x1C

    使用 block 定义异步接口:

    - (void)downloadObjectsAtPath:(NSString *)path
                       completion:(void(^)(NSArray *objects, NSError *error))completion;
    

    0x1D

    当定义一个类似上面的接口的时候,尽量使用一个单独的 block 作为接口的最后一个参数。把需要提供的数据和错误信息整合到一个单独 block 中,比分别提供成功和失败的 block 要好。

    0x1E

    这里需要遵循以下两点:

    • objects 不为 nil,则 error 必须为 nil
    • objectsnil,则 error 必须不为 nil

    0x1F

    block一些注意点:

    • block 是在栈上创建的
    • block 可以复制到堆上
    • Block 会捕获栈上的变量(或指针),将其复制为自己私有的 const (变量)。
    • (如果在 Block 中修改 Block 块外的)栈上的变量和指针,那么这些变量和指针必须用 __block 关键字申明

    0x20

    当使用代码块和异步分发的时候,要注意避免引用循环。

    __weak __typeof(self)weakSelf = self;
    [self executeBlock:^(NSData *data, NSError *error) {
        __strong __typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf doSomethingWithData:data];
            [strongSelf doSomethingWithData:data];
        }
    }];
    

    0x21

    为了分离概念,应该这样做:

    • 委托模式(delegate pattern):事件发生的时候,委托者需要通知代理者。
    • 数据源模式(datasource pattern): 委托者需要从数据源对象拉取数据。

    0x22

    代理方法必须以调用者(即委托者)作为第一个参数

    0x23

    Aspect Oriented Programming (AOP,面向切面编程) 在 Objective-C 社区内没有那么有名,但是 AOP 在运行时可以有巨大威力。
    Objective-C 的世界里,这意味着使用运行时的特性来为指定的方法追加 切面 。切面所附加的行为可以是这样的:

    • 在类的特定方法调用前运行特定的代码
    • 在类的特定方法调用后运行特定的代码
    • 增加代码来替代原来的类的方法的实现

    相关文章

      网友评论

          本文标题:《禅与 Objective-C 编程艺术》阅读笔记

          本文链接:https://www.haomeiwen.com/subject/huoxvftx.html