《禅与Objective-c编程编程艺术》笔记
推荐dealloc方法放在文件最开始,在init方法之前,形成配对
OC的两步创建:alloc & init
永远不要在init方法中调用getter 和 setter方法
在init中使用setter方法不会很好的执行UIAppearence
alloc负责创建对象,分配足够的内存来保存对象
The isa instance variable of the new instance is initialized to a data structure that describes the class; (写入isa指针,初始化数据结构内存)
memory for all other instance variables is set to 0。(重置引用计数,实例变量)
swift中没有alloc方法
init初始化对象,表明对象处于可使用状态(对象的实例变量正常初始化,被赋合理有用的值)
An object isn’t ready to be used until it has been initialized.正常初始化
The init method defined in the NSObject class does no initialization; it simply returns self.
指定初始化方法 & 间接初始化方法
instanceype使用技巧
关联返回类型
只能使用在init方法中
只能作为返回对象(id可以作为参数)
使用id返回对象后,对象类型为id,而instanceype可以关联对象对垒,对象返回结果为实际的对象类型(如NSArray)
优点
编译器优化,更好的避免一些代码书写问题,有利于开发者在编码阶段就发现错误
编译器正确的检查类型
类簇 Class Cluster
定义:
an architecture that groups a number of private concrete subclasses under a public abstract superclass.
一个在公有的抽象超类下设置一组私有子类的结构
思想:
使用信息进行(类的)初始化处理期间,会使用一个抽象类(通常作为初始化方法的参数或者判定环境的可用性参数)来完成特定的逻辑或者实例化一个具体的子类
例子:UIViewController 在iPad和iPhone上有不同的行为
self = nil 目的是为了解除该类的实例中所有引用,实例(抽象类的实例)本身将会解除分配(发生在Main Runloop这一次结束时)
单例
请可能的避免使用单例,而是依赖注入
虽然单例对象可以子类化,但这种方式有用的情况非常少见
属性
应使用setter & getter访问属性,除了init和dealloc方法
原因:
使用setter会遵循定义的内存管理语句(strong,weak,copy),这个在ARC之前就是相关内容
KVO通知(willChangeValueForKey: , didChangeValueForKey:)会被自动执行
更容易DEBUG
允许在一个单独的地方为设置值添加额外的逻辑
更倾向于使用getter
对未来的变化有拓展能力
允许子类化
更简单的DEBUG
让意图更清晰和明确
自动产生KVO通知
在消息发送的时候添加的开销是微不足道的
属性可以存储一个代码块block,为了让他存货在定义块的结束,必须使用copy,block最早在栈里面创建,使用copy让block拷贝到堆里面
可变对象(NSArray NSString NSURLRequest等)属性的内存管理类型必须是copy的
懒加载
缺点
getter方法应该避免副作用
在第一次访问的时候改变了初始化的消耗,产生了副作用,让优化性能和调试变得困难
这个初始化可能是不确定的
这个行为是KVO不友好的
方法
参数断言
NSParameterAssert()
断言条件是否成立或是抛出一个异常
私有方法
永远不要在自定义的私有方法前加_
原因:_前缀是Apple保留的,不要冒重载苹果的私有方法的风险
相等性
当你需要实现相等性的时候记住这个约定:需要同时实现isEqual和hash方法
isEqual相等,hash一定相等
hash相等,isEqual不一定相等
这个约定是因为当被存储在集合(NSDictionary NSSet在底层使用hash表数据的数据结构)的时候,如何查找这些对象
Categories
我们应该要在我们的category方法前加上自己的小写前缀一记下划线
(id)tc_myCatrogyMethod
Protocols
默认情况下所有的方法定义都是required
NSNotification
通知名字需定义为一个字符串常量
公开的接口中声明为extern
使用Did / Will 这样的动词以及Notification后缀来命名通知
.h
extern NSString * const TCFooDidBecomeBarNotification
.m
NSString * const TCFooDidBecomeBarNotification = @"TCFooDidBecomeBarNotification"
- Block
- 使用block定义异步接口
- 把需要提供的数据和错误信息整合在一个单独的block中,比分别提供成功和失败的block要好
- Apple提供的一些同步接口在成功的状态下向error参数写入垃圾值
- 深入block
- block在栈上被创建
- block可以复制到堆上
- block会捕获栈上的变量(或指针),并将其复制为自己的私有的const变量
- (如果在block中修改block外的)栈上的变量和指针,那么这个变量和指针必须使用__block关键字进行修饰
- 如果block 没有在其他地方被retain,那么它会随着栈生存并且当栈帧(stack frame)返回的时候消失。仅存在栈上时,block对对象访问的内存管理和声明周期是没有影响的
- block在OC的runtime里面被当做一等公民对待
- 他们有一个isa指针,一个类也是用isa指针在OC 的runtime来访问方法和存储数据的
- self的循环引用
- 使用weak来引用对象,避免循环引用
- 更优雅的方式:使用影子变量 @weakify / @strongify
- 把持有的block属性设置为Nil是一个好的实践
- 打破block捕获的作用于带来的循环引用
- 直接在block中使用self
- 只能在block不是作为一个property的时候使用,否则会导致循环引用
- 在block外定义一个__weak引用到self,并且在block内使用这个弱引用
- 当block被声明为一个property的使用使用
- 在block外定义一个弱引用,在block内通过这个弱引用定义一个__strong的强引用
- 优点
- 抢占执行的时候的鲁棒性
- 和并发执行有关。当涉及到一部服务的时候,block可能在执行之后被执行,并且不会发生关于self是否存在的问题