1.尽量用字面量语法,少用与之等价的方法,如@1替代NSNumber nuberWithInt:1;语法更简明,而且遇到nil时会抛异常,提早发现问题,有时也会显得容错性不够。
2.多用常量类型代替#define,因为前者会进行类型检查,后者只做替换,出错了不容易发现;
3.使用枚举来表示状态机状态,好处是:1)多个枚举可以用位或方式共同表示&值;2)可以使用NS_ENUM和NS_OPTIONS宏指定枚举类型的底层数据类型;3)不实现case的default分支时,语法会提示枚举类型处理不完整的情况。
4.若想检测对象相等,需要同时提供isEqual和hash两个方法;相同的对象hash码相同,反之未必;
5.类族是指:类似UIButton,有buttonWithType:方法,根据传入的Type创建对应类型的按钮,这样使用者不需要关心具体的子类实现,而具体的按钮都是UIButton的子类;
6.设置关联对象的方式,可以为现有的类通过类别的方式扩展其属性,默认情况下类别不允许添加属性,通过objc_setAssociatedObject可以为self设置一个某个key上的关联对象,这样就做到了添加属性的目的,只有其它方法不可行时可以尝试这个办法,因为引起的bug可能也难以排查。
7.若对象无法响应某个方法,则进入消息转发流程,越往后代价越大:
1)先调用自己的resolveInstanceMethod:(SEL)selector或resolveClassMethod:(SEL)selector,取决于方法是由类调用的还是实例调用的;因此可通过重载这个方法并在其中使用method swizzling插入方法的方式实现方法解析;
2)调用自己的forwardingTargetForSelector:(SEL)selector方法来查找是否可将该消息转发给其它对象处理,如果能转发则该方法会返回对象,否则返回nil;这一步实现不如在前面就处理。
3)第2步返回nil时则进入完整消息转发流程了,创建NSInvocation对象,封装对象的选择子及参数等,将该对象交给消息派发系统,由消息派发系统进行消息转发,此时会调用这个方法派发消息:-(void)forwardInvocation:(NSInvocation *)invocation;此方法实现时,可以通过改变选择子与参数等方式来转发给其它对象,本类无法处理时给父类处理等,如果这一步也无法处理则会抛出doesNotRecognizeSelector异常。
8.尽量使用类型信息查询方法(系统提供的api)来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发机制;
9.在类中提供一个全能初始化方法,并在文档中说明,其它初始化方法均调用该方法,如果超类的初始化方法不适用于子类,则需要在子类中覆写并抛出异常;
10.抛出异常后,后续的代替即不会执行,因此内存释放等都会存在问题,因此只有出现了可使整个系统崩溃的严重错误时才应使用异常,否则可以把错误信息放在NSError中,经以输出参数返回给调用者;捕获异常时,需要清除try块中所有的对象以避免内存泄漏,arc时默认不会这样处理,需要加编译参数-fobj-arc-excption类似的参数,但这样会使代码变大,效率下降;
11.若想令自己的对象具有拷贝功能,则需要实现NSCopying协议,如果自定义的对象分为可变版本也不可变版本,则需要分别实现NSCopying与NSMutableCopying协议;复制对象时需要考虑深拷贝还时潜拷贝的问题;
12.类别可用来分享类的实现到多个文件中,匿名类别可用来隐藏实现及隐藏部分属性,即实现private属性的效果;
13.可以通过协议的方式提供匿名对象,即将方法返回或接受的对象、对象属性等定义为符合某一协议的id类型,如:
@property (nonatomic,weak)id <EOCDelegate> delegate;
14.僵尸对象可以用来调试内存问题,即对象delloc时并不会真正释放,而是将该对象转换为一个僵尸对象(通过调用objc_duplicateClass方法),再调用该对象方法时僵尸对象则打印日志或抛出异常。
15.块默认是分配在栈上的,要使之安全地不被释放,则可使用copy方法,还有一种全局栈,其不会捕获任何变量,因为它本身是全局的,最外层了,全局块的copy方法只是一个空操作;
16.在加载阶段,如果类实现了load方法,则系统就会调用它,如果有多个分类也定义了上方法,则类的load方法会先调用,然后调用分类的方法,不像其它方法,load方法不会有覆写机制;
17.首次使用某个类之前系统会向其发送initlize消息。由于此方法遵从普通覆写规则,因此通常要在里面判断执行的是哪一个类的初始化,总之load与initlize方法都尽量精减,这有利于保持程序响应,也能减少引入依赖环的几率,无法在编译期设定的全局常量,可以放在initlize方法里初始化。
2.多用常量类型代替#define,因为前者会进行类型检查,后者只做替换,出错了不容易发现;
3.使用枚举来表示状态机状态,好处是:1)多个枚举可以用位或方式共同表示&值;2)可以使用NS_ENUM和NS_OPTIONS宏指定枚举类型的底层数据类型;3)不实现case的default分支时,语法会提示枚举类型处理不完整的情况。
4.若想检测对象相等,需要同时提供isEqual和hash两个方法;相同的对象hash码相同,反之未必;
5.类族是指:类似UIButton,有buttonWithType:方法,根据传入的Type创建对应类型的按钮,这样使用者不需要关心具体的子类实现,而具体的按钮都是UIButton的子类;
6.设置关联对象的方式,可以为现有的类通过类别的方式扩展其属性,默认情况下类别不允许添加属性,通过objc_setAssociatedObject可以为self设置一个某个key上的关联对象,这样就做到了添加属性的目的,只有其它方法不可行时可以尝试这个办法,因为引起的bug可能也难以排查。
7.若对象无法响应某个方法,则进入消息转发流程,越往后代价越大:
1)先调用自己的resolveInstanceMethod:(SEL)selector或resolveClassMethod:(SEL)selector,取决于方法是由类调用的还是实例调用的;因此可通过重载这个方法并在其中使用method swizzling插入方法的方式实现方法解析;
2)调用自己的forwardingTargetForSelector:(SEL)selector方法来查找是否可将该消息转发给其它对象处理,如果能转发则该方法会返回对象,否则返回nil;这一步实现不如在前面就处理。
3)第2步返回nil时则进入完整消息转发流程了,创建NSInvocation对象,封装对象的选择子及参数等,将该对象交给消息派发系统,由消息派发系统进行消息转发,此时会调用这个方法派发消息:-(void)forwardInvocation:(NSInvocation *)invocation;此方法实现时,可以通过改变选择子与参数等方式来转发给其它对象,本类无法处理时给父类处理等,如果这一步也无法处理则会抛出doesNotRecognizeSelector异常。
8.尽量使用类型信息查询方法(系统提供的api)来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发机制;
9.在类中提供一个全能初始化方法,并在文档中说明,其它初始化方法均调用该方法,如果超类的初始化方法不适用于子类,则需要在子类中覆写并抛出异常;
10.抛出异常后,后续的代替即不会执行,因此内存释放等都会存在问题,因此只有出现了可使整个系统崩溃的严重错误时才应使用异常,否则可以把错误信息放在NSError中,经以输出参数返回给调用者;捕获异常时,需要清除try块中所有的对象以避免内存泄漏,arc时默认不会这样处理,需要加编译参数-fobj-arc-excption类似的参数,但这样会使代码变大,效率下降;
11.若想令自己的对象具有拷贝功能,则需要实现NSCopying协议,如果自定义的对象分为可变版本也不可变版本,则需要分别实现NSCopying与NSMutableCopying协议;复制对象时需要考虑深拷贝还时潜拷贝的问题;
12.类别可用来分享类的实现到多个文件中,匿名类别可用来隐藏实现及隐藏部分属性,即实现private属性的效果;
13.可以通过协议的方式提供匿名对象,即将方法返回或接受的对象、对象属性等定义为符合某一协议的id类型,如:
@property (nonatomic,weak)id <EOCDelegate> delegate;
14.僵尸对象可以用来调试内存问题,即对象delloc时并不会真正释放,而是将该对象转换为一个僵尸对象(通过调用objc_duplicateClass方法),再调用该对象方法时僵尸对象则打印日志或抛出异常。
15.块默认是分配在栈上的,要使之安全地不被释放,则可使用copy方法,还有一种全局栈,其不会捕获任何变量,因为它本身是全局的,最外层了,全局块的copy方法只是一个空操作;
16.在加载阶段,如果类实现了load方法,则系统就会调用它,如果有多个分类也定义了上方法,则类的load方法会先调用,然后调用分类的方法,不像其它方法,load方法不会有覆写机制;
17.首次使用某个类之前系统会向其发送initlize消息。由于此方法遵从普通覆写规则,因此通常要在里面判断执行的是哪一个类的初始化,总之load与initlize方法都尽量精减,这有利于保持程序响应,也能减少引入依赖环的几率,无法在编译期设定的全局常量,可以放在initlize方法里初始化。
18.NSTimer会保留其目标对象,如果目标对象又持有了NSTimer,则形成保留环,使用weak可破除保留环现象。
19.为什么NSError要传递**类型进去?因为oc所有都是值传递,比如你传递NSError *类型进去,这个指针传入之前指向的是空,则在函数中的新赋值不起作用,函数外仍然是空;把指针的指针传递进去,才可以做到改变。