<EffectiveObjective-C2.0编写高质量iOS与OS X代码的52个有效方法>读书笔记(四)

<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保留目标对象

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循环引用的方法即可!

系统框架在ios10.0也引入了block的这个方法!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容