Effective Objective-C 2.0重读笔记---1
上次看这本书的时候匆匆走了一遍,最近不太忙,重温了一遍,把笔记写出来~..
有兴趣的可以去买一本,感觉这本书还是挺不错的
由于大部分是在坐车的时候用手机写的,所以代码很少,图也很少
1. 尽量使用向前声明,延后引入头文件的时间,这样可以减少编译时间
2. 使用arraywithobjects:....如果遇到为空的变量,就会自动终止,后面的变量便会添加不上,切不会报错,会造成数据不一致问题,所以尽量使用字面量语法创建相关对象,减少出错且减少代码量
3. 使用字面量创建的对象都是不可变的,如果要获得可变对象,需要调用mutableCopy方法
4. static声明的变量(imple块和interf块中间)只会在本编译单元内可见。如果不加static,编译器便会为其创建一个外部符号,此时如果另一个编译单元里也声明了同名变量则会抛出异常信息
5. 定义常量时应该少用预处理指令,多用static const,这样的常量带有类型信息,可以减少出错机会。另外如果一个变量声明为static const编译器不会为其创建符号,而是直接替换
6 .如果要让外部使用你定义的常值变量,就要用在头文件声明extern nsstring *const variable 在实现文件里写nsstring const variable = @"kkkkk",这样外部就能访问这个变量,另外这种供全局访问的变量命名时最好使用该类类名开头,避免有重复声明的冲突
7. 使用枚举时,如果枚举不需要组合,应该是用NS_ENUM来定义枚举,这种形式的枚举编译时会自动添加相关的判断宏定义,使其在新环境中使用新语法,旧环境使用旧语法
8 .如果要使用组合枚举,最好使用NS_OPTIONS来定义。因为C++中,枚举运算后所得变量应为其底层数据,不应该是枚举型变量,所以要转换,使用上述方法可以自动判断是否需要实现显示转换
9. C++访问成员变量是是利用在编译期确定的偏移量去访问的,如果增加了一个变量,位于其后的变量将会访问出错,因为偏移量已经确定了,所以增加后会出错。OC的做法是将偏移量存在类对象里面,访问的时候回去累对象里查询,所以此时如果再新增加变量总是能正确访问。见书22页(ABI和API)
10. @synthesize方法命 = 变量名 在编译期间就会自动生成get和set方法@dynamic声明的变量系统不会为其创建实例变量和存取方法,而且在编译器访问属性代码时编译器即使没找到存入方法也不会报错,因为他相信在运行期可以找到,CoreData.就用到了这个技术,因为他的属性是来自数据库的不是实例变量
11 .属性后面的小括号里写的是属性的特质,属性的特质分为四类①原子性②读写权限③内存管理语义④方法名
12. 内存管理语义weak strong assign copy unsafe_unretained(该语义不会在其指向的对象销毁时会自动清空,weak是会的)
13. 在写带参数的初始化方法时,如果直接给属性赋值要注意赋值时内存管理语义,定义为copy的,记得要cooy 。
14. 在类对象内部直接访问实例变量的速度比用点语法访问快,因为直接访问不经过方法派发。直接访问不会调用存取方法,如果在存取方法里做了处理就要注意了并且直接访问不会调用内存管理语义。直接访问变量不会触发KVO,通过属性访问有助于排查错误。
两种访问机制各有其优缺点,折中的方法是获取时直接访问,设置时调用方法(保证内存管理语义)
15. 如果获取方法中写了懒加载,那么访问变量时应该用点语法访问,在初始化方法中应该总是用变量直接访问。因为,,这个不解
16. 抛出异常NSException raise: format:
17. 判断同等性如果是同一个类可以调用isEqual:如果是比较字符串同等性应该用起特定的方法以减少性能消耗。数组和字典也有自己的等同性比较方法
18. 如果要给自己定义的类写等同性判断方法,也应该复写isEqual方法,判断是否为本类,如果是本类则调用自己实现的判断方法,如果不是则交由父类判断
如果对象有自己的特定id则直接判断id即可。
19 .类族可以隐藏抽象基类背后的实现细节,UIButton就是其中之一。类族定义时一般需要提供枚举的子类和创建子类的类方法
20. 判断类族时应该用isKindOfClass:如果比较的是确定类实例应该用isMemberOfClass
21. oc可以让一个对象动态关联其他对象。objc_setAssociatedObject()函数可以给对象设置关联对象。objc_getAssociatedObjects()函数可以根据给定的键将之前存入的关键对象取出来。objc_removeAssociatedObject()此方法可以移除指定的关联对象
但是关联对象不宜滥用,因为很有可能引入保留环,而保留环不易调试解决问题
22. OC中的方法最终都会转换为底层的objc_msgSend()方法实现。选择子和参数合起来称为消息,objc_msgSend()能接受两个或以上的参数。objc_msgSend() 会依据接受者与选择子的类型调用适当的方法,该方法会在接收者所属的类中搜寻其方法列表,如果找到就执行,如果没找到就沿着类的继承体系向上查找,等找到这个方法之后再跳转,如果没有一直找到,就会执行消息转发操作。执行一个方法似乎要很多步骤,但是首次执行完一个方法后objc_msgSend()会将匹配结果缓存在快速映射表里,但是尽管如此还是不是静态函数绑定操作那样迅速
23. objc_msgSend_stret()用于处理返回量为结构体的方法,这个函数只能处理能被cpu寄存器容纳的结构体,如果结构体太大则会用另一个函数调用
objc_msgSend_fpret()处理返回数据为浮点型的方法。objc_msgSendSuper()用于给超类发送消息项
尾调用优化技术可以对消息机制进优化,当且仅当一个消息的最后一行调用了另一个函数切不将其返回值另做他用。
24 .消息转发分为两个阶段,首先会征询接受者看其是否能动态添加方法来处理这个未知的选择子。第一阶段响应完毕,此时运行期系统会请求接受者看看有没有其他对象可以处理这条消息,如果可以则交由那个对象处理,如果不行则会启动完整的消息转发机制,这时运行期系统会把消息相关细节封装到NSInvocation对象中,再次给接受者一个机会令其设法解决当前这条消息
25. 对象收到无法解读的消息后,首先调用下面的方法
-(BOOL)resolveInstanceMethod:(SEL)selector
如果寻找的是一个类方法,找不到会调用-(BOOL)resolveClassMethod:(SEL)selector
如果上面的方法返回假,则代表类不会新增方法来处理上面的消息则会进去下一步
如果上面的方法返回NO则代表本类不会新增方法来处理这个消息,下一步调用
-(id)forwardingTargetForSelector:(SEL)selector
这个返回值代表将会把消息派送给其进行处理,利用这个特性可以模拟多重继承的一些特征,在类内部指定执行的对象,外界看来好像是该对象亲自处理其实是他的内部对象处理
如果上面返回nil代表该对象内部没有可以处理改消息的对象,运行时系统此时就会调用完整的消息转发执行
-(void)forwardInvocation:(NSInvocation *)invocation
这个也是指定接受者来执行这个方法,实现此方法时若发现调用操作不应由本类处理,则需要调用超类的同名方法如果追溯到NSObject类还是处理不了此方法,则会调用doesNotRecognizeSelector:抛出异常
p49有例子
class_addMethod()可以动态添加方法
26. 当执行方法时,系统会到该类的方法列表上去查找该方法,这些方法均以IMP指针形式来表示例如
修改方法名和函数的映射表即可实现方法的互换
method_exchangeImplementations()这个方法可以互换两个函数
Method m1 = class_getInstanceMethod(即可获取相应的方法。
将自定方法跟系统定义的方法互换以后即可增加黑盒日志功能,在自定义的方法里做一些事情,然后在调用自定义的方法,看似会产生死循环,但需要注意的是运行期他们指向互换,调用的时候其实是调用原方法
Class superCla = class_getSuperclass(cla); class_getSuperclass()可以获取一个类的父类信息;
const char *name = class_getName(cla); class_getName(cla)可以获得一个类的类名
Class baseZombi = objc_lookUpClass([@"_NSZombie_" UTF8String]); objc_lookUpClass可以查找
当前系统有没有指定的类
zob = objc_duplicateClass(baseZombi, [@"_Zombie_Person" UTF8String], 0);
objc_duplicateClass可以动态添加类,以指定的模板添加类
objc_destructInstance(p); objc_destructInstance可以销毁一个实例;
abort();可以中断应用程序的执行
Block_copy(<#...#>) Block_release(<#...#>)分别执行拷贝和释放操作;
全局块是不访问外界变量的块,这样的块执行copy操作是空操作,因为他一成不变,没必要拷贝(可以优惠程序执行)
51. 如果必须产生保留环则应该在功能执行完毕时通过XX = nil等方法将某一方的引用置为空,解除保留环。很多网络框架都是先保留后解除