GCD技术
多线程编程的三个技术 NSThread NSOperation GCD1.GCD(Grand central Dispatch:宏大的中央调度) 1) 是用纯C语言实现的.提供了非常多而且强大的函数,可以提高代码的执行效率和多核的利用率 2) 是在Mac OS X 10.6 雪豹系统 IOS4引入的一种新一代的多线程编程技术 2.1) 它是由苹果公司为多核的并行运算提出的解决方案 2.2) 它会自动利用更多的处理器核心 2.3) 不用关心线程代码,GCD会负责创建线程和调度任务 3) 核心概念 任务:执行什么功能,准备执行的代码 队列:用来存放(调度)任务的.先进先出FIFO,后添加进来的任务在队列末端,但是执行的时机不好说 4) 使用步骤 4.1) 先制定任务(确定想做什么事) 4.2) 把任务添加到适当的队列中即可 说明:GCD会自动将队列中的任务取出,放到对应的线程中执行 5) 区分一下名词 5.1) 同步和异步的区别 同步和异步是相对于另外一个任务而言的 同步:在当前线程执行,第一个任务不执行完,第二个任务不会开始 异步:在另一条线程中执行,不管第一个有没有执行完,都会开始第二个 5.2) 并发和串行队列的区别 并发和串行是相对于多个任务而言的 并发队列:在异步的情况下,多个任务并发执行 串行队列:多个任务会按照顺序执行 5.3) 并行与并发的区别 并发说的是多任务,并行说的是多线程 并行要求并发,并发并不能保证并行(单核) */1..自定义:串行队列dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);3.串行队列的异步任务(非常有用) 特点:会创建新线程(只有一个),任务按照顺序执行 这是斯坦福大学极力推荐的写法 优点:不会卡死,任务顺序执行的话方便调试 缺点:只开一个线程,并发性不强 /*.自定义:并发队列1.创建并发队列 先敲dispatch 参数1) C的字符串:队列的唯一标识符(域名反写) 参数2) 队列的类型:SERIAL(NULL)串行 CONCURRENT并发 */ dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT); /* 2.并发队列的同步任务 特点:不会创建新线程,任务按照顺序执行(然并卵) */#pragma mark - 2.系统自带的全局队列- (void)gcdDemo3 { /* 1.全局队列 参数1) 队列的优先级:有四种 参数2) 标记:为了以后准备的一个参数,填0 */ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); /* 2.全局队列的同步任务 */ for(int i = 0; i < 100; i ++) { dispatch_sync(queue, ^{ NSLog(@"1:%d----%@",i,[NSThread currentThread]); }); } /* 3.全局队列的异步任务 */ for(int i= 0; i < 100; i++){ dispatch_async(queue, ^{ NSLog(@"2:%d----%@",i,[NSThread currentThread]); }); } }#pragma mark - 2.系统自带的主队列- (void)gcdDemo4 { /* 1.主队列:一般用来UI操作 */ dispatch_queue_t queue = dispatch_get_main_queue(); /* 2.主队列的同步任务:造成一个现象"死锁" 这个写法不会出现 for(int i = 0; i < 100; i ++) { dispatch_sync(queue, ^{ NSLog(@"1:%d----%@",i,[NSThread currentThread]); }); } */ /* 3.主队列的异步任务(串行的) 在主线程中顺序执行 */ for(int i= 0; i < 100; i++){ dispatch_async(queue, ^{ NSLog(@"2:%d----%@",i,[NSThread currentThread]); }); } }/* 小总结 1.无论什么队列和什么任务,线程的创建和回收不需要程序猿管理,由队列负责 2.GCD在后端有一个线程池,自己决定block代码块在哪个线程中执行 3.任务的执行顺序跟队列有关,串行队列不管同步异步都是顺序执行,并发队列在同步时是顺序执行的 */#pragma mark - GCD补充 #pragma mark 群组- (void)gcdDemo5 { /* 例子:下载若干电影(10份),全部下载完以后,弹出警告框 先下载电影 --- 下载图片 --- 播放 */ dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_group_t group = dispatch_group_create(); //向组中和队列中添加任务 dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:10]; NSLog(@"下载钢铁侠1"); }); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:5]; NSLog(@"下载钢铁侠2"); }); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"下载钢铁侠3"); }); //当组中的所有任务都执行完毕,会发送一个通知 dispatch_group_notify(group, queue, ^{ NSLog(@"钢铁侠系列电影下载完毕"); });#pragma mark GCD的内存管理- (void)gcdDemo6 { /*MRC 1. 凡是通过create创建出来的queue/group都需要release create创建出来的东西引用计数为1,通过dispatch_release变为0,系统释放 2. 对于系统自带的globalQueue,mainQueue,不需要释放 3. 如果你创建的group/queue作为全局变量来使用的话,需要dispatch_retain */ dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL); dispatch_group_t group = dispatch_group_create(); /*相关操作*/ /*使用完毕以后记得释放掉*/ dispatch_release(queue); dispatch_release(group); }#pragma mark GCD的延迟方法- (void)gcdDemo7 { //[self performSelector:<#(SEL)#> withObject:<#(id)#> afterDelay:<#(NSTimeInterval)#>] /* 参数2) 单位是纳秒 */ dispatch_time_t myTime = dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC); dispatch_after(myTime, dispatch_get_main_queue(), ^{ NSLog(@"延迟后执行这段代码"); }); }#pragma mark GCD循环执行某段代码- (void)gcdDemo8 { /*for循环*/ /* 参数1)循环次数 参数2)队列 参数3)执行的内容 */ dispatch_queue_t queue = dispatch_get_global_queue(0, 0); /* %zu ---> 无符long 类型 优点:可以同时遍历多个,效率比较高 缺点:无序的 */ dispatch_apply(100, queue, ^(size_t index) { NSLog(@"------%zu----%@",index,[NSThread currentThread]); }); } GCD创建单例类@implementation Peoplestatic People *instance = nil; + (instancetype)sharedPeople { /*快捷方式:dispatch_onece*/ /* onceToken作为代码只执行一次的标记 */ static dispatch_once_t onceToken; /* 需要且必须执行一次的代码块 好处:如果多个线程同时调用这个方法,那么该函数会"同步等待",直到代码块执行完毕 */ dispatch_once(&onceToken, ^{ instance = [[super allocWithZone:NULL] init]; }); return instance; } + (id)allocWithZone:(struct _NSZone *)zone { return [self sharedPeople]; }@end block block:块. 在IOS4.0以后引入的新特性.block是Object-C的”特殊对象”,不是NSObject那种对象 */ /* 1. 数组遍历/字典:for for in block */ NSArray *array1 = @[@"1",@"2",@"3",@"4"]; /* id obj ---> 数组中的每一个对象,类型为id(可以根据实际情况进行修改) NSUInteger idx ---> 数组中对象的索引/编号 BOOL *stop ---> break */ [array1 enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) { NSLog(@"%d -- %@",idx,obj); if(idx == 1) { *stop = YES; } }]; /* NSEnumerationConcurrent:并发的 NSEnumerationReverse:反序 */ [array1 enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSLog(@"------- %d -- %@",idx,obj); }]; /* id key --> 字典的key id obj --> 字典的key所对应的内容 */ NSDictionary *dic = nil; [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { }]; /* 2. UIView动画 */ [UIView animateWithDuration:2 animations:^{ //动画的具体内容:设置控件的终点状态 }]; [UIView animateWithDuration:2 animations:^{ } completion:^(BOOL finished) { //动画完成之后执行的代码写到这里 }]; //[UIView transitionFromView:<#(UIView *)#> toView:<#(UIView *)#> duration:<#(NSTimeInterval)#> options:<#(UIViewAnimationOptions)#> completion:<#^(BOOL finished)completion#>] /* 3. 模态 */ [self presentViewController:nil animated:YES completion:^{ //弹出完成后要做的事情 }]; [self dismissViewControllerAnimated:YES completion:^{ //消失之后要做的事情 }]; /* 4.发送请求 */ [NSURLConnection sendAsynchronousRequest:nil queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { }]; 5.NSBlockOperation */ /* 6.ASIHttpRequest */ ASIHTTPRequest *request = nil; //request.delegate = self; [request setCompletionBlock:^{ //请求完成后的代码 }]; [request setFailedBlock:^{ //请求失败调用的代码 }]; [request startAsynchronous]; } /*内存管理 1.内存主要分为五块 1.1 栈(区):由编译器自动分配/释放,一般用来存放函数的参数值,局部变量的值等. 例子:int a = 0; 1.2 堆(区):一般由程序猿分配/释放.如果说程序员不释放,程序结束时可能由OS回收 例子:People *p = [[People alloc]init]; 1.3 全局区(静态区):全局变量/静态变量放在这个区域.初始化的全局变量/静态变量放在一个区域,未初始化的全局变量/静态变量放在另一个区域.程序结束后由系统释放 1.4 文字常量区:常量字符串.程序结束后系统释放 NSString *name=@"张三"; 1.5 程序代码区:存放函数体的二进制代码 2.block本身的内存管理 2.1 非ARC 2.1.1 在非ARC下,block默认分配在"栈区".如果离开block方法的作用域,block会被丢弃/释放 2.1.2 NSGlobalBlock(如果块中没有使用外部变量):对于retain,relese,copy无效 NSStackBlock(如果块中使用了外部变量):retain,release无效,copy有效,会得到NSMallocBlock 陷阱:如果把block添加到数组中,然后再从数组中取出的block已经被释放了,这个block变成了野指针.正确做法是[array addObject:[[block copy]autorelease]] NSMallocBlock(NSStackBlock被copy):支持retain,relese.retain/release可以增加/减少引用计数,但是block的retainCount始终为1. 建议:不要对block使用retain操作,不方便管理 2.2 ARC 3.block块中对象的内存 前提:主要是针对copy的block */ __block int a = 10; void(^block1)() = ^() { a = 20; }; block1(); NSLog(@"%@",[block1 copy]); //陷阱 int b = 20; MyBlock block2 = ^() { NSLog(@"%d",b); }; block2(); NSLog(@"block2:%@",block2); NSMutableArray *array = [NSMutableArray array]; [array addObject:[[block2 copy] autorelease]]; MyBlock block3 = array[0]; block3(); NSLog(@"block3:%@",block3); MyBlock block4 = [block3 retain]; NSLog(@"----%d",[block3 retainCount]); __block People * localObj = [People new]; staticObj = [People new]; gloabObj = [People new]; _propertyObj = [People new]; NSLog(@"%d--%d--%d--%d====%d",localObj.retainCount,staticObj.retainCount,gloabObj.retainCount,_propertyObj.retainCount,self.retainCount); /*block块中的对象 全局变量:不变 局部变量:通过block copy会+1 (MRC下局部变量一般需要加上__block,防止循环引用,ARC下加__weak) static变量:不变 属性:不变 self本身也会+1 */ MyBlock block5 = ^() { gloabObj.age = 10; staticObj.age = 20; _propertyObj.age = 30; localObj.age = 40; }; block5(); [block5 copy]; NSLog(@"%d--%d--%d--%d====%d",localObj.retainCount,staticObj.retainCount,gloabObj.retainCount,_propertyObj.retainCount,self.retainCount); }