《Effective Objective-C 2.0:编写高质量iOS与OS X代码的52个有效方法》阅读笔记

前言:第一次阅读此书大概是一年半之前,在网上找到电子版,也就临时看了一晚上,之后就没有再涉猎。八月份决定抽出半个月左右的时间认真阅读下这本书(但是由于这两周一直在准备公司项目的新版本,实际只读完了前五章)。读后受益匪浅,今天抽空把之前做的笔记整理一部分出来,做个记录与分享。


  • 对象所占内存总是分配在堆空间(heap space)中,而绝不会分配在栈(stack)上,不能在栈中分配OC对象;

  • 有时会遇到定义里不含*的变量,他们可能会使用栈空间(stack space)。这些变量所保存的不是OC对象,比如CGRect,是一个结构体;

  • 创建对象需要额外开销,例如分配和释放内存;

  • 如果在一个类文件中,只需要知道另一类的类名,而不需要用到该类接口的情况下,尽量使用前置声明@class而不是#import“”引入头文件,将引入头文件的时机尽量延后,这样可以减少编译时间,也可以避免循环引用,导致两个类中有一个类无法被编译。

  • 多用字面量语法
    1、缩减代码长度,简便整洁
    2、数组和字典使用字面量语法,当集合内对象为空时,会抛出异常,这样会更安全

  • 多用类型常量,少用#define预处理指令
    1、 预处理指令定义的常量没有类型信息
    2、尽量在实现文件而不要在声明文件中声明预处理指令

  • 凡是需要以按位或操作来组合的枚举都应该使用NS_OPTIONS定义,不需要互相组合的枚举,应使用NS_ENUM来定义,这两个宏具备后向兼容能力都是用#define预处理指令来定义的。
    原因:在C++的编译模式下,认为按位运算的运算结果的数据类型应该是枚举的底层数据类型,而且C++不允许将这个底层类型“隐式转换”为枚举类型本身。如果使用NS_ENUM宏来定义组合枚举,则有可能会报错

  • 在switch语句中,若用枚举来定义状态机,最好不要有default分支。这样的话,如果之后又加了一种状态,编译器就会发出警告,提示新加入的状态并未在switch语句中进行处理,可以保证代码的准确性。否则就会自动进入default分支进行处理,不会发出警告。

  • 在iOS开发中,几乎所有属性都声明为nonatomic,这样做的历史原因是:在iOS开发中使用同步锁开销较大,会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”,若要实现线程安全的操作,还需要采用更为深层的锁定机制才行。

  • “collection”与“set”在中文里都叫做“集合”,前者是Array、Dictionary、set等数据结构的总称。

  • 对象等同性
    “==”操作符比较的是两个指针本身,而不是指针所指的对象,准确率较低;
    “isEqualToString”比“isEqual”执行速度快,后者还要进行其他操作;
    基本数据类型判断等同性使用:“==”
    判断等同性优先使用:“isEqualToString/Array/Dictionary” > "isEqual" > "=="

  • 学会使用类族模式
    系统框架中有许多类族,特别是collection类,如数组,字典等。使用“类族模式”的主要思想是通过继承,隐藏基类的内部实现细节,开放公共接口,方便调用,便于维护;
    例如在项目工程中为所有包含列表视图的viewController类创建父类ListViewController,通过继承来统一接口,统一管理,简化代码;
    在子类越来越多时,类族的优势会越来越明显,开发人员无需关心其他问题,只需要在基类的实现方法中增加该子类的初始化接口即可。而各种属性定义,方法实现从基类的公共接口处调用;
  • 关联对象
    设置关联对象:objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)
    获取相应的关联对象: objc_getAssociatedObject(<#id object#>, <#const void *key#>)
    删除所有的关联对象:objc_removeAssociatedObjects(<#id object#>)
    注:由于设置关联对象时用的key是“不透明指针”,就是不局限于某种特定类型的指针,若想使两个键匹配到同一个值,必须是完全相同的指针,所以在设置静态全局变量时,通常使用静态全局变量做key,也可以使用选择器等,保证指针相等性。
    一般只有在其他办法行不通的情况下才会使用关联对象,因为滥用关联对象可能会导致“保留环”,使代码失控,程序崩溃

  • C语言使用“静态绑定”,在编译期就能决定在运行时所应调用的函数
    在OC中,如果向某对象传递消息,就会使用动态绑定机制来决定需要调用的方法
    向某一对象发送消息,编译器看到此消息后,会将其转换为一条标准的C语言函数调用,所调用的函数为消息传递的核心函数:
    objc_msgSend();
    iOS 8.0之后调用方式:((void (*)(id,SEL,id、、、))objc_msgSend)(self,@selector(selector),object1、、、);
    objc_msgSend函数执行方法调用的顺序:
    在接受者所在类的方法列表中,查找与选择器名称相同的方法,进行调用;
    查找未果--》向接受者所属类的父类递进查找;
    查找未果--》进行消息转发
    在匹配成功后,objc_msgSend会将匹配结果缓存在“快速映射表”中,再次调用时速度会大大加快

  • 用方法调配技术互换两个方法的实现:
    Method method1 = class_getInstanceMethod([self class], @selector(testMethodWithInfo:));
    Method method2 = class_getInstanceMethod([self class], @selector(test));
    method_exchangeImplementations(method1, method2);
    系统在调用method1时就会执行method2,调用method2时调用method1.
    通过这一方案,可以为那些“完全不知道其具体实现的”黑盒方法添加日志记录功能,非常有助于程序调试,单一般也只在调试程序时用到这一方案。

  • Apple宣称其保留所有“两字母前缀”的权利,所以为了避免出现“重名符号错误”,在应用程序中声明类名时都应该添加“三字母前缀”,如所在公司是阿里,想要自定义一个PayView类,则应给其命名为ALiPayView。

  • 给私有方法的名称加上前缀,例如p_(p代表private),使其与公共方法区分开;
    不要单用一个下划线做前缀,这种方式是苹果公司预留方式,容易产生冲突;

  • 浅拷贝之后的内容与原始内容均指向相同对象,而深拷贝之后的内容所指的对象是原始内容中相关对象的一份拷贝。即浅拷贝后改变拷贝对象的值,原始内容也会随之改变,但是深拷贝后改变拷贝对象的值,原始内容不会发生改变。

  • 无论当前实例是否可变,若需获取其可变版本的拷贝,则使用mutableCopy,若需获取其不可变版本的拷贝,则使用copy
    对于不可变的NSArray与可变的NSMutableArray来说,下列关系总成立:
    [NSMutableArray copy] =>NSArray
    [NSArray mutableCopy] =>NSMutableArray

  • 当类的实现中方法过多,导致代码冗余时,可以通过“分类”的方法,将类中的方法按需分类,划入几个分区中,便于管理与维护。

  • 在编写程序库用作分享时,如果有一些方法,它们不是公共API的一部分,然而却很适合在程序库之内使用,这时我们应该创建private分类,当程序库的某些地方需要用到这些方法时,就导入此分类头文件,并且设置头文件不随程序库一并公开即可。

  • 向第三方类(公共API,如NSString)添加分类时:
    1、总应给其名称加上你专用的前缀,避免类名冲突;
    2、总应给其中的方法名加上你专用的前缀,避免方法重写覆盖;
    注:如果在项目中给第三方类创建了多个分类,并且都声明并实现了方法名相同的方法,则在程序中调用该方法时,实现的是最后一个创建的分类中的方法。
    3、在分类中不要定义属性

  • 保留环(循环引用):当两个或两个以上对象呈环状相互引用时,因为循环中的对象其引用计数不会降为0,所以内存无法释放,会导致内存泄漏情况。

  • 可用下列修饰符来改变局部变量与实例变量的语义:
    __strong: 默认语义,保留此值。
    __unsafe_unretained: 不保留此值,这么做可能不安全,因为等到再次使用变量时,其对象可能已经被收回了。
    __weak: 不保留此值,但是变量可以安全使用,因为如果系统把这个对象回收了,那么变量也会自动清空。
    __autoreleasing: 把对象“按引用传递”给方法时,使用这个特殊的修饰符,此值在方法返回时自动释放。

所以在程序调用block语法时,因为block会自动保留其所捕获的全部对象,而这其中如果有某个对象又保留了block块本身(如self),就可能会导致循环引用(保留环)。而使用__weak来修饰self时,weakSelf的引用计数会自动清空,就不会导致保留环的产生。

  • ARC只负责管理oc对象的内存,要注意的是:CoreFoundation对象不归ARC管理,开发者必须适时调用CFRetain/CFRelease。

  • 给对象配置过的观测行为都应该在dealloc中注销,ARC会自动执行父类的dealloc方法,所以在ARC下实现dealloc方法时,不需要手动调用[super dealloc]。

  • 系统在回收对象时,可以不将其真的回收,而是把它转化为僵尸对象。通过环境变量NSZombieEnable可开启此功能。

  • 系统会修改对象的isa指针,令其指向特殊的僵尸类,从而使对象变为僵尸对象。僵尸类能够响应所有的选择器,响应方式为:打印一条包含消息内容及其接收者的消息,然后终止应用程序。

你可能感兴趣的:(《Effective Objective-C 2.0:编写高质量iOS与OS X代码的52个有效方法》阅读笔记)