Effective Objective-C 2.0

第一章 熟悉 Objective-C

1、OC起源

2、在类的头文件中尽量少引入其他头文件(可用@class)

3、多用字面量语法,少用与之等价的方法

4、多用类型常量,少用#define预处理指令

5、使用枚举表示状态、选项、状态码

第二章 对象、消息、运行时

6、理解“属性”这个概念

7、在对象内部尽量直接访问实例变量 

        1.在对象内部读取数据直接访问实例变量,写入数据使用属性;

        2.在初始化及dealloc方法中使用实例变量;

        3.用到了懒加载时,一定需要通过属性读取数据

8、理解“对象等同性”这一概念

        1.若想检测对象的等同性,请提供“isEqual”和hash方法;

        2.相同对象具有相同的hash码,但两个hash码相同的对象不一定相同;

        3.不要盲目地逐个检测每一个属性,而是应该按照具体需求制定检测方案;

        4.编写hash方法时,应该使用计算速度快而且hash码碰撞几率低的算法)

9、以“类族模式”隐藏实现细节(工厂模式就是创建类族的办法之一)

        1.类族模式可以把实现细节隐藏在一套简单的公共接口后面

        2.系统卡框架中间经常使用类族

        3.从类族的公共抽象基类中继承子类时要当心

10、在既有类中使用关联对象存放自定义数据

        1.可以通过“关联对象”机制吧两个对象关联起来

        2.定义关联对象时可指定内存管理语义,用以模拟定义属性时所采用的“拥有关系”与“非拥有关系”

        3.只有在其他做法不可行时才应选用关联对象,因为这种做法通常会引入难以查找的bug

11、理解objc_messageSend的作用

12、理解消息转发机制

13、用“方法调配技术”调试“黑盒方法”(method swizzling,方法交换,动态添加方法)

14、理解“类对象”的用意

        1.每个实例都有一个指向Class对象的指针,用于表明其类型,而这些Class对象则构成了类的继承体系

        2.如果对象类型无法在编译期确定,那么就应该使用类型信息查询方法来探知

        3.尽量使用类型洗信息查询方法来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发功能

第三章 接口与API设计

15、用前缀避免命名空间冲突

        1.选择与自己公司、应用程序或者二者有关联值名称作为类名的前缀,并在所有代码中均使用这一前缀

        2.若自己所开发的程序中用到了第三方库,则应该为其中的名称加上前缀

16、提供“全能初始化方法”

        1.在类中提供一个全能初始化方法,并在文档中指明,其他初始化方法都应该调用此初始化方法

        2.若全能初始化方法与父类不同,则要覆写父类中的对应方法

        3.如果父类的初始化方法不适用于子类,那么应该覆写这个父类方法,并在其中抛出异常

17、实现description方法

        1.实现description方法返回一个有意义的字符串,用以描述该实例

        2.若想在调试时打印更详细的对象描述信息,则应该实现debugDescripition

18、尽量使用不可变对象

        1.尽量创建不可变对象

        2.若某属性仅可用于内部修改,则在“class-continuation分类”中将其由readonly属性扩展为readwrite属性

        3.不要把可变的collerction作为公开属性,而应该提供相关方法,以此修改对象中的collection

19、使用清晰而协调的命名方式

20、为私有方法名加前缀

        1.给私有方法名称加上前缀,这样可以很容易地将其同公共方法区分开

        2.不要用单个下划线做私有方法的前缀,因为这种做法是预留给苹果公司的

21、理解Objective-C错误模型

        1.只有发生了可使整个应用程序崩溃的严重错误时,才应使用异常

        2.在错误不是那么严重的情况下,可以指派“委托方法(delegate method)”来处理错误,也可以把错误信息放在NSError对象里面,经由“输出参数”返回给调用者

22、理解NSCopying协议

        1.若想要自定义的对象具有拷贝功能,则需要实现NSCopying协议

        2.如果自定义对象分为可变版本和不可变版本,那么就要同时实现NSCopying和NSMutableCopying协议

        3.复制对象时需要决定采用深拷贝还是浅拷贝,一般情况下应该尽量执行浅拷贝

        4.如果你所写的对象需要深拷贝,那么可以考虑新增加一个专门执行深拷贝的方法

第四章 协议与分类

23、通过委托与数据源协议进行对象间通信

24、将类的实现代码分散到便于关凯的数个分类中

25、总是为第三方类的分类名称加上前缀

        1.向第三方类中添加分类时,总应该为其名称上加上你专用的前缀

        2.向第三方类中添加分类时,总应该为其中的方法名上加上你专用的前缀

26、不要在分类中声明属性

        1.把封装数据所用的全部属性都定义在主接口里面

        2.在"class-continuation分类"之外的其他分类中,可以定义存取方法,但尽量不要定义属性

27、使用"class-continuation分类"隐藏实现细节

        1.通过"class-continuation分类"向类中新增实例变量

        2.如果某属性在主接口中声明为只读,而类的内部又需要用设置方法修改这个属性,那么就在"class-continuation分类"中将其扩展为"可读可写"

        3.把私有方法的原型声明在"class-continuation分类"里面

        4.若想使类所遵守的协议不为人知,则可在"class-continuation分类"中声明

28、通过协议提供匿名对象

第五章 内存管理

29、理解引用计数

        1.引用计数机制通过可以递增递减的计数器来管理内存。对象创建好之后,其保留指数至少为1.若保留计数为正,则对象继续存活。当保留计数器降为0时,对象被销毁

        2.在对象的生命周期中,其余对象通过引用老保留或释放此对象。保留与释放操作分别会递增递减保留计数

30、以ARC简化引用计数

        1.在ARC之后,我们无须担心内存管理问题。使用ARC编程,可省去勒种许多“样板代码”

        2.ARC管理对象生命周期基本就是:在合适的地方插入“保留”和“释放“操作。在ARC环境下,变量的内存管理语义可通过修饰符指明,而原来则需要手工执行”保留“和”释放“操作

        3.由方法所返回的独享,其内存管理语义总是通过方法名来体现。ARC将此确定为开发者必须遵守的规则

        4.ARC值负责管理OV对象的内存。尤其注意CoreFoundation对象不归ARC管理,开发者必须在适时调用CFReatain/CFRelease

31、在dealloc方法中释放引用并解除监听

32、编写”异常安全代码“时留意内存管理

        1.捕获异常时,一定要将try块neural所创建的对象清理干净

        2.在默认情况下,ARC不生成安全处理异常所需的清理代码。开启编译器标志(-fobjc-arc-exception)后,可生成这中代码,不过会导致应用程序变大,而且降低运行效率

33、以弱引用避免保留环

        1.将某些引用设为weak,可避免出现”保留环“

        2.weak引用可以自动清空,也可以不自动清空。自动清空是随着ARC而引入的新特性,有运行系统实现。在具备自动清空功能的弱引用上,可以随意读取数据,而且这种引用不会指向已经回收过的对象

34、以”自动释放池块”降低内存峰值

        1.自动释放池排布在栈中,对象收到antorelease消息后,系统将其放入最顶端的池内

        2.合理运用自动释放池,可降低应用程序的内存峰值

        3.@autoreleasepool这种新式写法能创建出更为轻便的自动释放池

35、用”僵尸对象“调试内存管理问题

36、不要使用retainCount

        1.对象的保留计数看似有用,实则不然,因为任何给定时间上的“绝对保留计数”都无法反应对象生命周期的全貌

        2.引入ARC之后,retainCount方法就正式废止了,在ARC下调用该方法会导致编译器报错

第六章 块与大中枢派发

37、理解“块”这一概念 

        1.块是C、C++、OC中的词法闭包

        2.块可接受参数,也可返回值

        3.块可以分配在栈或堆上,也可以是全局的。分配在栈上的块可以拷贝到堆,这样的话就和标准的OC对象一样,具备引用计数了

38、为常用的块类型创建typedef

        1.以taypedef重新定义块类型,可以令块变量用起来更简单

        2.定义新类型时应遵从现有的命名习惯,勿使其名称与别的类型相冲突

        3.不妨为同一个块签名定义多个类型别名。如果要重构的代码使用了块类型的某个别名,那么只需要修改相应typedef中块的签名即可,无需改动其他typedef

39、使用handler块降低代码分散程度

        1.在创建对象时,可以使用内联的handler块将相关业务逻辑一并声明

        2.在有多个实例需要监控时,如果采用delegate模式,那么经常需要根据传入的对象来切换,而若使用handler块来实现,则可直接将块与内联对象放在一起

         3.设计API时,如果用到了handler块,那么可以增加一个参数,使调用者可以通过此参数来决定应该吧块安排在哪个队列上执行

40、用块引用其所属对象时不要出现保留换(block是self的属性,在block内部不能直接引用self,而要打破循环)

        1.如果块所捕获的对象直接或者间接地保留了块本身,那么就得当心保留环问题

        2.一定要找到适当的时机解除保留环,而不能把责任推给API的调用者

41、多用派发队列,少用同步锁

        1.派发队列可用来表述同步语义(synchronization semantic),这种做法要比使用@synchronized块或者NSLock对象更简单

        2.将同步与异步派发结合起来,可以实现与普通加锁机制一样的同步行为,而这样做不会阻塞执行异步派发的线程

        3.使用同步队列以及栅栏块,可以令同步行为更加高效

42、多用GCD,少用performSelector系列方法

        1.performSelector系列方法在内存管理上容易有疏失。它无法确定将要执行烦人选择子是什么,因而ARC编译器就无法插入适当的内存管理方法

        2.performSelector系列方法能处理的选择子太过局限了,选择子的返回值类型以及发送给方法的参数个数都受到限制

        3.如果想把任务放到另外一个线程上执行,那么最好不要使用performSelector系列方法,而是应该吧任务封装到块里,然后调用大中枢派发机制的相关方法来实现。

43、掌握GCD及操作队列的使用时机

        1.在解决多线程与任务管理问题时,派发队列并非唯一方案

        2.操作队列提供了一套高层的Objective-C API,能实现纯GCD所具备的绝大部分功能,而且还能完成一些更为复杂的操作,哪些操作若改用GCD来实现,则需另外编写代码

        3.操作对列的优势有:取消某个操作、指定操作的依赖关系、通过KVO监控NSOPeration对象的属性、指定操作的优先级、重用NSOperation对象

44、通过Dispatch Group机制,根据系统资源状况来执行任务

        1.一 系列任务可归入一个dispatch group之中。开发者可以在这组任务执行完毕时获得通知

        2.通过dispatch group,可以在并发式派发队列中执行多项任务。此时GCD会根据系统资源状况来调度这些并发执行的任务。开发者若自己来实现此功能,则需编写大量代码

45、使用dispatch_once来执行只需要运行一次的线程安全代码

46、不要使用dispatch_get_current_queue

第七章 统框架

47、熟悉系统框架

        1.许多系统框架都可以直接使用。其中最重要的还Foundation和CoreFoundation,这两个框架提供了构建应用程序所需的许多核心功能

        2.很多常见任务都能用框架来做,比如音频与视频、网络通信、数据管理等

        3.请记住:用纯C写成的框架与用Objective-C写成一样重要,若想成为优秀的Objective-C开发者,应该掌握C语言的核心概念

48、多用块枚举,少用for循环

49、对自定义其内存管理语义的collection使用无缝桥接

50、构建缓存时选用NSCache而非NSDictionary

        1.实现缓存时应选用NSCache而非NSDictionary对象,因为NSCache可以优雅地提供自动删减功能,而且是“线程安全”的,此外,它与字典不同,并不会拷贝键

        2.可以给NSCache对象设置上限,用以限制缓存中对象总个数以及“总成本”,而这些尺度则定义了缓存删减其中对象的时机。但是绝对不要把这些尺度当成可靠的”硬限制“,他们仅仅对NSCache起指导作用

        3.将NSPurgeableData与NSCache搭配使用,可实现自动清除数据功能,也就是说,当NSPurgeableData对象所占内存为系统丢弃时,该对象自身也会从缓存中移除

        4.如果缓存使用得当,那么应用程序的响应速度就能提高。只有“重新计算起来很费事的”数据,才值得放入缓存,比如那些需要从网络获取或者裁判读取的数据

51、精简initialize 与 load的实现代码

        1.在加载阶段,如果实现了load方法,那么系统就会调用它。分类里面也可以定义此方    法,类的load方法要比分类中的先调用。与其他方法不同,load方法不参与覆写机制

        2.首次使用某个类之前,系统会向其发送initialize消息。由于此方法遵从普通的覆写规则,所以通常应该在里面判断当前要初始化的是那个类

        3.load 与 initialize方法都应该实现得精简一些,这有助于保存应用程序的相应能力,也能减少引入“依赖环”的几率

        4.无法在编译期设定的全局常量,可以放在initialize方法中初始化

52、别忘了NSTimer会保留其目标对象

你可能感兴趣的:(Effective Objective-C 2.0)