读《OC2.0编写高质量iOS代码的52个方法》记录

一 熟悉OC 对象 消息 运行时

1.OC消息结构语言,其运行时所应执行的代码由运行环境决定; 动态性语言。
OC语言的对象所占的内存总是分配在堆空间,绝不会分配在栈空间。 指针的大小:32位4字节,64位机8字节。

2.在类的头文件里尽量少引用其他头文件。 可以使用@class向前声明类的方法。 这可以相应的减少编译时间。
注意:类遵循某个协议,就必须引用完整,不能使用向前声明方法。 (因此也就出现了:1.要么将协议移到class-continuation里;2.如果不行,就把协议单独放到一个头文件里,然后引入。)

3.多使用字面量语法,少使用与之等价的方法
NSArray *array = [@"name", @"title"]; 而不要使用 NSArray *array = [NSArray arrayWithObjects:@"name",@"title",nil]; NSDictionary 同理。
这样子创建,就可以通过去下标的方式取对应的元素了。方便。

4.多用类型常量,少用#define 宏预处理
可以参考官方通知名称的写法。

5.用枚举表示状态、选项、状态码
首先枚举名字直接易懂。 枚举分 NS_ENUM 和 NS_OPTIONS。 具体使用:需要以按位或操作组合的都用NS_OPTIONS,不用互相组合的用NS_ENUM。
另外枚举使用switch的时候,不要加default。 这样子,新增枚举值,编译器就会给出警告信息。

6.属性 @property 自动生成set get方法。
ABI: Application Binary Interface 应用程序二进制接口。
assign: 只会执行“纯量类型(CGFloat, NSInteger)”的简单赋值操作。
strong: 定义一种拥有关系。 先保留新值,并释放旧值,然后再将新值设置上去。
copy:设置方法并不保留新值,而是将其拷贝。
weak:定义了一种“非拥有关系”。 设置方法既不保留新值,也不释放旧值。 对象摧毁时,属性也会清空nil。
unsafe_unretained: 和assign 相同,它适用于对象类型。 也是“非拥有关系”。 但是对象摧毁,不会置空nil。 这和weak的区别。

atomic:使用同步锁开销较大,会带来严重的性能问题。

7.在对象内部尽量直接访问实例变量
读取实例变量的时候才用直接访问的形式,而在设置实例变量的时候通过属性来做。
懒加载必须通过“获取方法”来访问属性。

8.对象等同性
==操作符比较的两个对象的指针本身,不是其所指的对象。 isEqual:方法判断两个对象的等同性。
如果isEqual判断两个对象相等,那么其hash方法也必返回同一个值。反之,不一定。

9.以类族的方式隐藏实现细节
参考UIButton的定义方法。 只需传入Button类型,不用管内部具体实现。系统框架方法里非常常见。

10.既有类中使用关联对象存放自定义数据
objc_setAssociatedObject()方法。 只有其他方法不行时才使用,因为这个方法通常会引入难于查找的bug。

11.理解objc_msgSend方法
在底层,所有的方法都是普通的C语言函数,然而对象收到消息之后,究竟该调用哪个方法则完全在运行期决定,甚至可以在程序运行时改变,这就是OC是一门真正的动态性语言。

12.理解消息转发机制
第一阶段:动态方法解析 +(BOOL) resloveInstanceMethod:(SEL)selector 在此方法里,你可以插入一个已经实现的方法。
第二阶段:完整的消息转发机制。网上有一个经典的处理流程图。 越往后处理的代价越大。

13.方法调配技术调试黑盒方法
method swizzling 方法交换。 这种方法不要乱用,否则出bug很难定位。

14.理解类对象的用意
类对象底层是Struct。 这个要掌握看看。 isa、 super_class、 *name、 version、info、instance_size.等等
isMemberOfClass:判断对象是否为某个特定类的实例。 isKindOfClass:对象是否为某个类或其派生类的实例。

第三大部分 API

15.加前缀避免命名空间冲突
注意:两字母大写前缀Apple爸爸宣称它保留使用的权利,所以我们最好使用三字母前缀。

16.提供全能初始化方法
所有对象均要初始化。 如果有多种初始化方法,搞一个全能初始化方法,所有初始化方法都调用它。

17.实现description方法
实现description方法,返回一个更有意义的字符串,用于描述该实例。 若想在调试的时候打印更详尽的对象信息,应该实现debugDescription方法。

18.尽量使用不可变对象 readonly
如果这个属性不想外部改变,就用readonly修饰。 如果外部只想调用某个属性, 可以在.h文件中修饰readonly,在.m文件里修饰为readwrite。

19.使用清晰而协调的命名方式
方法与变量使用驼峰式大小写命名方式。 方法命名不要吝啬使用长的方法名,定义的方法应该直接就可以看出其语义作用。

20.为私有方法加前缀
这样可以很好的共有方法区别开来。 不过注意:一个下划线方式(_)不要使用,Apple爸爸说这个要给它预留的。

21.Objective-C的错误类型
OC里的异常,只应该用于及其严重的错误(fatal error),不可以用其处理一般错误类型。
在错误不那么严重的情况下,可以使用代理方式来处理、传递错误。
当NSError当参数传递错误时,实际上传递的是一个指针。 (NSError )error。 指针本身又指向一个指针,那个指针指向NSError对象。 在ARC下NSError 转化为 NSError__autoreleasing 的。 即:指针所指的对象会在方法执行完毕后自动释放。 这个队形也必须自动释放,因为处理错误的方法对象不能保证可以把方法中创建的NSError释放掉,所以必须autorelease。

22.NSCopying 协议
如果想让某个类支持copy操作,那就要实现NSCopying协议,该协议只有一个方法:

  • (id)copyWithZone:(NSZone *)zone; 注意是zone 不是 copy方法。
    可变对象实现mutableCopyWithZone方法。 NSMutableCopying协议。
    注意:可变对象使用copy,返回一个不可变的实例。
    深拷贝:在拷贝对象自身时,将其底层数据也一并复制过去。 Foundation框架里所有的collection类,在默认情况下都是浅拷贝。只拷贝对象本身,而不复制器内容。

四 协议和分类

23.通过委托与数据源协议进行对象间的通信Delegate
委托模式为对象提供了一套接口。

24.将类的实现代码分散到便于管理的数个分类之中category
利用分类机制把类的实现划分为易于管理的小模块。 私用方法归入private分类中

25.总是要为第三方分类添加名称前缀
不加前缀容易覆盖原来的方法,一旦覆盖,以最后加载的一个分类为准。

26.不要在分类中声明属性
虽然说可以通过objc_setAssociatedObject: 方法添加,但是不推荐。分类目标在于扩展类的功能,而非封装数据。
把封装数据所用的全部属性都定义在主接口里。

27.使用class-continuation分类 隐藏实现细节
将就在本类使用的属性写在class-continuation分类。如果只需外部访问的属性,可以在.h里声明成readonly。
.m文件里的@interface class {} @end

28.通过协议提供匿名对象
Id<Delegate>. ID类型, 相当于匿名。。

五 内存管理

29.理解引用计数
保留计数不能说一定是某个值,应该说你执行的操作,是递增了还是递减了该计数。
XXX = nil; 悬挂指针,防止出现指向无效对象的指针,从而造成可能的crash。
autorelease:释放操作在下一次事件循环时才回收。 因此它可以延长对象的生命周期,使其跨越方法边界依然可以存活一段时间。
ARC下:不可以调用:retain release autorelease dealloc 方法

30.以ARC简化引用计数
内存泄漏:没有正确释放已经不再使用的内存。
非OC对象,比如CoreFoundation中的对象,或是有malloc()分配在堆中的内存,要手动清理。 CFRelease。

31.在dealloc方法中只释放引用并解除监听
通知一旦发给了已经回收了的对象,必然crash。
dealloc方法里面不应该调用任何其他方法,因为此时对象已经接近尾声了。

32.编写“处理异常代码”时要注意内存管理问题
在OC 中应该程序必须因异常状况而终止时才应该抛出异常。fatal error(参见第21条)。 程序即将终止,内存可以不处理。
但是如果自己非要处理异常,一定要注意try 方法内清理好对象内存。 -fobjc-arc-exceptions 在ARC下大可以捕获异常标志。 但是这样运行效率会变低。

33.用弱引用避免保留环
weak 与 unsafe_unretained 作用完全一致,然而weak修饰的属性,只要系统回收了,会自动设为nil。

34.用autoreleasepool 降低内存峰值
GCD、数组block遍历等线程都默认创建了自动释放池。每次执行“事件循环”时,就会将其清空。
autoreleasepool 可以多层嵌套, 它的作用范围就在其大括号{}之间。。 如果内存峰值不是很大,尽量不要额外建立自动释放池。

35.用“僵尸对象”调试内存管理问题 Zoombie Objects
向已经回收的对象发送消息是不安全的。 不是必crash的。 这取决于:对象所占的内存有没有被其它内容所复写。。
系统会修改对象的ISA指针,令其指向特殊的僵尸类,从而使其变成僵尸对象。NSZombie 也是一个根类,和NSObject一样。
僵尸类可以响应任何选择子,响应方式:打印一套消息内容及接收者信息,并终止程序。

36.不要使用retainCount
因为retainCount返回的是某个节点的计数值,并不能考虑到后续系统可能会清掉、释放操作。 所以它是不能准确反映计数值得。

六 块与大中枢派发

37.理解块这一概念
Block:带有自动变量(局部变量)的匿名函数。 查看Block源码,它也有一个isa指针,所以实质上block就是OC对象。
Block根据存储区域分为三种: _NSConcreteStackBlock _NSConcreteMallocBlock _NSConcreteGlobalBlock 栈、堆、程序数据(静态)Block

38.通常用typedef创建块
方便写,也方便阅读。 block表达式: ^返回值类型 参数列表 表达式

39.使用handler块降低代码分散程度
一般获取网络数据用块处理很方便,因为在块定义范围内可以直接获取块内的所有变量。

40.使用块引用其所属对象时不要出现保留环
41.多用派发队列,少用同步锁
@synchronized(self){
//会降低代码效率,公用不用一个锁的同步块,都必须按顺序执行
}
使用同步队列及栅栏块,可以令同步行为更加高效。

42.多用GCD,少用performSelector系列方法
performSelector系列方法在内存管理方面容易有疏失。它无法确定将要执行的选择子具体是什么,因为ARC编译器也无法适当的插入内存管理方法。

43.掌握GCD及操作队列的使用时机
在执行后台任务时,GCD并不一定是最佳方式。 还有一个NSOperationQueue。 SD库就是用的这方式。
它的好处: 可以取消某个操作, 但是已经启动的不能取消。 可以指定操作间的依赖关系。可以指定优先级。可以通过兼职观测机制监控NSOperation对象属性。

44.dispatch_group
注意一对函数。 dispatch_group_enter dispatch_group_leave 必然成对出现。 这个主要用于函数回调完了 在执行notify方法。

45.使用dispatch_once来执行只需运行一次的线程安全代码
dispatch_once简化代码并能彻底保证线程安全。 更高效

46.不要使用dispatch_get_current_queue
此函数已经废除,最多在调试的时候使用

七 系统框架

47.熟悉系统框架
框架:将一系列代码封装成动态库,并在其中放入描述其接口的头文件, 这样做出来的东西就是框架。
48.多用枚举块,少用for循环
for in 和 block遍历方式。 block遍历方式可以根据options 反向遍历。
49.对自定义其内存管理语义的collection使用无缝桥接
__bridge
50.构建缓存是使用NSCache而非NSdictionary。
NSCache会在当系统资源耗尽时,它可以自动删除缓存。 先删除最久没使用的对象。 并且它也是线程安全的。
51.精简initialize和load的实现代码
如果分类和类都定义了load方法,先调用类的,在调用分类的。 整个应用程序在执行load时都会阻塞,所以load方法要精简。
initialize 方法是懒加载的。只有当程序用到了相关的类,才会调用。且只调用一次。 它也只应该设置类内部数据,不应该调用其它方法。
52.NSTimer会保留其目标对象
如果你的应用在iOS10之后,直接使用block方式创建就可以避免对象不释放这个问题。 也可以使用NSWeakTimer三方库解决。

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