[25]Effective Objective-C 2.0【37-46】

第六章:Block与GCD

第三十七条:理解block

  • 1.根据block在内存中的位置,block被分成三种类型:

NSGlobalBlock 全局块:

这种块运行时无需获取外界任何状态,块所使用的内存区域在编译器就可以完全确定,所以该块声明在全局内存中。如果全局块执行copy会是一个空操作,相当于什么都没做。全局块例如:

void (^block)() = ^{
    NSLog(@"I am a NSGlobalBlock");
}
  • 2.NSStackBlock 栈块:
    栈块保存于栈区,超出变量作用域,栈上的block以及__block变量都会被销毁。例如:
NSString *name = @"PHP";
void (^block)() = ^{
    NSLog(@"世界上最好的编程语言是%@", name);
};
NSLog(@"%@", block);

运行下你会发现控制台打印的是:

<__NSStackBlock__: 0x7fff5480fa18>

什么,你说什么,你打印出来的是__ NSMallocBlock __? 那是因为你在ARC下编译的,ARC下编译器编译时会帮你优化自动帮你加上了copy操作,你可以用-fno-objc-arc关闭ARC再看一下

  • 3.NSMallocBlock 堆块:
    NSMallocBlock内心独白:我已经被暴露了,为什么要最后才介绍我!!
    堆block内存保存于堆区,在变量作用域结束时不受影响。通过之前在ARC下的输出已经看到了__ NSMallocBlock __.所以我们在定义block类型的属性时常常加上copy修饰,这个修饰其实是多余的,系统在ARC的时候已经帮我们做了copy,但是还是建议写上copy。

第三十八条:为常用的块类型创建typedef

这条主要是为了代码更易读,也比较重要。

- (void)getDataWithHost:(NSString *)host success:(void (^)(id responseDic))success;
//以上要改成下面这种
typedef void (^SuccessBlock)(id responseDic);
- (void)getDataWithHost:(NSString *)host success:(SuccessBlock)success;

第三十九条:用handler块降低代码分散程度

在iOS开发中,我们经常需要异步执行一些任务,然后等待任务执行结束之后通知相关方法。实现此需求的做法很多,比如说有些人可能会选择用委托协议。那么在这种异步执行一些任务,然后等待执行结束之后调用代理的时候,可能代码就会比较分散。当多个任务都需要异步,等等就显得比较不那么合理了。

所以我们可以考虑使用block的方式设计,这样业务相关的代码会比较紧凑,不会显得那么凌乱。

第四十条:用块引用其所属对象是不要出现保留环

这点比较基础了,但是要稍微说一下,不是一定得在block中使用weakself,比如下面:

[YTKNetwork requestBlock:^(id responsObject) {
      NSLog(@"%@",self.name);
  }];

block 不是被self所持有的,在block中就可以使用self

第四十一条:多用派发队列,少用同步锁

在iOS开发中,如果有多个线程要执行同一份代码,我们可能需要加锁来实现某种同步机制。有人可能第一印象想到的就是@synchronized(self),例如:

- (void)synchronizedMethod {
    @synchronized(self){
        // Safe
    }
}

这样写法效率很低,而且也不能保证线程中觉得的安全。如果有很多属性,那么每个属性的同步块都要等其他同步块执行完毕才能执行。
应该用GCD来替换:

_syncQueue = dispatch_queue_create("syncQueue", DISPATCH_QUEUE_CONCURRENT);

//读取字符串
- (NSString*)someString {
    __block NSString *localSomeString;
     dispatch_sync(_syncQueue, ^{
        localSomeString = _someString;
    });
     return localSomeString;
}
- (void)setSomeString:(NSString*)someString {
     dispatch_barrier_async(_syncQueue, ^{
        _someString = someString;
    });
}

第四十二条:多用GCD,少用performSelector系列方法

Objective-C本质上是一门非常动态的语言,开发者在开发中可以指定任何一个方法去调用,也可以延迟调用一些方法,或者指定运行方法的线程。一般我们会想到performSelector,但是在GCD出来之后基本就没那么需要performSelector了,performSelector也有很多缺点:

内存管理问题:在ARC下使用performSelector我们经常会看到编译器发出如下警告:warning: performSelector may cause a leak because its selector is unknown [-Warc-performSelector-leaks]

performSelector的返回值只能是void或对象类型。

performSelector无法处理带有多个参数的选择子,最多只能处理两个参数。
为了改变这些,我们可以用下面这种方式

dispatch_async(dispatch_get_main_queue(), ^{
        [self doSomething];
});

替换掉

[self performSelectorOnMainThread:@selector(doSomething) 
                       withObject:nil 
                    waitUntilDone:NO];

然后还可以用

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 
                                (int64_t)(5.0 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^(void){
    [self doSomething];
});

替换

[self performSelector:@selector(doSomething) 
           withObject:nil 
           afterDelay:5.0];
});

第四十三条:掌握GCD以及操作队列的使用时机

GCD技术确实很棒,但是也有一些局限性,或者说有一些场景并不适合。比如过想取消队列中的某个操作,或者需要后台执行任务。还有一种技术叫NSOperationQueue,其实NSOperationQueue跟GCD有很多相像之处。NSOperationQueue在GCD之前就已经有了,GCD就是在其某些原理上构建的。GCD是C层次的API,而NSOperation是重量级的Objective-C对象。

使用NSOperation和NSOperationQueue的优点:

  1. 支持取消某个操作:在运行任务前,可以在NSOperation对象上调用cancel方法,用以表明此任务不需要执行。不过已经启动的任务无法取消。GCD队列是无法取消的,GCD是“安排好之后就不管了(fire and forget)”。

  2. 支持指定操作间的依赖关系:一个操作可以依赖其他多个操作,例如从服务器下载并处理文件的动作可以用操作来表示,而在处理其他文件之前必须先下载“清单文件”。而后续的下载工作,都要依赖于先下载的清单文件这一操作。这时如果操作队列允许并发执行的话,后续的下载操作就可以在他依赖的下载清单文件操作执行完毕之后开始同时执行。

  3. 支持通过KVO监控NSOperation对象的属性:可以通过isCancelled属性来判断任务是否已取消,通过isFinished属性来判断任务是否已经完成等等。

  4. 支持指定操作的优先级:操作的优先级表示此操作与队列中其他操作之间的优先关系,优先级搞的操作先执行,优先级低的后执行。GCD的队列也有优先级,不过不是针对整个队列的。

  5. 重用NSOperation对象。在开发中你可以使用NSOperation的子类或者自己创建NSOperation对象来保存一些信息,可以在类中定义方法,使得代码能够多次使用。不必重复自己。

第四十四条:通过Dispatch Group机制,根据系统资源状况来执行任务

这条主要是介绍dispatch group,任务分组的功能。他可以把任务分组,然后等待这组任务执行完毕时会有通知,开发者可以拿到结果然后继续下一步操作。

另外通过dispatch group在并发队列上同时执行多项任务的时候,GCD会根据系统资源状态来帮忙调度这些并发执行的任务。

第四十五条:使用dispatch_once来执行只需要运行一次的线程安全代码

这条讲的是常用的dispatch_once

+ (id)sharedInstance {
     static EOCClass *sharedInstance = nil;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
            sharedInstance = [[self alloc] init];
    });
     return sharedInstance;
}

dispatch_once比较高效,没有重量级的同步机制。

第四十六条:不要使用dispatch_get_current_queue

dispatch_get_current_queue 函数的行为常常与开发者所预期的不同,此函数已经废弃,只应做调试之用。

由于GCD是按层级来组织的,所以无法单用某个队列对象来描述"当前队列"这一概念。

dispatch_get_current_queue 函数用于解决由不可以重入的代码所引发的死锁,然后能用此函数解决的问题,通常也可以用"队列特定数据"来解决。

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

推荐阅读更多精彩内容