读书笔记 - 《编写高质量iOS与OS X代码的52个有效方法》
2016-10-14 本文已影响17人
herui201211
一、 Objective-C
1. 了解Objective-C
- Objective-C为C语言添加了面向对象特性,是其超集。
- Objective-C使用动态绑定的消息结构,即运行时才会检查对象类型。接收 一条消息之后,究竟执行何种代码由运行时环境决定。
2. 在类的头文件中尽量少引入其它头文件
- 前向申明
@class xxx
3. 多用字面量语法,少用与之等价的方法
-
@"str"
、@12
、@{key:val}
、@[obj1,obj2]
[@{key:val} mutableCopy]
4. 多用常量类型,少用#define
预处理指令
static const NSTimeInterval kAnimationDuration = 0.3; //私有常量 .m定义即可
extern const NSTimeInterval kAnimationDuration; //全局常量 .h声明
const NSTimeInterval kAnimationDuration = 0.3; //.m实现
extern NSString * const EOCStringConstant;
NSString * const EOCStringConstant = @"value";
5. 用枚举表示状态、选项、状态码
- 用宏
NS_ENUM
定义枚举,如果多个项可以组合则使用宏NS_OPTIONS
二、 对象、消息、运行时
6. 理解“属性”概念
-
atomic
、nonatomic
-
assign
、strong
、copy
、weak
-
readwrite
、readonly
-
getter=<name>
、setter=<name>
7. 在对象内部尽量直接访问实例变量
- 在对象内部读取数据时应直接通过实例变量,而写入数据时应通过属性来写。
- 在
init
及dealloc
方法中,应总是直接通过实例变量来读写数据。 - 如果使用
lazy init
技术初始化数据时,此时需通过属性来读取数据。
8. 理解“对象等同性”概念
-
isEqual:
、hash
9. 以“类族模式”隐藏实现细节
+ (UIButton *)buttonWithType:(UIButtonType)type;
10. 在既有类中使用关联对象存放自定义数据
objc_setAssociatedObject()
objc_getAssociatedObject()
objc_removeAssociatedObjects()
11. 理解objc_msgSend的作用
- 消息由接收者、选择子及参数构成。给对象“发消息invoke a message”等同于“调用方法 call a method”
- 发给某对象的全部消息都要由“动态消息派发系统 dynamic message dispatch system”来处理,该系统会查出对应的方法,并执行其代码。
12. 理解消息转发机制
resolveInstanceMethod`、`resolveClassMethod //能否新增方法?
forwardingTargetForSelector //能否让其它对象处理?
forwardInvocation //封装消息相关细节
doesNotRecognizeSelector //抛异常
13. 用“方法调配技术method swizzling
”调试“黑盒方法”
class_getInstanceMethod()
method_exchangeImplementions()
14. 理解“类对象”的用意
typedef struct objc_class *Class;
struct objc_class {
Class isa; //
Class super_class;
const char *name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars;
struct objc_method_list **methodList;
struct objc_cache *cache;
struct objc_protocol_list *protocols;
};
三、接口与API设计
15. 用前缀避免命名空间冲突
duplicate symbol xxxClass in:
build/xxx.o
build/yyy.o
16. 提供“全能初始化方法”
- 参考
NSDate
17. 实现description方法
- 参考
�NSArray
打印更详细的对象信息
18. �尽量使用不可变对象
- 尽量创建不可变对象;
- 若某属性仅可用于对象内部修改,则在“分类”中将其由readonly扩展为readwrite;
- 不要把可变的collection作为属性公开,应提供相关方法去修改collection;
19. ��使用清晰而协调的命名方式
20. ��为私有方法名加前缀
- 可以用p_作为前缀,以便于区分公有方法
- 不要用下划线作为类私有方法前缀,因为苹果公司使用了这种做法
21. ��理解Objective-C错误模型
- Error domain 错误范围(字符串)
- Error code 错误码(整数)
- User info 用户信息(字典)
22. ��理解NSCopying协议
copyWithZone:
copy、mutableCopy
四、协议与分类
23. ��通过委托与数据源协议进行对象间通信
- 参考
UITableView
、UITableViewDelegate
、UITableViewDataSource
24. ��将类的实现代码分散到便于管理的数个分类中
- 通过分类机制,把类代码分成很多个易于管理的小块,以便于单独检视;
- 将视为私有的方法归入“private”分类中,隐藏实现细节;
25. ��总是为第三方类的分类名称加前缀
26. ��勿在分类中声明属性
27. ���使用“class-continuation分类 (匿名分类)”隐藏实现细节
28. ���通过协议提供匿名对象
五、�内存管理
29. �理解引用计数
retain
release
autorelease
30. �以ARC简化引用计数
31. 在dealloc方法中只释放引用并解除监听
32. 编写“异常安全代码”时留意内存管理问题
@try {
SomeClass *obj = [[SomeClass alloc] init];
[obj deSomething];
}
@cach(...) {
//ARC不会自动清理对象,需要启用-fobjc-arc-exceptions
}
33. 以弱引用避免保留环
-
retain cycle
->weak reference
34. 以“自动释放池块”降低内存峰值
@autoreleasepool{
//...
}
35. 用“僵尸对象Zombie Object”调试内存管理问题
- 系统在回收对象时,不会真的回收,而是把它转化为僵尸对象。通过环境变量NSZombieEnable可开启此功能;
- 系统会修改对象的isa指针,令其指向特殊的僵尸类,从而使该对象变为僵尸对象。僵尸类能够响应所有的选择子,响应方式为:打印一条包含消息内容及其接收者的消息,然后终止程序;
36. 不要使用retainCount
六、�块(block)与大中枢派发(GCD)
37. 理解块这一概念
return_type (^block_name) (parameters)
38. 为常用的块类型创建typedef
typedef int (^MyBlock) (BOOL flag, int value);
MyBlock myBlock = ^(BOOL flag, int value){
//
};
39. 用handler块降低代码分散程度
- (void)startWithCompletionHandler:(void (^) (id result, NSError *error))handler;
-
NSNotification
//用block使代码更紧凑` Observer
40. 用块引用其所属对象时不要出现保留环(retain cycle)
41. 多用派发队列,少用同步锁
- 滥用
@synchronized(self)
、NSLock
会降低代码执行效率; - 可用串行同步队列(serial synchronization queue)保证数据同步
_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0);
- (NSString *)someString{
_block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString *)someString{
dispatch_barrier_async(_syncQueue, ^{
_someString = someString;
});
}
42. 多用GCD,少用performSelector方法
- ARC下编译器无法插入适当的内存管理方法;
- 参数类型受局限性大;
- �建议用GCD将任务放到另一个线程上运行;
43. 掌握GCD及操作队列(NSOperationQueue
)的使用时机
44. 通过Dispatch Group机制,根据系统资源状况来执行任务
45. 使用dispatch_once来执行只需运行一次的线程安全代码
46. 不要使用dispatch_get_current_queue
六、�内存管理
47. 熟悉系统框架
- Foundation
- CFNetwork
- CoreAudio
- AVFoundation
- CoreData
- CoreText
- CoreAnimation
- CoreGraphics
48. 多用块枚举,少用for循环
49. 对自定义其内存管理语义的collection使用无缝桥接
- Foundation <=> CoreFoundation
50. 构建缓存时选用NSCache而非NSDictionary
- NSPurgeableData + NSCache
- 线程安全、自动删减、缓存上限(非硬限制)
51. 精减initialize与load的实现代码
- initialize相较于load方法,是惰性调用的;
- initialize方法执行时是线程安全的,可以调用任意类的任意方法,而load方法则不保证依赖的其它类是否已加载;
-若子类未实现initialize方法,则会执行超类的方法;
52. 别忘了NSTimer会保留其目标对象
- invalidate会使定时器失效;
- 扩充NSTimer功能,使用block来打破保留环
@implementation NSTimer (HE)
+ (NSTimer *)he_scheduledTimerWithTimeInterval:(NSTimeInterval)ti repeats:(BOOL)isRepeats block:(void(^)())block {
return [self scheduledTimerWithTimeInterval:ti target:self selector:@selector(he_selector:) userInfo:[block copy] repeats:isRepeats];
}
+ (void)he_selector:(NSTimer *)timer{
void(^block)() = timer.userInfo;
if(block) block();
}
@end
<--未完待续-->
//考虑写一些自己的理解以加深印象