├── README.md ├── gcdtest.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── dongxuwei.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── dongxuwei.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ └── xcschememanagement.plist │ └── greatstar.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist └── gcdtest ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── DemoViewController.h ├── DemoViewController.m ├── DemoViewController.xib ├── GCDTableViewController.h ├── GCDTableViewController.m ├── Info.plist ├── data.plist ├── gcdtest.xcdatamodeld ├── .xccurrentversion └── gcdtest.xcdatamodel │ └── contents └── main.m /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## 前言 5 | 6 | 各路大神对GCD的原理解析和使用方法网上到处都是,可以轻松搜索到。那为什么笔者还要自己动手写一篇所谓的"葵花宝典"呢?其实本篇文章主要是普及了一些基础知识概念。例如队列、线程、异步同步的区别,搞懂这些才是弄明白GCD的条件。笔者搜集了GCD的绝大部分api,包括不常用的、冷门的。这里没有高大上的实现原理,没有难懂的底层实现。以最浅显的,最简单的文字说明配上demo和代码实例最后结合运行log,让你轻松不费脑力的理解GCD的应用场景和操作姿势。 7 | 8 | ![](https://ws3.sinaimg.cn/large/006tNc79gy1fpae9mawdwj30720fb74i.jpg) 9 | 10 | 11 | 12 | ## 基础概念 13 | 14 | #### 关于GCD: 15 | 16 | (1)是基于c语言的底层api 17 | (2)用block定义任务,使用起来非常灵活便捷 18 | (3)GCD会自动利用更多的CPU内核(比如双核、四核) 19 | (4)GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程) 20 | (5)程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码 21 | 22 | #### 关于进程: 23 | 24 | 正在进行中的程序被称为进程,负责程序运行的内存分配;每一个进程都有自己独立的虚拟内存空间; 25 | 26 | #### 关于线程: 27 | 28 | 线程是进程中一个独立的执行路径(控制单元);一个进程中至少包含一条线程,即主线程。 29 | 30 | #### 关于队列: 31 | 队列用来存放任务,一种符合 FIFO(先进先出)原则的数据结构,线程的创建和回收不需要程序员操作,由队列负责。 32 | 33 | 串行队列:队列中的任务只会顺序执行,一个任务执行完毕后,再执行下一个任务 34 | 并发队列:队列中的多个任务并发(同时)执行,而且无法确定任务的执行顺序     35 | 全局队列:是系统开发的,直接拿过来用就可以;与并行队列类似,但调试时,无法确认操作所在队列 36 | 主队列: 每一个应用程序对应唯一一个主队列,是gcd中自带的一种特殊的串行队列,直接获取即可。放在主队列中的任务,都会在主线程中执行。在多线程开发中,使用主队列更新UI。 37 | 38 | #### 关于操作: 39 | dispatch_async 异步操作,在新的线程中执行任务,具备开启新线程的能力(不是百分百开启新线程,会取决于任务所在队列类型),会并发执行,无法确定任务的执行顺序; 40 | dispatch_sync 同步操作,在当前线程中执行任务,不具备开启新线程的能力,会依次顺序执行; 41 | 42 | * 图例: 43 | ![](https://ws1.sinaimg.cn/large/006tNc79gy1fp6pp1knyxj30zk0chgmp.jpg) 44 | 45 | ![](https://ws3.sinaimg.cn/large/006tNc79gy1fp6pljr4g6j31080yktbi.jpg) 46 | 47 | ## 使用姿势 48 | 49 | #### 分为两步: 50 | 51 | 第一步:创建一个队列; 52 | 53 | 第二步:将任务放到队列中; 54 | 55 | #### 三个关键点: 56 | 57 | 第一点:任务内容; 58 | 59 | 第二点:队列类别; 60 | 61 | 第三点:操作(追加)姿势; 62 | 63 | ## 队列和任务 64 | 65 | #### 1. 获取队列: 66 | 67 | GCD中大体可以分为三种队列: 68 | 69 | * 串行队列: 70 | `dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL);` 71 | 72 | * 并发队列: 73 | * 一般并发队列 74 | `dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);` 75 | * 全局并发队列可以作为普通并发队列来使用 76 | `dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);` 77 | 78 | * 主队列: 79 | `dispatch_queue_t queue = dispatch_get_main_queue();` 80 | 81 | #### 2. 操作(追加)方式: 82 | 83 | * 同步执行任务创建方法 84 | ``` 85 | dispatch_sync(queue, ^{ 86 | NSLog(@"我是任务"); 87 | }); 88 | ``` 89 | * 异步执行任务创建方法 90 | ``` 91 | dispatch_async(queue, ^{ 92 | NSLog(@"我是任务"); 93 | }); 94 | ``` 95 | 96 | #### 3. 队列 + 任务: 97 | 98 | ##### 3.1 队列 + 任务的六种组合 99 | 100 | 看到这里你不难发现,GCD 提供了同步执行任务的创建方法`dispatch_sync`和异步执行任务创建方法`dispatch_async`,配合上述的三种队列形式(串行队列、并发队列、主队列),那么就会存在六种不同的多线程使用方法,如下: 101 | 102 | * 同步执行 & 并发队列 : 不新建线程,在当前线程中顺序执行 103 | * 异步执行 & 并发队列 : 新建多个新线程,线程会复用,无序执行 104 | * 同步执行 & 串行队列 : 在当前线程中顺序执行 105 | * 异步执行 & 串行队列 : 新建一条新的线程,在该线程中顺序执行 106 | * 异步执行 & 主队列 : 不新建线程,在主线程中顺序执行 107 | * 同步执行 & 主队列(在主线程中会crash): 主线程中会产生死锁 108 | 109 | 110 | ![](https://ws1.sinaimg.cn/large/006tNc79gy1fpa1h50ozjj30vs09gaao.jpg) 111 | 112 | ##### 3.2 各种组合的使用方法 113 | 114 | ###### 同步执行 & 并发队列: 115 | 116 | ``` 117 | -(void)syncAndConcurrentqueue{ 118 | 119 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 120 | 121 | //下面提供两种并发队列的获取方式,其运行结果无差别,所以归为了一类,你可以自由选择 122 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 123 | 124 | // 全局并发队列 125 | // dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 126 | 127 | // 第一个任务 128 | dispatch_sync(queue, ^{ 129 | 130 | //这里线程暂停2秒,模拟一般的任务的耗时操作 131 | [NSThread sleepForTimeInterval:2]; 132 | 133 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 134 | }); 135 | 136 | // 第二个任务 137 | dispatch_sync(queue, ^{ 138 | 139 | //这里线程暂停2秒,模拟一般的任务的耗时操作 140 | [NSThread sleepForTimeInterval:2]; 141 | 142 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 143 | }); 144 | 145 | // 第三个任务 146 | dispatch_sync(queue, ^{ 147 | 148 | //这里线程暂停2秒,模拟一般的任务的耗时操作 149 | [NSThread sleepForTimeInterval:2]; 150 | 151 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 152 | }); 153 | 154 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 155 | } 156 | ``` 157 | 158 | * 输出结果: 159 | 160 | ![](https://ws4.sinaimg.cn/large/006tNc79gy1fpa1hjyzm5j31e4050myy.jpg) 161 | 162 | * 总结:只会在当前线程中依次执行任务,不会开启新线程,执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 163 | 164 | 165 | ###### 异步执行 & 并发队列: 166 | 167 | ``` 168 | -(void)asyncAndConcurrentqueue{ 169 | 170 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 171 | 172 | //下面提供两种并发队列的获取方式,其运行结果无差别,所以归为了一类,你可以自由选择 173 | 174 | //dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 175 | 176 | // 全局并发队列 177 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 178 | 179 | // 第一个任务 180 | dispatch_async(queue, ^{ 181 | 182 | //这里线程暂停2秒,模拟一般的任务的耗时操作 183 | [NSThread sleepForTimeInterval:2]; 184 | 185 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 186 | }); 187 | 188 | // 第二个任务 189 | dispatch_async(queue, ^{ 190 | 191 | //这里线程暂停2秒,模拟一般的任务的耗时操作 192 | [NSThread sleepForTimeInterval:2]; 193 | 194 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 195 | }); 196 | 197 | // 第三个任务 198 | dispatch_async(queue, ^{ 199 | 200 | //这里线程暂停2秒,模拟一般的任务的耗时操作 201 | [NSThread sleepForTimeInterval:2]; 202 | 203 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 204 | }); 205 | 206 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 207 | 208 | } 209 | 210 | ``` 211 | * 输出结果: 212 | 213 | ![](https://ws2.sinaimg.cn/large/006tNc79gy1fpa1hvkwehj31ey05cjtb.jpg) 214 | 215 | * 总结:从log中可以发现,系统另外开启了3个线程,并且任务是同时执行的,并不是按照1>2>3顺序执行。所以异步+并发队列具备开启新线程的能力,且并发队列可开启多个线程,同时执行多个任务。 216 | 217 | ###### 同步执行 & 串行队列: 218 | 219 | ``` 220 | -(void)syncAndSerialqueue{ 221 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 222 | 223 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 224 | 225 | // 第一个任务 226 | dispatch_sync(queue, ^{ 227 | 228 | //这里线程暂停2秒,模拟一般的任务的耗时操作 229 | [NSThread sleepForTimeInterval:2]; 230 | 231 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 232 | }); 233 | 234 | // 第二个任务 235 | dispatch_sync(queue, ^{ 236 | 237 | //这里线程暂停2秒,模拟一般的任务的耗时操作 238 | [NSThread sleepForTimeInterval:2]; 239 | 240 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 241 | }); 242 | 243 | // 第三个任务 244 | dispatch_sync(queue, ^{ 245 | 246 | //这里线程暂停2秒,模拟一般的任务的耗时操作 247 | [NSThread sleepForTimeInterval:2]; 248 | 249 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 250 | }); 251 | 252 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 253 | 254 | } 255 | 256 | ``` 257 | * 输出结果: 258 | 259 | ![](https://ws1.sinaimg.cn/large/006tNc79gy1fpa1i33ifdj31eu056q4u.jpg) 260 | 261 | * 总结:只会在当前线程中依次执行任务,不会开启新线程,执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 262 | 263 | ###### 异步执行 & 串行队列: 264 | 265 | ``` 266 | -(void)asyncAndSerialqueue{ 267 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 268 | 269 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 270 | 271 | // 第一个任务 272 | dispatch_async(queue, ^{ 273 | 274 | //这里线程暂停2秒,模拟一般的任务的耗时操作 275 | [NSThread sleepForTimeInterval:2]; 276 | 277 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 278 | }); 279 | 280 | // 第二个任务 281 | dispatch_async(queue, ^{ 282 | 283 | //这里线程暂停2秒,模拟一般的任务的耗时操作 284 | [NSThread sleepForTimeInterval:2]; 285 | 286 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 287 | }); 288 | 289 | // 第三个任务 290 | dispatch_async(queue, ^{ 291 | 292 | //这里线程暂停2秒,模拟一般的任务的耗时操作 293 | [NSThread sleepForTimeInterval:2]; 294 | 295 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 296 | }); 297 | 298 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 299 | 300 | } 301 | ``` 302 | * 输出结果: 303 | ![](https://ws3.sinaimg.cn/large/006tNc79gy1fpa1ia4yh7j31fw056wge.jpg) 304 | 305 | * 总结:开启了一条新线程,异步执行具备开启新线程的能力,因为是串行队列所以只开启一个线程,在该线程中执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 306 | 307 | ###### 异步执行 & 主队列: 308 | 309 | ``` 310 | -(void)asyncAndMainqueue{ 311 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 312 | 313 | //获取主队列 314 | dispatch_queue_t queue = dispatch_get_main_queue(); 315 | 316 | // 第一个任务 317 | dispatch_async(queue, ^{ 318 | 319 | //这里线程暂停2秒,模拟一般的任务的耗时操作 320 | [NSThread sleepForTimeInterval:2]; 321 | 322 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 323 | }); 324 | 325 | // 第二个任务 326 | dispatch_async(queue, ^{ 327 | 328 | //这里线程暂停2秒,模拟一般的任务的耗时操作 329 | [NSThread sleepForTimeInterval:2]; 330 | 331 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 332 | }); 333 | 334 | // 第三个任务 335 | dispatch_async(queue, ^{ 336 | 337 | //这里线程暂停2秒,模拟一般的任务的耗时操作 338 | [NSThread sleepForTimeInterval:2]; 339 | 340 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 341 | }); 342 | 343 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 344 | 345 | } 346 | ``` 347 | * 输出结果: 348 | ![](https://ws2.sinaimg.cn/large/006tNc79gy1fpa1ii2uhwj31ei04wjt4.jpg) 349 | 350 | * 总结:所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中),在主线程中执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 351 | 352 | ###### 同步执行 & 主队列(在主线程中会crash): 353 | 354 | ``` 355 | -(void)syncAndMainqueue{ 356 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 357 | 358 | //获取主队列 359 | dispatch_queue_t queue = dispatch_get_main_queue(); 360 | 361 | // 第一个任务 362 | dispatch_sync(queue, ^{ 363 | 364 | //这里线程暂停2秒,模拟一般的任务的耗时操作 365 | [NSThread sleepForTimeInterval:2]; 366 | 367 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 368 | }); 369 | 370 | // 第二个任务 371 | dispatch_sync(queue, ^{ 372 | 373 | //这里线程暂停2秒,模拟一般的任务的耗时操作 374 | [NSThread sleepForTimeInterval:2]; 375 | 376 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 377 | }); 378 | 379 | // 第三个任务 380 | dispatch_sync(queue, ^{ 381 | 382 | //这里线程暂停2秒,模拟一般的任务的耗时操作 383 | [NSThread sleepForTimeInterval:2]; 384 | 385 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 386 | }); 387 | 388 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 389 | } 390 | 391 | //下面的例子类似:在同一个同步串行队列中,再使用该队列同步执行任务也是会发生死锁。 392 | -(void)syncAndMainqueue1{ 393 | 394 | dispatch_queue_t queue1 = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 395 | 396 | dispatch_sync(queue1, ^{ 397 | 398 | [NSThread sleepForTimeInterval:2]; 399 | 400 | NSLog(@"----11111-----当前线程%@",[NSThread currentThread]);//到这里就死锁了 401 | 402 | dispatch_sync(queue1, ^{ 403 | 404 | [NSThread sleepForTimeInterval:2]; 405 | 406 | NSLog(@"----22222---当前线程%@",[NSThread currentThread]); 407 | }); 408 | 409 | NSLog(@"----333333-----当前线程%@",[NSThread currentThread]); 410 | 411 | }); 412 | NSLog(@"----44444-----当前线程%@",[NSThread currentThread]); 413 | } 414 | 415 | ``` 416 | * 输出结果: 417 | ![](https://ws4.sinaimg.cn/large/006tNc79gy1fpa1itxjj6j31hc0oaq5r.jpg) 418 | 419 | 420 | * 总结:直接crash。这是因为发生了死锁,在gcd中,禁止在主队列(串行队列)中再以同步操作执行主队列任务。同理,在同一个同步串行队列中,再使用该队列同步执行任务也是会发生死锁。 421 | 422 | ###### 同步执行 & 主队列(在其它线程中): 423 | 424 | ``` 425 | -(void)othersyncAndMainqueue{ 426 | 427 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 428 | 429 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 430 | 431 | // 第一个任务 432 | dispatch_async(queue, ^{ 433 | 434 | NSLog(@"----执行任务---%@",[NSThread currentThread]); 435 | 436 | //获取主队列 437 | dispatch_queue_t queue = dispatch_get_main_queue(); 438 | 439 | // 第一个任务 440 | dispatch_sync(queue, ^{ 441 | 442 | //这里线程暂停2秒,模拟一般的任务的耗时操作 443 | [NSThread sleepForTimeInterval:2]; 444 | 445 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 446 | }); 447 | }); 448 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 449 | 450 | } 451 | ``` 452 | * 输出结果: 453 | ![](https://ws3.sinaimg.cn/large/006tNc79gy1fpa0w1vxkyj31eg03y3zu.jpg) 454 | 455 | * 总结:所有任务都是在主线程(非当前线程)中执行的,没有开启新的线程(所有放在主队列中的任务,都会放到主线程中执行)。在主线程中执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 456 | 457 | ## GCD常用API及其使用方法 458 | 459 | #### 1. Dispatch Queue: 460 | ``` 461 | //各种队列的获取方法 462 | -(void)getQueue{ 463 | 464 | //主队列的获取方法:主队列是串行队列,主队列中的任务都将在主线程中执行 465 | dispatch_queue_t mainqueue = dispatch_get_main_queue(); 466 | 467 | //串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 468 | dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 469 | 470 | //并发队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 471 | dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 472 | 473 | //全局并发队列的获取方法:第一个参数表示队列优先级,我们选择默认的好了,第二个参数flags作为保留字段备用,一般都直接填0 474 | dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 475 | } 476 | ``` 477 | 478 | #### 2. `dispatch_queue_creat`: 479 | ``` 480 | //自定义队列的创建方法 481 | -(void)queue_create{ 482 | //串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 483 | dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 484 | 485 | //并发队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 486 | dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 487 | } 488 | ``` 489 | 490 | #### 3. `dispatch_set_target_queue`: 491 | 492 | * `dispatch_set_target_queue`可以更改`Dispatch Queue`的执行优先级 493 | `dispatch_queue_create`函数生成的`DisPatch Queue`不管是`Serial DisPatch Queue`还是`Concurrent Dispatch Queue`,执行的优先级都与默认优先级的`Global Dispatch queue`相同,如果需要变更生成的`Dispatch Queue`的执行优先级则需要使用`dispatch_set_target_queue`函数。 494 | 495 | ``` 496 | //使用dispatch_set_target_queue更改Dispatch Queue的执行优先级 497 | -(void)testTargetQueue1{ 498 | 499 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 500 | 501 | //串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 502 | dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", NULL); 503 | 504 | //指定一个任务 505 | dispatch_async(seriaQueue, ^{ 506 | 507 | //这里线程暂停2秒,模拟一般的任务的耗时操作 508 | [NSThread sleepForTimeInterval:2]; 509 | 510 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 511 | }); 512 | 513 | //全局并发队列的获取方法:第一个参数表示队列优先级,我们选择默认的好了,第二个参数flags作为保留字段备用,一般都直接填0 514 | dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 515 | 516 | //指定一个任务 517 | dispatch_async(globalQueue, ^{ 518 | 519 | //这里线程暂停2秒,模拟一般的任务的耗时操作 520 | [NSThread sleepForTimeInterval:2]; 521 | 522 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 523 | }); 524 | 525 | 526 | //第一个参数为要设置优先级的queue,第二个参数是参照物,即将第一个queue的优先级和第二个queue的优先级设置一样。 527 | //第一个参数如果是系统提供的【主队列】或【全局队列】,则不知道会出现什么情况,因此最好不要设置第一参数为系统提供的队列 528 | dispatch_set_target_queue(seriaQueue,globalQueue); 529 | 530 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 531 | 532 | } 533 | 534 | //dispatch_set_target_queue除了能用来设置队列的优先级之外,还能够创建队列的层次体系,当我们想让不同队列中的任务同步的执行时,我们可以创建一个串行队列,然后将这些队列的target指向新创建的队列即可。 535 | - (void)testTargetQueue2 { 536 | 537 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 538 | 539 | dispatch_queue_t targetQueue = dispatch_queue_create("com.test.target_queue", DISPATCH_QUEUE_SERIAL); 540 | 541 | dispatch_queue_t queue1 = dispatch_queue_create("com.test.queue1", DISPATCH_QUEUE_SERIAL); 542 | 543 | dispatch_queue_t queue2 = dispatch_queue_create("com.test.queue2", DISPATCH_QUEUE_CONCURRENT); 544 | 545 | dispatch_set_target_queue(queue1, targetQueue); 546 | 547 | dispatch_set_target_queue(queue2, targetQueue); 548 | 549 | //指定一个异步任务 550 | dispatch_async(queue1, ^{ 551 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 552 | [NSThread sleepForTimeInterval:2]; 553 | }); 554 | 555 | //指定一个异步任务 556 | dispatch_async(queue2, ^{ 557 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 558 | [NSThread sleepForTimeInterval:2]; 559 | }); 560 | 561 | //指定一个异步任务 562 | dispatch_async(queue2, ^{ 563 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 564 | [NSThread sleepForTimeInterval:2]; 565 | }); 566 | 567 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 568 | 569 | } 570 | ``` 571 | 572 | #### 4. dispatch_after: 573 | ``` 574 | //延时执行,需要注意的是:dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的。 575 | -(void)dispatch_after{ 576 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 577 | 578 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 579 | // 2秒后异步追加任务代码到主队列等待执行 580 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 581 | }); 582 | 583 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 584 | 585 | } 586 | ``` 587 | 588 | #### 5. dispatch_once: 589 | ``` 590 | //只执行一次,通常在创建单例时使用,多线程环境下也能保证线程安全 591 | -(void)dispatch_once_1{ 592 | 593 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 594 | 595 | static dispatch_once_t onceToken; 596 | dispatch_once(&onceToken, ^{ 597 | NSLog(@"----只执行一次的任务---当前线程%@",[NSThread currentThread]); 598 | }); 599 | 600 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 601 | 602 | } 603 | 604 | ``` 605 | 606 | #### 6. dispatch_apply: 607 | ``` 608 | //快速遍历方法,可以替代for循环的函数。dispatch_apply按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。 609 | //会创建新的线程,并发执行 610 | -(void)dispatch_apply{ 611 | 612 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 613 | 614 | dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 615 | 616 | dispatch_apply(100, globalQueue, ^(size_t index) { 617 | NSLog(@"执行第%zd次的任务---%@",index, [NSThread currentThread]); 618 | }); 619 | 620 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 621 | 622 | } 623 | ``` 624 | 625 | #### 7. dispatch_group: 626 | ``` 627 | //队列组:当我们遇到需要异步下载3张图片,都下载完之后再拼接成一个整图的时候,就需要用到gcd队列组。 628 | -(void)dispatch_group{ 629 | 630 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 631 | 632 | dispatch_group_t group = dispatch_group_create(); 633 | 634 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 635 | // 第一个任务 636 | [NSThread sleepForTimeInterval:2]; 637 | 638 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 639 | 640 | }); 641 | 642 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 643 | // 第二个任务 644 | [NSThread sleepForTimeInterval:2]; 645 | 646 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 647 | }); 648 | 649 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 650 | 651 | // 第三个任务 652 | [NSThread sleepForTimeInterval:2]; 653 | 654 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 655 | }); 656 | 657 | 658 | dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 659 | [NSThread sleepForTimeInterval:2]; 660 | 661 | NSLog(@"----执行最后的汇总任务---当前线程%@",[NSThread currentThread]); 662 | }); 663 | 664 | //若想执行完上面的任务再走下面这行代码可以加上下面这句代码 665 | 666 | // 等待上面的任务全部完成后,往下继续执行(会阻塞当前线程) 667 | // dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 668 | 669 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 670 | } 671 | 672 | //dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1 673 | //dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。 674 | //当 group 中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。 675 | -(void)dispatch_group_1{ 676 | 677 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 678 | 679 | dispatch_group_t group = dispatch_group_create(); 680 | 681 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 682 | 683 | dispatch_group_enter(group); 684 | dispatch_async(queue, ^{ 685 | // 第一个任务 686 | [NSThread sleepForTimeInterval:2]; 687 | 688 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 689 | 690 | dispatch_group_leave(group); 691 | }); 692 | 693 | dispatch_group_enter(group); 694 | dispatch_async(queue, ^{ 695 | // 第二个任务 696 | [NSThread sleepForTimeInterval:2]; 697 | 698 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 699 | 700 | dispatch_group_leave(group); 701 | }); 702 | 703 | dispatch_group_enter(group); 704 | dispatch_async(queue, ^{ 705 | // 第三个任务 706 | [NSThread sleepForTimeInterval:2]; 707 | 708 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 709 | 710 | dispatch_group_leave(group); 711 | }); 712 | 713 | dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 714 | 715 | [NSThread sleepForTimeInterval:2]; 716 | NSLog(@"----执行最后的汇总任务---当前线程%@",[NSThread currentThread]); 717 | }); 718 | 719 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 720 | 721 | } 722 | 723 | ``` 724 | 725 | #### 8. dispatch_semaphore: 726 | ``` 727 | //信号量 728 | //总结:信号量设置的是2,在当前场景下,同一时间内执行的线程就不会超过2,先执行2个线程,等执行完一个,下一个会开始执行。 729 | -(void)dispatch_semaphore{ 730 | 731 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 732 | 733 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); 734 | 735 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 736 | 737 | //任务1 738 | dispatch_async(queue, ^{ 739 | 740 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 741 | 742 | NSLog(@"----开始执行第一个任务---当前线程%@",[NSThread currentThread]); 743 | 744 | [NSThread sleepForTimeInterval:2]; 745 | 746 | NSLog(@"----结束执行第一个任务---当前线程%@",[NSThread currentThread]); 747 | 748 | dispatch_semaphore_signal(semaphore); 749 | }); 750 | 751 | //任务2 752 | dispatch_async(queue, ^{ 753 | 754 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 755 | 756 | NSLog(@"----开始执行第二个任务---当前线程%@",[NSThread currentThread]); 757 | 758 | [NSThread sleepForTimeInterval:1]; 759 | 760 | NSLog(@"----结束执行第二个任务---当前线程%@",[NSThread currentThread]); 761 | 762 | dispatch_semaphore_signal(semaphore); 763 | }); 764 | 765 | //任务3 766 | dispatch_async(queue, ^{ 767 | 768 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 769 | 770 | NSLog(@"----开始执行第三个任务---当前线程%@",[NSThread currentThread]); 771 | 772 | [NSThread sleepForTimeInterval:2]; 773 | 774 | NSLog(@"----结束执行第三个任务---当前线程%@",[NSThread currentThread]); 775 | 776 | dispatch_semaphore_signal(semaphore); 777 | }); 778 | 779 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 780 | 781 | } 782 | 783 | ``` 784 | 785 | #### 9. Dispatch I/O: 786 | ``` 787 | //以下为苹果中使用Dispatch I/O和Dispatch Data的例子 788 | pipe_q = dispatch_queue_create("PipeQ",NULL); 789 | pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM,fd,pipe_q,^(int err){ 790 | close(fd); 791 | }); 792 | 793 | *out_fd = fdpair[i]; 794 | 795 | dispatch_io_set_low_water(pipe_channel,SIZE_MAX); 796 | 797 | dispatch_io_read(pipe_channel,0,SIZE_MAX,pipe_q, ^(bool done,dispatch_data_t pipe data,int err){ 798 | if(err == 0) 799 | { 800 | size_t len = dispatch_data_get_size(pipe data); 801 | if(len > 0) 802 | { 803 | const char *bytes = NULL; 804 | char *encoded; 805 | 806 | dispatch_data_t md = dispatch_data_create_map(pipe data,(const void **)&bytes,&len); 807 | asl_set((aslmsg)merged_msg,ASL_KEY_AUX_DATA,encoded); 808 | free(encoded); 809 | _asl_send_message(NULL,merged_msg,-1,NULL); 810 | asl_msg_release(merged_msg); 811 | dispatch_release(md); 812 | } 813 | } 814 | 815 | if(done) 816 | { 817 | dispatch_semaphore_signal(sem); 818 | dispatch_release(pipe_channel); 819 | dispatch_release(pipe_q); 820 | } 821 | }); 822 | ``` 823 | #### 10. dispatch_barrier_async: 824 | ``` 825 | //隔断方法:当前面的写入操作全部完成之后,再执行后面的读取任务。当然也可以用Dispatch Group和dispatch_set_target_queue,只是比较而言,dispatch_barrier_async会更加顺滑 826 | -(void)dispatch_barrier_async{ 827 | 828 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 829 | 830 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 831 | 832 | dispatch_async(queue, ^{ 833 | // 第一个写入任务 834 | [NSThread sleepForTimeInterval:3]; 835 | 836 | NSLog(@"----执行第一个写入任务---当前线程%@",[NSThread currentThread]); 837 | 838 | }); 839 | dispatch_async(queue, ^{ 840 | // 第二个写入任务 841 | [NSThread sleepForTimeInterval:1]; 842 | 843 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 844 | 845 | }); 846 | 847 | dispatch_barrier_async(queue, ^{ 848 | // 等待处理 849 | [NSThread sleepForTimeInterval:2]; 850 | 851 | NSLog(@"----等待前面的任务完成---当前线程%@",[NSThread currentThread]); 852 | 853 | }); 854 | 855 | dispatch_async(queue, ^{ 856 | // 第一个读取任务 857 | [NSThread sleepForTimeInterval:2]; 858 | 859 | NSLog(@"----执行第一个读取任务---当前线程%@",[NSThread currentThread]); 860 | 861 | }); 862 | dispatch_async(queue, ^{ 863 | // 第二个读取任务 864 | [NSThread sleepForTimeInterval:2]; 865 | 866 | NSLog(@"----执行第二个读取任务---当前线程%@",[NSThread currentThread]); 867 | 868 | }); 869 | 870 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 871 | 872 | } 873 | ``` 874 | #### 11. dispatch_suspend & dispatchp_resume: 875 | ``` 876 | //场景:当追加大量处理到Dispatch Queue时,在追加处理的过程中,有时希望不执行已追加的处理。例如演算结果被Block截获时,一些处理会对这个演算结果造成影响。在这种情况下,只要挂起Dispatch Queue即可。当可以执行时再恢复。 877 | //总结:dispatch_suspend,dispatch_resume提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的任务,也就是如果挂起之前已经有队列中的任务在进行中,那么该任务依然会被执行完毕 878 | -(void)dispatch_suspend{ 879 | 880 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 881 | 882 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 883 | 884 | dispatch_async(queue, ^{ 885 | // 执行第一个任务 886 | [NSThread sleepForTimeInterval:5]; 887 | 888 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 889 | 890 | }); 891 | 892 | dispatch_async(queue, ^{ 893 | // 执行第二个任务 894 | [NSThread sleepForTimeInterval:5]; 895 | 896 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 897 | 898 | }); 899 | 900 | dispatch_async(queue, ^{ 901 | // 执行第三个任务 902 | [NSThread sleepForTimeInterval:5]; 903 | 904 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 905 | }); 906 | 907 | //此时发现意外情况,挂起队列 908 | NSLog(@"suspend"); 909 | dispatch_suspend(queue); 910 | 911 | //挂起10秒之后,恢复正常 912 | [NSThread sleepForTimeInterval:10]; 913 | 914 | //恢复队列 915 | NSLog(@"resume"); 916 | dispatch_resume(queue); 917 | 918 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 919 | 920 | } 921 | ``` 922 | 参考资料: 923 | 924 | * Objective-C 高级编程 iOS 与 OS X 多线程和内存管理 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | -------------------------------------------------------------------------------- /gcdtest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F44941B0204F99A4004ED29B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F44941AF204F99A4004ED29B /* AppDelegate.m */; }; 11 | F44941B6204F99A4004ED29B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F44941B4204F99A4004ED29B /* Main.storyboard */; }; 12 | F44941B9204F99A4004ED29B /* gcdtest.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = F44941B7204F99A4004ED29B /* gcdtest.xcdatamodeld */; }; 13 | F44941BB204F99A4004ED29B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F44941BA204F99A4004ED29B /* Assets.xcassets */; }; 14 | F44941BE204F99A4004ED29B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F44941BC204F99A4004ED29B /* LaunchScreen.storyboard */; }; 15 | F44941C1204F99A4004ED29B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F44941C0204F99A4004ED29B /* main.m */; }; 16 | F44941C920537184004ED29B /* GCDTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F44941C820537183004ED29B /* GCDTableViewController.m */; }; 17 | F44941CB20537B90004ED29B /* data.plist in Resources */ = {isa = PBXBuildFile; fileRef = F44941CA20537B90004ED29B /* data.plist */; }; 18 | F44941CF2053881F004ED29B /* DemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F44941CD2053881F004ED29B /* DemoViewController.m */; }; 19 | F44941D02053881F004ED29B /* DemoViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F44941CE2053881F004ED29B /* DemoViewController.xib */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | F44941AB204F99A4004ED29B /* gcdtest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = gcdtest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | F44941AE204F99A4004ED29B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 25 | F44941AF204F99A4004ED29B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 26 | F44941B5204F99A4004ED29B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | F44941B8204F99A4004ED29B /* gcdtest.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = gcdtest.xcdatamodel; sourceTree = ""; }; 28 | F44941BA204F99A4004ED29B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | F44941BD204F99A4004ED29B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | F44941BF204F99A4004ED29B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | F44941C0204F99A4004ED29B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | F44941C720537183004ED29B /* GCDTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDTableViewController.h; sourceTree = ""; }; 33 | F44941C820537183004ED29B /* GCDTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDTableViewController.m; sourceTree = ""; }; 34 | F44941CA20537B90004ED29B /* data.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = data.plist; sourceTree = ""; }; 35 | F44941CC2053881F004ED29B /* DemoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; 36 | F44941CD2053881F004ED29B /* DemoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoViewController.m; sourceTree = ""; }; 37 | F44941CE2053881F004ED29B /* DemoViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DemoViewController.xib; sourceTree = ""; }; 38 | /* End PBXFileReference section */ 39 | 40 | /* Begin PBXFrameworksBuildPhase section */ 41 | F44941A8204F99A4004ED29B /* Frameworks */ = { 42 | isa = PBXFrameworksBuildPhase; 43 | buildActionMask = 2147483647; 44 | files = ( 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | F44941A2204F99A4004ED29B = { 52 | isa = PBXGroup; 53 | children = ( 54 | F44941AD204F99A4004ED29B /* gcdtest */, 55 | F44941AC204F99A4004ED29B /* Products */, 56 | ); 57 | sourceTree = ""; 58 | }; 59 | F44941AC204F99A4004ED29B /* Products */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | F44941AB204F99A4004ED29B /* gcdtest.app */, 63 | ); 64 | name = Products; 65 | sourceTree = ""; 66 | }; 67 | F44941AD204F99A4004ED29B /* gcdtest */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | F44941AE204F99A4004ED29B /* AppDelegate.h */, 71 | F44941AF204F99A4004ED29B /* AppDelegate.m */, 72 | F44941CC2053881F004ED29B /* DemoViewController.h */, 73 | F44941CD2053881F004ED29B /* DemoViewController.m */, 74 | F44941CE2053881F004ED29B /* DemoViewController.xib */, 75 | F44941C720537183004ED29B /* GCDTableViewController.h */, 76 | F44941C820537183004ED29B /* GCDTableViewController.m */, 77 | F44941B4204F99A4004ED29B /* Main.storyboard */, 78 | F44941BA204F99A4004ED29B /* Assets.xcassets */, 79 | F44941BC204F99A4004ED29B /* LaunchScreen.storyboard */, 80 | F44941BF204F99A4004ED29B /* Info.plist */, 81 | F44941C0204F99A4004ED29B /* main.m */, 82 | F44941B7204F99A4004ED29B /* gcdtest.xcdatamodeld */, 83 | F44941CA20537B90004ED29B /* data.plist */, 84 | ); 85 | path = gcdtest; 86 | sourceTree = ""; 87 | }; 88 | /* End PBXGroup section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | F44941AA204F99A4004ED29B /* gcdtest */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = F44941C4204F99A5004ED29B /* Build configuration list for PBXNativeTarget "gcdtest" */; 94 | buildPhases = ( 95 | F44941A7204F99A4004ED29B /* Sources */, 96 | F44941A8204F99A4004ED29B /* Frameworks */, 97 | F44941A9204F99A4004ED29B /* Resources */, 98 | ); 99 | buildRules = ( 100 | ); 101 | dependencies = ( 102 | ); 103 | name = gcdtest; 104 | productName = gcdtest; 105 | productReference = F44941AB204F99A4004ED29B /* gcdtest.app */; 106 | productType = "com.apple.product-type.application"; 107 | }; 108 | /* End PBXNativeTarget section */ 109 | 110 | /* Begin PBXProject section */ 111 | F44941A3204F99A4004ED29B /* Project object */ = { 112 | isa = PBXProject; 113 | attributes = { 114 | LastUpgradeCheck = 0920; 115 | ORGANIZATIONNAME = greatstar; 116 | TargetAttributes = { 117 | F44941AA204F99A4004ED29B = { 118 | CreatedOnToolsVersion = 9.2; 119 | ProvisioningStyle = Automatic; 120 | }; 121 | }; 122 | }; 123 | buildConfigurationList = F44941A6204F99A4004ED29B /* Build configuration list for PBXProject "gcdtest" */; 124 | compatibilityVersion = "Xcode 8.0"; 125 | developmentRegion = en; 126 | hasScannedForEncodings = 0; 127 | knownRegions = ( 128 | en, 129 | Base, 130 | ); 131 | mainGroup = F44941A2204F99A4004ED29B; 132 | productRefGroup = F44941AC204F99A4004ED29B /* Products */; 133 | projectDirPath = ""; 134 | projectRoot = ""; 135 | targets = ( 136 | F44941AA204F99A4004ED29B /* gcdtest */, 137 | ); 138 | }; 139 | /* End PBXProject section */ 140 | 141 | /* Begin PBXResourcesBuildPhase section */ 142 | F44941A9204F99A4004ED29B /* Resources */ = { 143 | isa = PBXResourcesBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | F44941BE204F99A4004ED29B /* LaunchScreen.storyboard in Resources */, 147 | F44941BB204F99A4004ED29B /* Assets.xcassets in Resources */, 148 | F44941CB20537B90004ED29B /* data.plist in Resources */, 149 | F44941B6204F99A4004ED29B /* Main.storyboard in Resources */, 150 | F44941D02053881F004ED29B /* DemoViewController.xib in Resources */, 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXResourcesBuildPhase section */ 155 | 156 | /* Begin PBXSourcesBuildPhase section */ 157 | F44941A7204F99A4004ED29B /* Sources */ = { 158 | isa = PBXSourcesBuildPhase; 159 | buildActionMask = 2147483647; 160 | files = ( 161 | F44941B9204F99A4004ED29B /* gcdtest.xcdatamodeld in Sources */, 162 | F44941C920537184004ED29B /* GCDTableViewController.m in Sources */, 163 | F44941C1204F99A4004ED29B /* main.m in Sources */, 164 | F44941CF2053881F004ED29B /* DemoViewController.m in Sources */, 165 | F44941B0204F99A4004ED29B /* AppDelegate.m in Sources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXSourcesBuildPhase section */ 170 | 171 | /* Begin PBXVariantGroup section */ 172 | F44941B4204F99A4004ED29B /* Main.storyboard */ = { 173 | isa = PBXVariantGroup; 174 | children = ( 175 | F44941B5204F99A4004ED29B /* Base */, 176 | ); 177 | name = Main.storyboard; 178 | sourceTree = ""; 179 | }; 180 | F44941BC204F99A4004ED29B /* LaunchScreen.storyboard */ = { 181 | isa = PBXVariantGroup; 182 | children = ( 183 | F44941BD204F99A4004ED29B /* Base */, 184 | ); 185 | name = LaunchScreen.storyboard; 186 | sourceTree = ""; 187 | }; 188 | /* End PBXVariantGroup section */ 189 | 190 | /* Begin XCBuildConfiguration section */ 191 | F44941C2204F99A4004ED29B /* Debug */ = { 192 | isa = XCBuildConfiguration; 193 | buildSettings = { 194 | ALWAYS_SEARCH_USER_PATHS = NO; 195 | CLANG_ANALYZER_NONNULL = YES; 196 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 197 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 198 | CLANG_CXX_LIBRARY = "libc++"; 199 | CLANG_ENABLE_MODULES = YES; 200 | CLANG_ENABLE_OBJC_ARC = YES; 201 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 202 | CLANG_WARN_BOOL_CONVERSION = YES; 203 | CLANG_WARN_COMMA = YES; 204 | CLANG_WARN_CONSTANT_CONVERSION = YES; 205 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 206 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 207 | CLANG_WARN_EMPTY_BODY = YES; 208 | CLANG_WARN_ENUM_CONVERSION = YES; 209 | CLANG_WARN_INFINITE_RECURSION = YES; 210 | CLANG_WARN_INT_CONVERSION = YES; 211 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 212 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 213 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 214 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 215 | CLANG_WARN_STRICT_PROTOTYPES = YES; 216 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 217 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 218 | CLANG_WARN_UNREACHABLE_CODE = YES; 219 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 220 | CODE_SIGN_IDENTITY = "iPhone Developer"; 221 | COPY_PHASE_STRIP = NO; 222 | DEBUG_INFORMATION_FORMAT = dwarf; 223 | ENABLE_STRICT_OBJC_MSGSEND = YES; 224 | ENABLE_TESTABILITY = YES; 225 | GCC_C_LANGUAGE_STANDARD = gnu11; 226 | GCC_DYNAMIC_NO_PIC = NO; 227 | GCC_NO_COMMON_BLOCKS = YES; 228 | GCC_OPTIMIZATION_LEVEL = 0; 229 | GCC_PREPROCESSOR_DEFINITIONS = ( 230 | "DEBUG=1", 231 | "$(inherited)", 232 | ); 233 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 234 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 235 | GCC_WARN_UNDECLARED_SELECTOR = YES; 236 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 237 | GCC_WARN_UNUSED_FUNCTION = YES; 238 | GCC_WARN_UNUSED_VARIABLE = YES; 239 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 240 | MTL_ENABLE_DEBUG_INFO = YES; 241 | ONLY_ACTIVE_ARCH = YES; 242 | SDKROOT = iphoneos; 243 | }; 244 | name = Debug; 245 | }; 246 | F44941C3204F99A4004ED29B /* Release */ = { 247 | isa = XCBuildConfiguration; 248 | buildSettings = { 249 | ALWAYS_SEARCH_USER_PATHS = NO; 250 | CLANG_ANALYZER_NONNULL = YES; 251 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 253 | CLANG_CXX_LIBRARY = "libc++"; 254 | CLANG_ENABLE_MODULES = YES; 255 | CLANG_ENABLE_OBJC_ARC = YES; 256 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 257 | CLANG_WARN_BOOL_CONVERSION = YES; 258 | CLANG_WARN_COMMA = YES; 259 | CLANG_WARN_CONSTANT_CONVERSION = YES; 260 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 261 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 262 | CLANG_WARN_EMPTY_BODY = YES; 263 | CLANG_WARN_ENUM_CONVERSION = YES; 264 | CLANG_WARN_INFINITE_RECURSION = YES; 265 | CLANG_WARN_INT_CONVERSION = YES; 266 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 267 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 269 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 270 | CLANG_WARN_STRICT_PROTOTYPES = YES; 271 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 272 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 273 | CLANG_WARN_UNREACHABLE_CODE = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | CODE_SIGN_IDENTITY = "iPhone Developer"; 276 | COPY_PHASE_STRIP = NO; 277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 278 | ENABLE_NS_ASSERTIONS = NO; 279 | ENABLE_STRICT_OBJC_MSGSEND = YES; 280 | GCC_C_LANGUAGE_STANDARD = gnu11; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 289 | MTL_ENABLE_DEBUG_INFO = NO; 290 | SDKROOT = iphoneos; 291 | VALIDATE_PRODUCT = YES; 292 | }; 293 | name = Release; 294 | }; 295 | F44941C5204F99A5004ED29B /* Debug */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 299 | CODE_SIGN_STYLE = Automatic; 300 | INFOPLIST_FILE = gcdtest/Info.plist; 301 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 302 | PRODUCT_BUNDLE_IDENTIFIER = com.gc.mpaas.gcdtest; 303 | PRODUCT_NAME = "$(TARGET_NAME)"; 304 | TARGETED_DEVICE_FAMILY = "1,2"; 305 | }; 306 | name = Debug; 307 | }; 308 | F44941C6204F99A5004ED29B /* Release */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 312 | CODE_SIGN_STYLE = Automatic; 313 | INFOPLIST_FILE = gcdtest/Info.plist; 314 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 315 | PRODUCT_BUNDLE_IDENTIFIER = com.gc.mpaas.gcdtest; 316 | PRODUCT_NAME = "$(TARGET_NAME)"; 317 | TARGETED_DEVICE_FAMILY = "1,2"; 318 | }; 319 | name = Release; 320 | }; 321 | /* End XCBuildConfiguration section */ 322 | 323 | /* Begin XCConfigurationList section */ 324 | F44941A6204F99A4004ED29B /* Build configuration list for PBXProject "gcdtest" */ = { 325 | isa = XCConfigurationList; 326 | buildConfigurations = ( 327 | F44941C2204F99A4004ED29B /* Debug */, 328 | F44941C3204F99A4004ED29B /* Release */, 329 | ); 330 | defaultConfigurationIsVisible = 0; 331 | defaultConfigurationName = Release; 332 | }; 333 | F44941C4204F99A5004ED29B /* Build configuration list for PBXNativeTarget "gcdtest" */ = { 334 | isa = XCConfigurationList; 335 | buildConfigurations = ( 336 | F44941C5204F99A5004ED29B /* Debug */, 337 | F44941C6204F99A5004ED29B /* Release */, 338 | ); 339 | defaultConfigurationIsVisible = 0; 340 | defaultConfigurationName = Release; 341 | }; 342 | /* End XCConfigurationList section */ 343 | 344 | /* Begin XCVersionGroup section */ 345 | F44941B7204F99A4004ED29B /* gcdtest.xcdatamodeld */ = { 346 | isa = XCVersionGroup; 347 | children = ( 348 | F44941B8204F99A4004ED29B /* gcdtest.xcdatamodel */, 349 | ); 350 | currentVersion = F44941B8204F99A4004ED29B /* gcdtest.xcdatamodel */; 351 | path = gcdtest.xcdatamodeld; 352 | sourceTree = ""; 353 | versionGroupType = wrapper.xcdatamodel; 354 | }; 355 | /* End XCVersionGroup section */ 356 | }; 357 | rootObject = F44941A3204F99A4004ED29B /* Project object */; 358 | } 359 | -------------------------------------------------------------------------------- /gcdtest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /gcdtest.xcodeproj/project.xcworkspace/xcuserdata/dongxuwei.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrTung/GCD_Demo/caa8e4b750d1ab0fd04f8e4776b68b01335405a7/gcdtest.xcodeproj/project.xcworkspace/xcuserdata/dongxuwei.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /gcdtest.xcodeproj/xcuserdata/dongxuwei.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /gcdtest.xcodeproj/xcuserdata/dongxuwei.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | gcdtest.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /gcdtest.xcodeproj/xcuserdata/greatstar.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /gcdtest.xcodeproj/xcuserdata/greatstar.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | gcdtest.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /gcdtest/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // gcdtest 4 | // 5 | // Created by greatstar on 2018/3/7. 6 | // Copyright © 2018年 greatstar. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow *window; 15 | 16 | @property (readonly, strong) NSPersistentContainer *persistentContainer; 17 | 18 | - (void)saveContext; 19 | 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /gcdtest/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // gcdtest 4 | // 5 | // Created by greatstar on 2018/3/7. 6 | // Copyright © 2018年 greatstar. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | // Saves changes in the application's managed object context before the application terminates. 49 | [self saveContext]; 50 | } 51 | 52 | 53 | #pragma mark - Core Data stack 54 | 55 | @synthesize persistentContainer = _persistentContainer; 56 | 57 | - (NSPersistentContainer *)persistentContainer { 58 | // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it. 59 | @synchronized (self) { 60 | if (_persistentContainer == nil) { 61 | _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"gcdtest"]; 62 | [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) { 63 | if (error != nil) { 64 | // Replace this implementation with code to handle the error appropriately. 65 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 66 | 67 | /* 68 | Typical reasons for an error here include: 69 | * The parent directory does not exist, cannot be created, or disallows writing. 70 | * The persistent store is not accessible, due to permissions or data protection when the device is locked. 71 | * The device is out of space. 72 | * The store could not be migrated to the current model version. 73 | Check the error message to determine what the actual problem was. 74 | */ 75 | NSLog(@"Unresolved error %@, %@", error, error.userInfo); 76 | abort(); 77 | } 78 | }]; 79 | } 80 | } 81 | 82 | return _persistentContainer; 83 | } 84 | 85 | #pragma mark - Core Data Saving support 86 | 87 | - (void)saveContext { 88 | NSManagedObjectContext *context = self.persistentContainer.viewContext; 89 | NSError *error = nil; 90 | if ([context hasChanges] && ![context save:&error]) { 91 | // Replace this implementation with code to handle the error appropriately. 92 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 93 | NSLog(@"Unresolved error %@, %@", error, error.userInfo); 94 | abort(); 95 | } 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /gcdtest/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /gcdtest/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /gcdtest/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /gcdtest/DemoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoViewController.h 3 | // gcdtest 4 | // 5 | // Created by greatstar on 2018/3/10. 6 | // Copyright © 2018年 greatstar. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DemoViewController : UIViewController 12 | 13 | 14 | //测试方法名 15 | @property (nonatomic,copy) NSString *selectorStr; 16 | @end 17 | -------------------------------------------------------------------------------- /gcdtest/DemoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoViewController.m 3 | // gcdtest 4 | // 5 | // Created by greatstar on 2018/3/10. 6 | // Copyright © 2018年 greatstar. All rights reserved. 7 | // 8 | 9 | //ignore selector unknown warning 10 | #define SuppressPerformSelectorLeakWarning(Stuff) \ 11 | do { \ 12 | _Pragma("clang diagnostic push") \ 13 | _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ 14 | Stuff; \ 15 | _Pragma("clang diagnostic pop") \ 16 | } while (0) 17 | 18 | #import "DemoViewController.h" 19 | 20 | @interface DemoViewController () 21 | - (IBAction)runBtnHandler:(id)sender; 22 | 23 | @end 24 | 25 | @implementation DemoViewController 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | } 30 | 31 | #pragma mark --------队列的三种获取方式---------- 32 | 33 | //串行队列 34 | -(void)testSerialqueue{ 35 | 36 | // 串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 37 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 38 | 39 | //主队列其实也是一种特殊的串行队列 40 | //主队列的获取方法 41 | dispatch_queue_t mainqueue = dispatch_get_main_queue(); 42 | } 43 | 44 | //并发队列 45 | -(void)testConcurrentqueue{ 46 | 47 | //并发队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 48 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 49 | 50 | //系统提供了全局并发队列的直接获取方法:第一个参数表示队列优先级,我们选择默认的好了,第二个参数flags作为保留字段备用,一般都直接填0 51 | //全局并发队列的获取方法 52 | dispatch_queue_t mainqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 53 | } 54 | 55 | //主队列 56 | -(void)testMainqueue{ 57 | //主队列其实也是一种特殊的串行队列 58 | //主队列的获取方法 59 | dispatch_queue_t mainqueue = dispatch_get_main_queue(); 60 | } 61 | 62 | 63 | #pragma mark ---------任务的两种追加方式---------- 64 | 65 | //异步执行 66 | -(void)testAsync{ 67 | 68 | //获取一个队列,这里以并发队列作为例子 69 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 70 | 71 | // 异步执行任务创建方法,这句代码的意思是以异步操作将任务添加到队列(queue)中执行 72 | dispatch_async(queue, ^{ 73 | // 这里放异步执行任务代码 74 | }); 75 | } 76 | 77 | //同步执行 78 | -(void)testSync{ 79 | 80 | //获取一个队列,这里以并发队列作为例子 81 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 82 | 83 | // 异步执行任务创建方法,这句代码的意思是以同步操作将任务添加到队列(queue)中执行 84 | dispatch_sync(queue, ^{ 85 | // 这里放同步执行任务代码 86 | }); 87 | } 88 | 89 | 90 | #pragma mark ---------三种队列+两种执行方式组合起来的7种多线程常用使用姿势---------- 91 | 92 | //同步执行 and 并发队列 93 | //总结:只会在当前线程中依次执行任务,不会开启新线程,执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 94 | -(void)syncAndConcurrentqueue{ 95 | 96 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 97 | 98 | //下面提供两种并发队列的获取方式,其运行结果无差别,所以归为了一类,你可以自由选择 99 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 100 | 101 | // 全局并发队列 102 | // dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 103 | 104 | // 第一个任务 105 | dispatch_sync(queue, ^{ 106 | 107 | //这里线程暂停2秒,模拟一般的任务的耗时操作 108 | [NSThread sleepForTimeInterval:2]; 109 | 110 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 111 | }); 112 | 113 | // 第二个任务 114 | dispatch_sync(queue, ^{ 115 | 116 | //这里线程暂停2秒,模拟一般的任务的耗时操作 117 | [NSThread sleepForTimeInterval:2]; 118 | 119 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 120 | }); 121 | 122 | // 第三个任务 123 | dispatch_sync(queue, ^{ 124 | 125 | //这里线程暂停2秒,模拟一般的任务的耗时操作 126 | [NSThread sleepForTimeInterval:2]; 127 | 128 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 129 | }); 130 | 131 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 132 | 133 | } 134 | 135 | //异步执行 and 并发队列 136 | //总结:从log中可以发现,系统另外开启了3个线程,并且任务是同时执行的,并不是按照1>2>3顺序执行。所以异步+并发队列具备开启新线程的能力,且并发队列可开启多个线程,同时执行多个任务。 137 | -(void)asyncAndConcurrentqueue{ 138 | 139 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 140 | 141 | //下面提供两种并发队列的获取方式,其运行结果无差别,所以归为了一类,你可以自由选择 142 | 143 | //dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 144 | 145 | // 全局并发队列 146 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 147 | 148 | // 第一个任务 149 | dispatch_async(queue, ^{ 150 | 151 | //这里线程暂停2秒,模拟一般的任务的耗时操作 152 | [NSThread sleepForTimeInterval:2]; 153 | 154 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 155 | }); 156 | 157 | // 第二个任务 158 | dispatch_async(queue, ^{ 159 | 160 | //这里线程暂停2秒,模拟一般的任务的耗时操作 161 | [NSThread sleepForTimeInterval:2]; 162 | 163 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 164 | }); 165 | 166 | // 第三个任务 167 | dispatch_async(queue, ^{ 168 | 169 | //这里线程暂停2秒,模拟一般的任务的耗时操作 170 | [NSThread sleepForTimeInterval:2]; 171 | 172 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 173 | }); 174 | 175 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 176 | 177 | } 178 | 179 | //同步执行 and 串行队列 180 | //总结:只会在当前线程中依次执行任务,不会开启新线程,执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 181 | -(void)syncAndSerialqueue{ 182 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 183 | 184 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 185 | 186 | // 第一个任务 187 | dispatch_sync(queue, ^{ 188 | 189 | //这里线程暂停2秒,模拟一般的任务的耗时操作 190 | [NSThread sleepForTimeInterval:2]; 191 | 192 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 193 | }); 194 | 195 | // 第二个任务 196 | dispatch_sync(queue, ^{ 197 | 198 | //这里线程暂停2秒,模拟一般的任务的耗时操作 199 | [NSThread sleepForTimeInterval:2]; 200 | 201 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 202 | }); 203 | 204 | // 第三个任务 205 | dispatch_sync(queue, ^{ 206 | 207 | //这里线程暂停2秒,模拟一般的任务的耗时操作 208 | [NSThread sleepForTimeInterval:2]; 209 | 210 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 211 | }); 212 | 213 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 214 | 215 | } 216 | 217 | //异步执行 and 串行队列 218 | //总结:开启了一条新线程,异步执行具备开启新线程的能力且只开启一个线程,在该线程中执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 219 | -(void)asyncAndSerialqueue{ 220 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 221 | 222 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 223 | 224 | // 第一个任务 225 | dispatch_async(queue, ^{ 226 | 227 | //这里线程暂停2秒,模拟一般的任务的耗时操作 228 | [NSThread sleepForTimeInterval:2]; 229 | 230 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 231 | }); 232 | 233 | // 第二个任务 234 | dispatch_async(queue, ^{ 235 | 236 | //这里线程暂停2秒,模拟一般的任务的耗时操作 237 | [NSThread sleepForTimeInterval:2]; 238 | 239 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 240 | }); 241 | 242 | // 第三个任务 243 | dispatch_async(queue, ^{ 244 | 245 | //这里线程暂停2秒,模拟一般的任务的耗时操作 246 | [NSThread sleepForTimeInterval:2]; 247 | 248 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 249 | }); 250 | 251 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 252 | 253 | } 254 | 255 | //同步执行 and 主队列 256 | //总结:直接crash。这是因为发生了死锁,在gcd中,禁止在主队列(串行队列)中再以同步操作执行主队列任务。同理,在同一个同步串行队列中,再使用该队列同步执行任务也是会发生死锁。 257 | -(void)syncAndMainqueue{ 258 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 259 | 260 | //获取主队列 261 | dispatch_queue_t queue = dispatch_get_main_queue(); 262 | 263 | // 第一个任务 264 | dispatch_sync(queue, ^{ 265 | 266 | //这里线程暂停2秒,模拟一般的任务的耗时操作 267 | [NSThread sleepForTimeInterval:2]; 268 | 269 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 270 | }); 271 | 272 | // 第二个任务 273 | dispatch_sync(queue, ^{ 274 | 275 | //这里线程暂停2秒,模拟一般的任务的耗时操作 276 | [NSThread sleepForTimeInterval:2]; 277 | 278 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 279 | }); 280 | 281 | // 第三个任务 282 | dispatch_sync(queue, ^{ 283 | 284 | //这里线程暂停2秒,模拟一般的任务的耗时操作 285 | [NSThread sleepForTimeInterval:2]; 286 | 287 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 288 | }); 289 | 290 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 291 | 292 | return; 293 | //下面的例子类似: 294 | dispatch_queue_t queue1 = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 295 | 296 | dispatch_sync(queue1, ^{ 297 | 298 | [NSThread sleepForTimeInterval:2]; 299 | 300 | NSLog(@"----11111-----当前线程%@",[NSThread currentThread]);//到这里就死锁了 301 | 302 | dispatch_sync(queue1, ^{ 303 | 304 | [NSThread sleepForTimeInterval:2]; 305 | 306 | NSLog(@"----22222---当前线程%@",[NSThread currentThread]); 307 | }); 308 | 309 | NSLog(@"----333333-----当前线程%@",[NSThread currentThread]); 310 | 311 | }); 312 | NSLog(@"----44444-----当前线程%@",[NSThread currentThread]); 313 | 314 | } 315 | 316 | //其它线程中(同步执行 and 主队列) 317 | //总结:可以执行任务,所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中),在主线程中执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 318 | -(void)othersyncAndMainqueue{ 319 | 320 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 321 | 322 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 323 | 324 | // 第一个任务 325 | dispatch_async(queue, ^{ 326 | 327 | NSLog(@"----执行任务---%@",[NSThread currentThread]); 328 | 329 | //获取主队列 330 | dispatch_queue_t queue = dispatch_get_main_queue(); 331 | 332 | // 第一个任务 333 | dispatch_sync(queue, ^{ 334 | 335 | //这里线程暂停2秒,模拟一般的任务的耗时操作 336 | [NSThread sleepForTimeInterval:2]; 337 | 338 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 339 | }); 340 | }); 341 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 342 | 343 | } 344 | 345 | 346 | //异步执行 and 主队列 347 | //总结:所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中),在主线程中执行完一个任务,再执行下一个任务,按照1>2>3顺序执行,遵循FIFO原则。 348 | -(void)asyncAndMainqueue{ 349 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 350 | 351 | //获取主队列 352 | dispatch_queue_t queue = dispatch_get_main_queue(); 353 | 354 | // 第一个任务 355 | dispatch_async(queue, ^{ 356 | 357 | //这里线程暂停2秒,模拟一般的任务的耗时操作 358 | [NSThread sleepForTimeInterval:2]; 359 | 360 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 361 | }); 362 | 363 | // 第二个任务 364 | dispatch_async(queue, ^{ 365 | 366 | //这里线程暂停2秒,模拟一般的任务的耗时操作 367 | [NSThread sleepForTimeInterval:2]; 368 | 369 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 370 | }); 371 | 372 | // 第三个任务 373 | dispatch_async(queue, ^{ 374 | 375 | //这里线程暂停2秒,模拟一般的任务的耗时操作 376 | [NSThread sleepForTimeInterval:2]; 377 | 378 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 379 | }); 380 | 381 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 382 | 383 | } 384 | 385 | #pragma mark ---------GCD其它常用API---------- 386 | 387 | //各种队列的获取方法 388 | -(void)getQueue{ 389 | 390 | //主队列的获取方法:主队列是串行队列,主队列中的任务都将在主线程中执行 391 | dispatch_queue_t mainqueue = dispatch_get_main_queue(); 392 | 393 | //串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 394 | dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 395 | 396 | //并发队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 397 | dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 398 | 399 | //全局并发队列的获取方法:第一个参数表示队列优先级,我们选择默认的好了,第二个参数flags作为保留字段备用,一般都直接填0 400 | dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 401 | } 402 | 403 | //自定义队列的创建方法 404 | -(void)queue_create{ 405 | //串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 406 | dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 407 | 408 | //并发队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 409 | dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 410 | } 411 | 412 | //使用dispatch_set_target_queue更改Dispatch Queue的执行优先级 413 | -(void)testTargetQueue1{ 414 | 415 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 416 | 417 | //串行队列的创建方法:第一个参数表示队列的唯一标识,第二个参数用来识别是串行队列还是并发队列(若为NULL时,默认是DISPATCH_QUEUE_SERIAL) 418 | dispatch_queue_t seriaQueue = dispatch_queue_create("com.test.testQueue", NULL); 419 | 420 | //指定一个任务 421 | dispatch_async(seriaQueue, ^{ 422 | 423 | //这里线程暂停2秒,模拟一般的任务的耗时操作 424 | [NSThread sleepForTimeInterval:2]; 425 | 426 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 427 | }); 428 | 429 | //全局并发队列的获取方法:第一个参数表示队列优先级,我们选择默认的好了,第二个参数flags作为保留字段备用,一般都直接填0 430 | dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 431 | 432 | //指定一个任务 433 | dispatch_async(globalQueue, ^{ 434 | 435 | //这里线程暂停2秒,模拟一般的任务的耗时操作 436 | [NSThread sleepForTimeInterval:2]; 437 | 438 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 439 | }); 440 | 441 | 442 | //第一个参数为要设置优先级的queue,第二个参数是参照物,即将第一个queue的优先级和第二个queue的优先级设置一样。 443 | //第一个参数如果是系统提供的【主队列】或【全局队列】,则不知道会出现什么情况,因此最好不要设置第一参数为系统提供的队列 444 | dispatch_set_target_queue(seriaQueue,globalQueue); 445 | 446 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 447 | 448 | } 449 | 450 | //dispatch_set_target_queue除了能用来设置队列的优先级之外,还能够创建队列的层次体系,当我们想让不同队列中的任务同步的执行时,我们可以创建一个串行队列,然后将这些队列的target指向新创建的队列即可。 451 | - (void)testTargetQueue2 { 452 | 453 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 454 | 455 | dispatch_queue_t targetQueue = dispatch_queue_create("com.test.target_queue", DISPATCH_QUEUE_SERIAL); 456 | 457 | dispatch_queue_t queue1 = dispatch_queue_create("com.test.queue1", DISPATCH_QUEUE_SERIAL); 458 | 459 | dispatch_queue_t queue2 = dispatch_queue_create("com.test.queue2", DISPATCH_QUEUE_CONCURRENT); 460 | 461 | dispatch_set_target_queue(queue1, targetQueue); 462 | 463 | dispatch_set_target_queue(queue2, targetQueue); 464 | 465 | //指定一个异步任务 466 | dispatch_async(queue1, ^{ 467 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 468 | [NSThread sleepForTimeInterval:2]; 469 | }); 470 | 471 | //指定一个异步任务 472 | dispatch_async(queue2, ^{ 473 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 474 | [NSThread sleepForTimeInterval:2]; 475 | }); 476 | 477 | //指定一个异步任务 478 | dispatch_async(queue2, ^{ 479 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 480 | [NSThread sleepForTimeInterval:2]; 481 | }); 482 | 483 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 484 | 485 | } 486 | 487 | //延时执行,需要注意的是:dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的。 488 | -(void)dispatch_after{ 489 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 490 | 491 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 492 | // 2秒后异步追加任务代码到主队列等待执行 493 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 494 | }); 495 | 496 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 497 | 498 | } 499 | 500 | //只执行一次,通常在创建单例时使用,多线程环境下也能保证线程安全 501 | -(void)dispatch_once_1{ 502 | 503 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 504 | 505 | static dispatch_once_t onceToken; 506 | dispatch_once(&onceToken, ^{ 507 | NSLog(@"----只执行一次的任务---当前线程%@",[NSThread currentThread]); 508 | }); 509 | 510 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 511 | 512 | } 513 | 514 | //快速遍历方法,可以替代for循环的函数。dispatch_apply按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。 515 | //会创建新的线程,并发执行 516 | -(void)dispatch_apply{ 517 | 518 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 519 | 520 | dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 521 | 522 | dispatch_apply(100, globalQueue, ^(size_t index) { 523 | NSLog(@"执行第%zd次的任务---%@",index, [NSThread currentThread]); 524 | }); 525 | 526 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 527 | 528 | } 529 | 530 | //队列组:当我们遇到需要异步下载3张图片,都下载完之后再拼接成一个整图的时候,就需要用到gcd队列组。 531 | -(void)dispatch_group{ 532 | 533 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 534 | 535 | dispatch_group_t group = dispatch_group_create(); 536 | 537 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 538 | // 第一个任务 539 | [NSThread sleepForTimeInterval:2]; 540 | 541 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 542 | 543 | }); 544 | 545 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 546 | // 第二个任务 547 | [NSThread sleepForTimeInterval:2]; 548 | 549 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 550 | }); 551 | 552 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 553 | 554 | // 第三个任务 555 | [NSThread sleepForTimeInterval:2]; 556 | 557 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 558 | }); 559 | 560 | 561 | dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 562 | [NSThread sleepForTimeInterval:2]; 563 | 564 | NSLog(@"----执行最后的汇总任务---当前线程%@",[NSThread currentThread]); 565 | }); 566 | 567 | //若想执行完上面的任务再走下面这行代码可以加上下面这句代码 568 | 569 | // 等待上面的任务全部完成后,往下继续执行(会阻塞当前线程) 570 | // dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 571 | 572 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 573 | } 574 | 575 | //dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1 576 | //dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。 577 | //当 group 中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。 578 | -(void)dispatch_group_1{ 579 | 580 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 581 | 582 | dispatch_group_t group = dispatch_group_create(); 583 | 584 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 585 | 586 | dispatch_group_enter(group); 587 | dispatch_async(queue, ^{ 588 | // 第一个任务 589 | [NSThread sleepForTimeInterval:2]; 590 | 591 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 592 | 593 | dispatch_group_leave(group); 594 | }); 595 | 596 | dispatch_group_enter(group); 597 | dispatch_async(queue, ^{ 598 | // 第二个任务 599 | [NSThread sleepForTimeInterval:2]; 600 | 601 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 602 | 603 | dispatch_group_leave(group); 604 | }); 605 | 606 | dispatch_group_enter(group); 607 | dispatch_async(queue, ^{ 608 | // 第三个任务 609 | [NSThread sleepForTimeInterval:2]; 610 | 611 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 612 | 613 | dispatch_group_leave(group); 614 | }); 615 | 616 | dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 617 | 618 | [NSThread sleepForTimeInterval:2]; 619 | NSLog(@"----执行最后的汇总任务---当前线程%@",[NSThread currentThread]); 620 | }); 621 | 622 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 623 | 624 | } 625 | 626 | //信号量 627 | //总结:信号量设置的是2,在当前场景下,同一时间内执行的线程就不会超过2,先执行2个线程,等执行完一个,下一个会开始执行。 628 | -(void)dispatch_semaphore{ 629 | 630 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 631 | 632 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); 633 | 634 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 635 | 636 | //任务1 637 | dispatch_async(queue, ^{ 638 | 639 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 640 | 641 | NSLog(@"----开始执行第一个任务---当前线程%@",[NSThread currentThread]); 642 | 643 | [NSThread sleepForTimeInterval:2]; 644 | 645 | NSLog(@"----结束执行第一个任务---当前线程%@",[NSThread currentThread]); 646 | 647 | dispatch_semaphore_signal(semaphore); 648 | }); 649 | 650 | //任务2 651 | dispatch_async(queue, ^{ 652 | 653 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 654 | 655 | NSLog(@"----开始执行第二个任务---当前线程%@",[NSThread currentThread]); 656 | 657 | [NSThread sleepForTimeInterval:1]; 658 | 659 | NSLog(@"----结束执行第二个任务---当前线程%@",[NSThread currentThread]); 660 | 661 | dispatch_semaphore_signal(semaphore); 662 | }); 663 | 664 | //任务3 665 | dispatch_async(queue, ^{ 666 | 667 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 668 | 669 | NSLog(@"----开始执行第三个任务---当前线程%@",[NSThread currentThread]); 670 | 671 | [NSThread sleepForTimeInterval:2]; 672 | 673 | NSLog(@"----结束执行第三个任务---当前线程%@",[NSThread currentThread]); 674 | 675 | dispatch_semaphore_signal(semaphore); 676 | }); 677 | 678 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 679 | 680 | } 681 | 682 | 683 | //在读取较大的文件时,如果将文件分成合适的大小并使用 Global Dispatch Queue 并列读取的话,应该会比一般的读取速度快不少。 在 GCD 当中能实现这一功能的就是 Dispatch I/O 和 Dispatch Data。 684 | -(void)dispatch_IO{ 685 | 686 | //一般情况下可以这样异步去读取数据 687 | // dispatch_async(queue, ^{ /* 读取 0 ~ 8080 字节*/ }); 688 | // dispatch_async(queue, ^{ /* 读取 8081 ~ 16383 字节*/ }); 689 | // dispatch_async(queue, ^{ /* 读取 16384 ~ 24575 字节*/ }); 690 | // dispatch_async(queue, ^{ /* 读取 24576 ~ 32767 字节*/ }); 691 | // dispatch_async(queue, ^{ /* 读取 32768 ~ 40959 字节*/ }); 692 | // dispatch_async(queue, ^{ /* 读取 40960 ~ 49191 字节*/ }); 693 | // dispatch_async(queue, ^{ /* 读取 49192 ~ 57343 字节*/ }); 694 | // dispatch_async(queue, ^{ /* 读取 57344 ~ 65535 字节*/ }); 695 | 696 | 697 | //下面是GCD提供的更加便捷的操作姿势 698 | 699 | int fd = 0, fdpair[2]; 700 | dispatch_queue_t pipe_q; 701 | dispatch_io_t pipe_channel; 702 | dispatch_semaphore_t sem; 703 | /* ..... 此处省略若干代码.....*/ 704 | 705 | // 创建串行队列 706 | pipe_q = dispatch_queue_create("PipeQ", NULL); 707 | // 创建 Dispatch I/O 708 | 709 | pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){ 710 | close(fd); 711 | }); 712 | 713 | // 该函数设定一次读取的大小(分割大小) 714 | dispatch_io_set_low_water(pipe_channel, SIZE_MAX); 715 | 716 | dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){ 717 | if (err == 0) // err等于0 说明读取无误 718 | { 719 | // 读取完“单个文件块”的大小 720 | size_t len = dispatch_data_get_size(pipedata); 721 | if (len > 0) 722 | { 723 | // // 定义一个字节数组bytes 724 | // const charchar *bytes = NULL; 725 | // charchar *encoded; 726 | // 727 | // dispatch_data_t md = dispatch_data_create_map(pipedata, (const voidvoid **)&bytes, &len); 728 | // encoded = asl_core_encode_buffer(bytes, len); 729 | // asl_set((aslmsg)merged_msg, ASL_KEY_AUX_DATA, encoded); 730 | // free(encoded); 731 | // _asl_send_message(NULL, merged_msg, -1, NULL); 732 | // asl_msg_release(merged_msg); 733 | // dispatch_release(md); 734 | } 735 | } 736 | if (done) 737 | { 738 | dispatch_semaphore_signal(sem); 739 | } 740 | }); 741 | } 742 | 743 | 744 | //隔断方法:当前面的写入操作全部完成之后,再执行后面的读取任务。当然也可以用Dispatch Group和dispatch_set_target_queue,只是比较而言,dispatch_barrier_async会更加顺滑 745 | -(void)dispatch_barrier_async{ 746 | 747 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 748 | 749 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT); 750 | 751 | dispatch_async(queue, ^{ 752 | // 第一个写入任务 753 | [NSThread sleepForTimeInterval:3]; 754 | 755 | NSLog(@"----执行第一个写入任务---当前线程%@",[NSThread currentThread]); 756 | 757 | }); 758 | dispatch_async(queue, ^{ 759 | // 第二个写入任务 760 | [NSThread sleepForTimeInterval:1]; 761 | 762 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 763 | 764 | }); 765 | 766 | dispatch_barrier_async(queue, ^{ 767 | // 等待处理 768 | [NSThread sleepForTimeInterval:2]; 769 | 770 | NSLog(@"----等待前面的任务完成---当前线程%@",[NSThread currentThread]); 771 | 772 | }); 773 | 774 | dispatch_async(queue, ^{ 775 | // 第一个读取任务 776 | [NSThread sleepForTimeInterval:2]; 777 | 778 | NSLog(@"----执行第一个读取任务---当前线程%@",[NSThread currentThread]); 779 | 780 | }); 781 | dispatch_async(queue, ^{ 782 | // 第二个读取任务 783 | [NSThread sleepForTimeInterval:2]; 784 | 785 | NSLog(@"----执行第二个读取任务---当前线程%@",[NSThread currentThread]); 786 | 787 | }); 788 | 789 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 790 | 791 | } 792 | 793 | //场景:当追加大量处理到Dispatch Queue时,在追加处理的过程中,有时希望不执行已追加的处理。例如演算结果被Block截获时,一些处理会对这个演算结果造成影响。在这种情况下,只要挂起Dispatch Queue即可。当可以执行时再恢复。 794 | //总结:dispatch_suspend,dispatch_resume提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的任务,也就是如果挂起之前已经有队列中的任务在进行中,那么该任务依然会被执行完毕 795 | -(void)dispatch_suspend{ 796 | 797 | NSLog(@"----start-----当前线程---%@",[NSThread currentThread]); 798 | 799 | dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_SERIAL); 800 | 801 | dispatch_async(queue, ^{ 802 | // 执行第一个任务 803 | [NSThread sleepForTimeInterval:5]; 804 | 805 | NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]); 806 | 807 | }); 808 | 809 | dispatch_async(queue, ^{ 810 | // 执行第二个任务 811 | [NSThread sleepForTimeInterval:5]; 812 | 813 | NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]); 814 | 815 | }); 816 | 817 | dispatch_async(queue, ^{ 818 | // 执行第三个任务 819 | [NSThread sleepForTimeInterval:5]; 820 | 821 | NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]); 822 | }); 823 | 824 | //此时发现意外情况,挂起队列 825 | NSLog(@"suspend"); 826 | dispatch_suspend(queue); 827 | 828 | //挂起10秒之后,恢复正常 829 | [NSThread sleepForTimeInterval:10]; 830 | 831 | //恢复队列 832 | NSLog(@"resume"); 833 | dispatch_resume(queue); 834 | 835 | NSLog(@"----end-----当前线程---%@",[NSThread currentThread]); 836 | 837 | } 838 | 839 | - (IBAction)runBtnHandler:(id)sender { 840 | SEL selector = NSSelectorFromString(self.selectorStr); 841 | if([self respondsToSelector:selector]){ 842 | SuppressPerformSelectorLeakWarning([self performSelector:selector withObject:nil]); 843 | } 844 | } 845 | @end 846 | -------------------------------------------------------------------------------- /gcdtest/DemoViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 30 | 31 | 32 | 33 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /gcdtest/GCDTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCDTableViewController.h 3 | // gcdtest 4 | // 5 | // Created by greatstar on 2018/3/10. 6 | // Copyright © 2018年 greatstar. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GCDTableViewController : UITableViewController 12 | 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /gcdtest/GCDTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GCDTableViewController.m 3 | // gcdtest 4 | // 5 | // Created by greatstar on 2018/3/10. 6 | // Copyright © 2018年 greatstar. All rights reserved. 7 | // 8 | 9 | #import "GCDTableViewController.h" 10 | #import "DemoViewController.h" 11 | 12 | 13 | 14 | @interface GCDTableViewController () 15 | 16 | 17 | @property (nonatomic,strong) NSArray *dataProvider; 18 | @end 19 | 20 | @implementation GCDTableViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | self.dataProvider = @[@{@"title" : @"队列",@"children":@[@{@"title" : @"串行队列",@"children":@"",@"selector":@"testSerialqueue"}, 26 | @{@"title" : @"并发队列",@"children":@"",@"selector":@"testConcurrentqueue"}, 27 | @{@"title" : @"主队列",@"children":@"",@"selector":@"testMainqueue"}]}, 28 | @{@"title" : @"任务的追加方式",@"children":@[@{@"title" : @"同步执行",@"children":@"",@"selector":@"testSync"}, 29 | @{@"title" : @"异步执行",@"children":@"",@"selector":@"testAsync"}]}, 30 | @{@"title" : @"队列+任务组合",@"children":@[@{@"title" : @"同步执行 and 并发队列",@"children":@"",@"selector":@"syncAndConcurrentqueue"}, 31 | @{@"title" : @"异步执行 and 并发队列",@"children":@"",@"selector":@"asyncAndConcurrentqueue"}, 32 | @{@"title" : @"同步执行 and 串行队列",@"children":@"",@"selector":@"syncAndSerialqueue"}, 33 | @{@"title" : @"异步执行 and 串行队列",@"children":@"",@"selector":@"asyncAndSerialqueue"}, 34 | @{@"title" : @"同步执行 & 主队列(在主线程中会crash)",@"children":@"",@"selector":@"syncAndMainqueue"}, 35 | @{@"title" : @"同步执行 & 主队列(在其它线程中)",@"children":@"",@"selector":@"othersyncAndMainqueue"}, 36 | @{@"title" : @"异步执行 and 主队列",@"children":@"",@"selector":@"asyncAndMainqueue"}]}, 37 | @{@"title" : @"GCD其它常用API",@"children":@[ 38 | @{@"title" : @"Dispatch Queue",@"children":@"",@"selector":@"getQueue"}, 39 | @{@"title" : @"dispatch_queue_creat",@"children":@"",@"selector":@"queue_create"}, 40 | @{@"title" : @"dispatch_set_target_queue",@"children":@"",@"selector":@"testTargetQueue1"}, 41 | @{@"title" : @"dispatch_after",@"children":@"",@"selector":@"dispatch_after"}, 42 | @{@"title" : @"dispatch_once",@"children":@"",@"selector":@"dispatch_once_1"}, 43 | @{@"title" : @"dispatch_apply",@"children":@"",@"selector":@"dispatch_apply"}, 44 | @{@"title" : @"dispatch_group",@"children":@"",@"selector":@"dispatch_group"}, 45 | @{@"title" : @"dispatch_semaphore",@"children":@"",@"selector":@"dispatch_semaphore"}, 46 | @{@"title" : @"Dispatch I/O",@"children":@"",@"selector":@"dispatch_IO"}, 47 | @{@"title" : @"dispatch_barrier_async",@"children":@"",@"selector":@"dispatch_barrier_async"}, 48 | @{@"title" : @"dispatch_suspend/dispatchp_resume",@"children":@"",@"selector":@"dispatch_suspend"},]}, 49 | ]; 50 | 51 | self.tableView.tableFooterView = [UIView new]; 52 | } 53 | 54 | #pragma mark - Table view data source 55 | 56 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 57 | return self.dataProvider.count; 58 | } 59 | 60 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 61 | NSArray *arr = [self.dataProvider[section] objectForKey:@"children"]; 62 | return arr.count; 63 | } 64 | 65 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 66 | 67 | static NSString *cellIdentifier = @"commonCell"; 68 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 69 | if (cell == nil) { 70 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 71 | } 72 | 73 | NSArray *arr = [self.dataProvider[indexPath.section] objectForKey:@"children"]; 74 | NSDictionary *dict = arr[indexPath.row]; 75 | cell.textLabel.text = [dict objectForKey:@"title"]; 76 | cell.textLabel.font = [UIFont systemFontOfSize:14]; 77 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 78 | 79 | return cell; 80 | } 81 | 82 | -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ 83 | return [self.dataProvider[section] objectForKey:@"title"]; 84 | } 85 | 86 | -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 87 | return 44; 88 | } 89 | 90 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 91 | 92 | DemoViewController *vc = [[DemoViewController alloc] init]; 93 | NSArray *arr = [self.dataProvider[indexPath.section] objectForKey:@"children"]; 94 | vc.selectorStr = [arr[indexPath.row] objectForKey:@"selector"]; 95 | [self.navigationController pushViewController:vc animated:YES]; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /gcdtest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /gcdtest/data.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title 7 | 队列 8 | children 9 | 10 | 11 | selector 12 | testSerialqueue 13 | title 14 | 串行队列 15 | 16 | 17 | selector 18 | testConcurrentqueue 19 | title 20 | 并发队列 21 | 22 | 23 | selector 24 | testMainqueue 25 | title 26 | 主队列 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /gcdtest/gcdtest.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | gcdtest.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /gcdtest/gcdtest.xcdatamodeld/gcdtest.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /gcdtest/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // gcdtest 4 | // 5 | // Created by greatstar on 2018/3/7. 6 | // Copyright © 2018年 greatstar. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------