IOS GCD

转载: IOS GCD

GCD 是在iOS开发多线程技术里面,使用最简单,执行效率最高的,是相对底层的API,都是c函数。核心是d往ispatch queue里面添加执行的任务,由queue管理任务的执行。
在之前的面试中,经常会被大神问道:用过多线程吗?知道多线程的?知道各种实现多线的方法有什么区别吗?等等。之前只是会用多线程,但是对于面试这一关,只是会用还不够,还需要能把基本知识记住。在这推荐一个帖子,看了就懂个大概了 http://my.oschina.Net/u/2429584/blog/491771
dispatch queue有两种:串行serial queue和并行concurrent queue。无论是哪种queue都是FIFO(先进先出)

一、串行 serial queue

特点:执行queue中的第一个任务,结束后在执行第二个任务,第二个任务结束在执行第三个任务,,,,任何一个任务的执行必须在上一个任务的执行结束后。

1、获取mainQueue。mainQueue会在主线程中执行,即:主线程中执行队列中的各个任务
dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{
     NSLog(@"第1个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
 });//在block里写要执行的任务(代码)
    dispatch_async(mainQueue, ^{
        NSLog(@"第2个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(mainQueue, ^{
        NSLog(@"第3个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(mainQueue, ^{
        NSLog(@"第4个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(mainQueue, ^{
        NSLog(@"第5个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码) 
2 、 自己创建serial queue。//自己创建的serial queue不会在主线程中执行,queue会开辟一个子线程,在子线程中执行队列中的各个任务
dispatch_queue_t mySerialQueue = dispatch_queue_create("MySerialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(mySerialQueue, ^{
    NSLog(@"第1个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(mySerialQueue, ^{
    NSLog(@"第2个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(mySerialQueue, ^{
    NSLog(@"第3个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(mySerialQueue, ^{
    NSLog(@"第4个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(mySerialQueue, ^{
    NSLog(@"第5个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)

二、并行 concurrent

queue是另外一种队列。其特点:队列中的任 务,第一个先执行,不等第一个执行完毕,第二个就开始执行了,不等第二个任务执行完毕,第三个就开始执行了,以此类推。后面的任务执行的晚,但是不会等前面的执行完才执行。

获得concurrent queue的方法有2种:

1、获得global queue。
//第一个参数控制globalQueue的优先级,一共有4个优先级DISPATCH_QUEUE_PRIORITY_HIGH、DISPATCH_QUEUE_PRIORITY_DEFAULT、DISPATCH_QUEUE_PRIORITY_LOW、DISPATCH_QUEUE_PRIORITY_BACKGROUND。第二个参数是苹果预留参数,未来会用,目前填写为0
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
        NSLog(@"第1个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(globalQueue, ^{
        NSLog(@"第2个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(globalQueue, ^{
        NSLog(@"第3个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(globalQueue, ^{
        NSLog(@"第4个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(globalQueue, ^{
        NSLog(@"第5个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(globalQueue, ^{
        NSLog(@"第6个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(globalQueue, ^{
        NSLog(@"第7个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(globalQueue, ^{
        NSLog(@"第8个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(globalQueue, ^{
        NSLog(@"第9个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
    dispatch_async(globalQueue, ^{
        NSLog(@"第10个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
    });//在block里写要执行的任务(代码)
2、自己创建concurrent queue
dispatch_queue_t myConcurrentQueue = dispatch_queue_create("MyConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第1个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第2个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第3个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第4个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第5个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第6个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第7个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第8个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第9个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"第10个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//在block里写要执行的任务(代码)

concurrent queue会根据需要开辟若干个线程,并行执行队列中的任务(开始较晚的任务未必最后结束,开始较早的任务未必最先完成),开辟的线程数量 取决于多方面因素,比如:任务的数量,系统的内存资源等等,会以最优的方式开辟线程---根据需要开辟适当的线程。

延时调用

  - (void)after{
double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));

//    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//        NSLog(@"Hello");
//    });//dispatch_after函数是延迟执行某个任务,任务既可以在mainQueue中进行也可以在其他queue中进行.既可以在serial队列里执行也可以在concurrent队列里执行。

dispatch_queue_t myConcurrentQueue = dispatch_queue_create("MyConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_after(popTime, myConcurrentQueue, ^(void){
    NSLog(@"world");
});
}

若干任务执行完之后,想执行某任务

  - (void)group
{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t myConcurrentQueue = dispatch_queue_create("MyConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);

//dispatch_group_async用于把不同的任务归为一组
//dispatch_group_notify当指定组的任务执行完毕之后,执行给定的任务
dispatch_group_async(group, myConcurrentQueue, ^{
    NSLog(@"第1个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_group_async(group, myConcurrentQueue, ^{
    NSLog(@"第2个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_group_async(group, myConcurrentQueue, ^{
    NSLog(@"第3个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_group_async(group, myConcurrentQueue, ^{
    NSLog(@"第4个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_group_async(group, myConcurrentQueue, ^{
    NSLog(@"第5个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_group_async(group, myConcurrentQueue, ^{
    NSLog(@"第6个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_group_notify(group, myConcurrentQueue, ^{
    NSLog(@"group中的任务都执行完毕之后,执行此任务。所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//dispatch_group_notify group中的任务都执行完毕之后,执行的任务
dispatch_group_async(group, myConcurrentQueue, ^{
    NSLog(@"第7个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_group_async(group, myConcurrentQueue, ^{
    NSLog(@"第8个任务,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});

}

数据安全

为了保证访问同一个数据时,数据的安全,我们可以使用serial queue解决数据安全访问的问题。
//serial queue的缺陷是:后面的任务 必须等待 前面的任务 执行完毕 才能执行
//对于往数据库写入数据 使用serial queue无疑能保证数据的安全。
//对于从数据库中读取数据,使用serial queue就不太合适了,效率比较低。使用concurrent queue无疑是最合适的。
//真实的项目中,通常既有对数据库的写入,又有数据库的读取。

//下面给出了 既有数据库数据读取,又有数据库数据写入的处理方法dispatch_barrier_async

  - (void)barrier{
dispatch_queue_t myConcurrentQueue = dispatch_queue_create("MyConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"读取一些数据,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"读取另外一些数据,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"读取这些数据,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"读取那些数据,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});



dispatch_barrier_async(myConcurrentQueue, ^{
    NSLog(@"写入某些数据,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});//dispatch_barrier_async就像一道墙,之前的任务都并行执行,执行完毕之后,执行barrier中的任务,之后的任务也是并行执行。



dispatch_async(myConcurrentQueue, ^{
    NSLog(@"读取XXX数据,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"读取OOXX数据,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"读取aaa数据,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"读取bbb数据,所在线程%@,是否是主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
 }
来一段swift代码
let myConcurrentQueue : dispatch_queue_t = dispatch_queue_create("MyConcurrentQueue", DISPATCH_QUEUE_CONCURRENT)
dispatch_async(myConcurrentQueue, {
    print("读取一些数据,所在线程%@,是否是主线程:%d",NSThread.currentThread(),NSThread.currentThread().isMainThread)
})
dispatch_async(myConcurrentQueue, {
print("读取另外一些数据,所在线程%@,是否是主线程:%d",NSThread.currentThread(),NSThread.currentThread().isMainThread)
})
dispatch_async(myConcurrentQueue, {
    print("读取这些数据,所在线程%@,是否是主线程:%d",NSThread.currentThread(),NSThread.currentThread().isMainThread)
})
dispatch_async(myConcurrentQueue, {
    print("读取那些数据,所在线程%@,是否是主线程:%d",NSThread.currentThread(),NSThread.currentThread().isMainThread)
})



dispatch_barrier_async(myConcurrentQueue, {
    print("写入某些数据,所在线程%@,是否是主线程:%d",NSThread.currentThread(),NSThread.currentThread().isMainThread)
})//dispatch_barrier_async就像一道墙,之前的任务都并行执行,执行完毕之后,执行barrier中的任务,之后的任务也是并行执行。



dispatch_async(myConcurrentQueue, {
    print("读取XXX数据,所在线程%@,是否是主线程:%d",NSThread.currentThread(),NSThread.currentThread().isMainThread)
})
dispatch_async(myConcurrentQueue, {
    print("读取OOXX数据,所在线程%@,是否是主线程:%d",NSThread.currentThread(),NSThread.currentThread().isMainThread
)
})
///当然也可以这样
dispatch_async(myConcurrentQueue, {
    print("读取aaa数据,所在线程\(NSThread.currentThread()),是否是主线程:\(NSThread.currentThread().isMainThread))")
})
dispatch_async(myConcurrentQueue, {    print("读取bbb数据,所在线程%@,是否是主线程:%d",NSThread.currentThread(),NSThread.currentThread().isMainThread)
})

GCD中提供了API让某个任务执行若干次。

  - (void)apply {
NSArray *array = [NSArray arrayWithObjects:@"红楼梦",@"水浒传",@"三国演义",@"西游记", nil];

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t index) {
    NSLog(@"%@所在线程%@,是否是主线程:%d",[array objectAtIndex:index],[NSThread currentThread],[[NSThread currentThread] isMainThread]);
});
}

dispatch_once 用于定义那些只需要执行一次的代码,比如单例的创建

 - (void)once
  {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    NSLog(@"只执行一次");
    //这个block里的代码,在程序执行过程中只会执行一次。
    //比如在这里些单例的初始化
    //        static YourClass *instance = nil;
    //        instance = [[YourClass alloc] init];
});
  }

GCD里有一些API是dispatch_sync

//dispatch_async无需等block执行完,继续执行dispatch_async后面的代码
//dispatch_sync必须等block执行完,才继续执行dispatch_sync后面的代码

- (void)syn
 {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//    dispatch_sync(queue, ^{
//        for (int i = 0; i < 10; i++) {
//            NSLog(@"%d",i);
//        }
//    });
//    NSLog(@"haha");


dispatch_async(queue, ^{
    for (int i = 0; i < 10; i++) {
        NSLog(@"%d",i);
    }
});
NSLog(@"haha");
}

dispatch_async_f往队列里放函数指针,队列控制相应函数的执行,不在是控制block的执行

void function (NSString * str){
    NSLog(@"调用一个函数指针   参数是%@",str);
}

- (void)functionPoint
 {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async_f(queue, @"你好", function);//函数指针对应的函数类型:必须没有返回值,参数必须是void *。函数指针对应的参数,由dispatch_async_f第二个参数提供,可以是任意对象类型。   
 } 

fun dispatch_asysn_f(_ queue: dispatch_queue_t!,_ content: UnsafeMutablePointer<void>,_ work : dispatch_function_t)

queue 提交到的队列,队列的类型决定了是串行还是并行执行队列中的任务
context 传递给work的参数
work 执行的函数(C语言函数)


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

推荐阅读更多精彩内容

  • 简介 GCD(Grand Central Dispatch)是在macOS10.6提出来的,后来在iOS4.0被引...
    sunmumu1222阅读 850评论 0 2
  • 一、GCD的API 1. Dispatch queue 在执行处理时存在两种Dispatch queue: 等待现...
    doudo阅读 493评论 0 0
  • GCD笔记 总结一下多线程部分,最强大的无疑是GCD,那么先从这一块部分讲起. Dispatch Queue的种类...
    jins_1990阅读 752评论 0 1
  • 简介 GCD(Grand Central Dispatch)是在macOS10.6提出来的,后来在iOS4.0被引...
    JerryLMJ阅读 2,310评论 1 11
  • 一. 重点: 1.dispatch_queue_create(生成Dispatch Queue) 2.Main D...
    BestJoker阅读 1,574评论 2 2