<EffectiveObjective-C2.0编写高质量iOS与OS X代码的52个有效方法>读书笔记(一)
<EffectiveObjective-C2.0编写高质量iOS与OS X代码的52个有效方法>读书笔记(二)
<EffectiveObjective-C2.0编写高质量iOS与OS X代码的52个有效方法>读书笔记(三)
书中的很多内容其实我们在平时写代码的过程中还是注意到了的. 像第五章的内存管理, 第六章的GCD, 都很值得花大篇幅来讲, 这两个话题的内容都可以独立出书了, 之前我也写过几篇关于GCD的文章, 所以这里就不再赘述了. 说两个小知识点:
-
利用
位段(bitfield)
缓存方法响应能力
这是一个C语言的特性, 比如:
struct data {
unsigned int fieldA : 8;
unsigned int fieldB : 4;
unsigned int fieldC : 2;
unsigned int fieldD : 1;
};
在结构体中, fieldA位段将占用8个二进制位, fieldB占用4个, fieldC占用两个, fieldD占用1个, 于是, fieldA可以表示0~255之间的值, 而fieldD则可以表示0或1这两个值. 我们可以像fieldD这样, 把代理对象是否实现了协议中的相关方法这一信息缓存起来, 如果创建的结构体中只有大小为1的位段, 那么就能把许多BOOL值塞入一小块数据里面了.
为什么要这么做呢? 我们在写代理的时候, 常常要判断代理对象是够实现了某个代理方法, 于是我们就常常会这样写:
if ([self.delegate respondsToSelector:@selector(someMethod:)]) {
[self.delegate someMethod:argms];
}
但是, 如果在一个类中我们反复判断代理对象是否实现了某个方法是一件很傻X的事情, 于是我们就想是否能够缓存这一信息, 只需要判断一次就够了, 于是, 位段(bitfield)
就大显身手了.
我们假设一个获取网络数据的工具类, 当我们异步拿到网络数据后, 就会执行代理回调的方法, 我们可能在这个网络工具类中频繁地判断代理对象是否实现了某个方法, 如果实现了, 我们就执行,返回数据或者返回错误. 如果没有, 我们就什么都不做. 但是, 就如同我们之前说的, 频繁判断是很耗性能的, 我们只需要判断一次就够了.
比如我先弄一个结构体变量:
@implementation YFNetworkTool {
struct {
unsigned int didReceiveData : 1;
unsigned int didFailedError : 1;
unsigned int didUpdateProgress : 1;
}_delegateFlags;
}
然后在设置代理的时候, 我同时判断代理对象有没有实现各个代理方法:
- (void)setDelegate:(id<YFNetworkToolDelegate>)delegate {
_delegate = delegate;
_delegateFlags.didReceiveData = [_delegate respondsToSelector:@selector(networkTool:didReceiveData:)];
_delegateFlags.didFailedError = [_delegate respondsToSelector:@selector(networkTool:didFailedWithError:)];
_delegateFlags.didUpdateProgress = [_delegate respondsToSelector:@selector(networkTool:didUpdateProgress:)];
}
这样, 我就不用每次判断了, 只需要取出结构体里的值出来即可, 因为位段为1, 就相当于只能存一个bit位, 要么是0 , 要么是1:
- (void)startConnectWithPort:(NSUInteger)port {
if (port == 1 && _delegateFlags.didReceiveData) {
NSString *str = @"成功连接服务器";
NSData *strData = [str dataUsingEncoding:NSUTF8StringEncoding];
[self.delegate networkTool:self didReceiveData:strData];
}else if (port == 2 && _delegateFlags.didUpdateProgress) {
NSProgress *progress = [NSProgress progressWithTotalUnitCount:32];
[self.delegate networkTool:self didUpdateProgress:progress];
}else if (_delegateFlags.didFailedError) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:101 userInfo:@{
NSLocalizedDescriptionKey : @"网络连接发生错误"
}];
[self.delegate networkTool:self didFailedWithError:error];
}
}
书中同时也提到:
如果要频繁通过数据源协议从数据源中获取多份相互独立的数据, 那么这项优化技术极有可能会提高程序的效率.
-
关于
NSTimer
的循环引用
timer会对target进行保留, 如果target是self, 那么timer就会对self进行强引用, 然而, 如果timer是self的一个属性的话, 就会self又会对timer进行强引用, 这样的话就会形成循环引用.
书中提到的方法是利用block来解决, 其实在YYKit的分类里面, 也是有NSTimer的分类的方法:
@implementation NSTimer (YYAdd)
+ (void)_yy_ExecBlock:(NSTimer *)timer {
if ([timer userInfo]) {
void (^block)(NSTimer *timer) = (void (^)(NSTimer *timer))[timer userInfo];
block(timer);
}
}
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats {
return [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(_yy_ExecBlock:) userInfo:[block copy] repeats:repeats];
}
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats {
return [NSTimer timerWithTimeInterval:seconds target:self selector:@selector(_yy_ExecBlock:) userInfo:[block copy] repeats:repeats];
}
@end
那么, 这种方法能解决问题吗?
block捕获了self, self又引用了timer. 看起来还是一个循环引用. 但此时只要用
__weak typeof(self)weakSelf = self;
就可以了, 就是利用常规的解决block循环引用的方法即可!