《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指针,令其指向特殊的僵尸类,从而使对象变为僵尸对象。僵尸类能够响应所有的选择器,响应方式为:打印一条包含消息内容及其接收者的消息,然后终止应用程序。

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

推荐阅读更多精彩内容