NSThread、NSOperation、GCD 總結:
無論使用哪種方法進行多線程開發,每個線程啟動后并不一定立即執行相應的操作,具體什么時候由系統調度(CPU 空閑時就會執行)
更新 UI 應該在主線程(UI 線程)中進行,并且推薦使用同步調用,常用的方法如下:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL) wait; //方法傳遞主線程[NSThread mainThread])
[NSOperationQueue mainQueue] addOperationWithBlock:
dispatch_sync(dispatch_get_main_queue(), ^{})
NSThread 適合輕量級多線程開發,控制線程順序比較難,同時線程總數無法控制(每次創建并不能重用之前的線程,只能創建一個新的線程)
對于簡單的多線程開發建議使用 NSObject 的擴展方法完成,而不必使用 NSThread
可以使用 NSThread 的 currentThread 方法取得當前線程,使用 sleepForTimeInterval: 方法讓當前線程掛起
NSOperation 進行多線程開發可以控制線程總數及線程依賴關系
創建一個 NSOperation 不應該直接調用 start 方法(如果直接 start 則會在主線程中調用)而是應該放到 NSOperationQueue 中啟動
相比 NSInvocationOperation 推薦使用 NSBlockOperation,代碼簡單,同時由于閉包性使他沒有傳參問題
NSOperation 是對 GCD 面向對象的 OC 封裝。而 GCD 基于 C 語言開發,效率更高。建議如果任務之間有依賴關系或者想要監聽任務完成狀態的情況下優先選擇 NSOperation,否則使用 GCD
在 GCD 中串行隊列中的任務被安排到一個單一線程執行(不是主線程),可以方便地控制執行順序;并發隊列在多個線程中執行(前提是使用異步方法),順序控制相對復雜,但是更高效
在 GCD 中一個操作是多線程還是單線程執行,取決于當前隊列類型和執行方法。只有隊列類型為并行隊列并且使用異步方法執行時才能在多個線程中執行(如果是并行隊列使用同步方法調用則會在主線程中執行)
效果如下:
ViewController.h
1 #import <UIKit/UIKit.h>2 3 @interface ViewController : UITableViewController4 @PRoperty (copy, nonatomic) NSArray *arrSampleName;5 6 - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName;7 8 @end
ViewController.m
1 #import "ViewController.h" 2 #import "FirstSampleViewController.h" 3 #import "SecondSampleViewController.h" 4 5 @interface ViewController () 6 - (void)layoutUI; 7 @end 8 9 @implementation ViewController10 - (void)viewDidLoad {11 [super viewDidLoad];12 13 [self layoutUI];14 }15 16 - (void)didReceiveMemoryWarning {17 [super didReceiveMemoryWarning];18 // Dispose of any resources that can be recreated.19 }20 21 - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName {22 if (self = [super initWithStyle:UITableViewStyleGrouped]) {23 self.navigationItem.title = @"多線程開發之三 GCD";24 self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回首頁" style:UIBarButtonItemStylePlain target:nil action:nil];25 26 _arrSampleName = arrSampleName;27 }28 return self;29 }30 31 - (void)layoutUI {32 }33 34 #pragma mark - UITableViewController相關方法重寫35 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {36 return 0.1;37 }38 39 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {40 return 1;41 }42 43 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {44 return [_arrSampleName count];45 }46 47 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {48 static NSString *cellIdentifier = @"cell";49 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];50 if (!cell) {51 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];52 }53 cell.textLabel.text = _arrSampleName[indexPath.row];54 return cell;55 }56 57 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {58 switch (indexPath.row) {59 case 0: {60 FirstSampleViewController *firstSampleVC = [FirstSampleViewController new];61 [self.navigationController pushViewController:firstSampleVC animated:YES];62 break;63 }64 case 1: {65 SecondSampleViewController *secondSampleVC = [SecondSampleViewController new];66 [self.navigationController pushViewController:secondSampleVC animated:YES];67 break;68 }69 default:70 break;71 }72 }73 74 @end
UIImage+RescaleImage.h
1 #import <UIKit/UIKit.h> 2 3 @interface UIImage (RescaleImage) 4 /** 5 * 根據寬高大小,獲取對應的縮放圖片 6 * 7 * @param size 寬高大小 8 * 9 * @return 對應的縮放圖片10 */11 - (UIImage *)rescaleImageToSize:(CGSize)size;12 13 @end
UIImage+RescaleImage.m
1 #import "UIImage+RescaleImage.h" 2 3 @implementation UIImage (RescaleImage) 4 5 - (UIImage *)rescaleImageToSize:(CGSize)size { 6 CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height); 7 8 UIGraphicsBeginImageContext(rect.size); 9 [self drawInRect:rect];10 UIImage *imgScale = UIGraphicsGetImageFromCurrentImageContext();11 UIGraphicsEndImageContext();12 13 return imgScale;14 }15 16 @end
Common.h
1 #import <Foundation/Foundation.h>2 3 @interface Common : NSObject4 + (NSURL *)randomImageURL;5 6 @end
Common.m
1 #import "Common.h" 2 3 static NSArray *arrPartOfURL = nil; 4 @implementation Common 5 6 + (NSURL *)randomImageURL { 7 if (arrPartOfURL == nil) { 8 arrPartOfURL = @[ 9 @"chronograph",10 @"color",11 @"modular",12 @"utility",13 @"mickey_mouse",14 @"simple",15 @"motion",16 @"solar",17 @"astronomy",18 @"timer",19 @"alarm",20 @"world_clock"21 ];22 }23 24 NSUInteger randomVal = (arc4random() % 12); //0-11的隨機數25 NSString *imageURLStr = [NSString stringWithFormat:@"http://images.apple.com/v/watch/e/timekeeping/images/%@_large.jpg", arrPartOfURL[randomVal]];26 return [NSURL URLWithString:imageURLStr];27 }28 29 @end
FirstSampleViewController.h
1 #import <UIKit/UIKit.h>2 3 @interface FirstSampleViewController : UIViewController4 @property (assign, nonatomic) CGSize rescaleImageSize;5 6 @property (strong, nonatomic) IBOutlet UIImageView *imgV;7 @property (strong, nonatomic) IBOutlet UIButton *btnLoadImage;8 9 @end
FirstSampleViewController.m
1 #import "FirstSampleViewController.h" 2 #import "UIImage+RescaleImage.h" 3 #import "Common.h" 4 5 @interface FirstSampleViewController () 6 - (void)layoutUI; 7 - (void)updateImage:(NSData *)imageData; 8 @end 9 10 @implementation FirstSampleViewController 11 12 - (void)viewDidLoad { 13 [super viewDidLoad]; 14 15 [self layoutUI]; 16 } 17 18 - (void)didReceiveMemoryWarning { 19 [super didReceiveMemoryWarning]; 20 // Dispose of any resources that can be recreated. 21 } 22 23 - (void)dealloc { 24 _imgV.image = nil; 25 } 26 27 - (void)layoutUI { 28 self.view.backgroundColor = [UIColor colorWithWhite:0.957 alpha:1.000]; 29 30 CGFloat width = [[UIScreen mainScreen] bounds].size.width; //bounds 返回整個屏幕大??;applicationFrame 返回去除狀態欄后的屏幕大小 31 CGFloat height = width * 243.0 / 221.0; 32 const CGFloat percentVal = 3.0 / 4.0; //以屏幕寬度的3/4比例顯示 33 _rescaleImageSize = CGSizeMake(width * percentVal, height * percentVal); 34 35 //NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"]; 36 //_imgV.image = [UIImage imageWithContentsOfFile:path]; 37 38 _btnLoadImage.tintColor = [UIColor darkGrayColor]; 39 _btnLoadImage.layer.masksToBounds = YES; 40 _btnLoadImage.layer.cornerRadius = 10.0; 41 _btnLoadImage.layer.borderColor = [UIColor grayColor].CGColor; 42 _btnLoadImage.layer.borderWidth = 1.0; 43 } 44 45 - (void)updateImage:(NSData *)imageData { 46 UIImage *img = [UIImage imageWithData:imageData]; 47 _imgV.image = [img rescaleImageToSize:_rescaleImageSize]; 48 } 49 50 - (IBAction)loadImage:(id)sender { 51 /* 52 GCD(Grand Central Dispatch):中央調度 53 相比 NSThread 和 NSOperation 來說,他的顯著優點是:對于多核運算更加有效 54 1、GCD 的一個重要概念是隊列,他的核心理念:將多個任務添加到 dispatch queue(調度隊列)中,系統會為我們管理這些 dispath queue,為我們在多個線程上分配并執行任務,我們不需要直接啟動和管理后臺線程。 55 2、系統提供了許多預定義的 dispatch queue,包括可以保證始終在主線程上執行工作的 dispatch queue。也可以創建自己的dispatch queue,而且可以創建任意多個。GCD的 dispatch queue 嚴格遵循 FIFO(先進先出)原則,添加到 dispatch queue 的任務將始終按照加入 dispatch queue 的順序啟動。 56 3、dispatch queue按先進先出的順序,串行或并發地執行任務 57 1> serial dispatch queue(串行調度隊列):一次只能執行一個任務,當前任務完成后才開始出列并啟動下一個任務 58 2> concurrent dispatch queue(并發調度隊列):則盡可能多地啟動任務并發執行 59 60 同步和異步決定了要不要開啟新的線程 61 同步:在當前線程中執行任務,不具備開啟新線程的能力(如果當前線程是主線程的話,會造成線程阻塞,一般比較少用) 62 異步:在新的線程中執行任務,具備開啟新線程的能力 63 64 串行和并發決定了任務的執行方式 65 串行:一個任務執行完畢后,再執行下一個任務 66 并發:多個任務并發(同時)執行 67 */ 68 69 /* 70 系統給每個應用提供了四個全局并發隊列,整個應用內全局共享,他們的區別是通過「全局并發隊列優先級」;優先級越高,隊列越先被列入執行計劃中 71 #define DISPATCH_QUEUE_PRIORITY_HIGH 2 72 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 73 #define DISPATCH_QUEUE_PRIORITY_LOW (-2) 74 #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 75 */ 76 77 //全局并發隊列 78 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 79 80 //自定義串行隊列;第二個參數為 NULL 或 DISPATCH_QUEUE_SERIAL 表示串行隊列,而為 DISPATCH_QUEUE_CONCURRENT 表示并發隊列,但一般用 dispatch_get_global_queue 方法獲取并發隊列 81 //dispatch_queue_t customSerialQueue = dispatch_queue_create("Custom Queue", NULL); 82 83 //主隊列;屬于串行隊列 84 dispatch_queue_t mainQueue = dispatch_get_main_queue(); 85 86 87 //方法一:自定義串行隊列同步:不會開啟新線程、串行執行任務 88 // dispatch_sync(customSerialQueue, ^{ 89 // NSLog(@"自定義串行隊列同步,線程:%@", [NSThread currentThread]); //所在的線程,這里就是在主線程 90 // NSURL *url = [Common randomImageURL]; 91 // NSData *data = [NSData dataWithContentsOfURL:url]; 92 // 93 // dispatch_async(mainQueue, ^{ //主隊列異步(同步也一樣):不會開啟新線程、串行執行任務 94 // NSLog(@"主隊列異步(同步也一樣),線程:%@", [NSThread currentThread]); //主隊列所對應的主線程 95 // [self updateImage:data]; 96 // }); 97 // }); 98 99 //方法二:自定義串行隊列異步:開啟新線程、串行執行任務100 // dispatch_async(customSerialQueue, ^{101 // NSLog(@"自定義串行隊列異步,線程:%@", [NSThread currentThread]); //開啟的新線程102 // NSURL *url = [Common randomImageURL];103 // NSData *data = [NSData dataWithContentsOfURL:url];104 // 105 // dispatch_async(mainQueue, ^{ //主隊列異步(同步也一樣):不會開啟新線程、串行執行任務106 // NSLog(@"主隊列異步(同步也一樣),線程:%@", [NSThread currentThread]); //主隊列所對應的主線程107 // [self updateImage:data];108 // });109 // });110 111 112 //方法三:全局并發隊列同步:不會開啟新線程、串行執行任務113 // dispatch_sync(globalQueue, ^{114 // NSLog(@"全局并發隊列同步,線程:%@", [NSThread currentThread]); //所在的線程,這里就是在主線程115 // NSURL *url = [Common randomImageURL];116 // NSData *data = [NSData dataWithContentsOfURL:url];117 // 118 // dispatch_async(mainQueue, ^{ //主隊列異步(同步也一樣):不會開啟新線程、串行執行任務119 // NSLog(@"主隊列異步(同步也一樣),線程:%@", [NSThread currentThread]); //主隊列所對應的主線程120 // [self updateImage:data];121 // });122 // });123 124 125 //方法四:全局并發隊列異步:開啟新線程、并發執行任務126 dispatch_async(globalQueue, ^{127 NSLog(@"全局并發隊列異步,線程:%@", [NSThread currentThread]); //開啟的新線程128 NSURL *url = [Common randomImageURL];129 NSData *data = [NSData dataWithContentsOfURL:url];130 131 dispatch_async(mainQueue, ^{ //主隊列異步(同步也一樣):不會開啟新線程、串行執行任務132 NSLog(@"主隊列異步(同步也一樣),線程:%@", [NSThread currentThread]); //主隊列所對應的主線程133 [self updateImage:data];134 });135 });136 }137 138 @end
FirstSampleViewController.xib
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyaccessControl="none" useAutolayout="YES" useTraitCollections="YES"> 3 <dependencies> 4 <deployment identifier="iOS"/> 5 <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/> 6 </dependencies> 7 <objects> 8 <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FirstSampleViewController"> 9 <connections>10 <outlet property="btnLoadImage" destination="sLs-f1-Gzc" id="kX8-J0-v0V"/>11 <outlet property="imgV" destination="4Qp-uk-KAb" id="RM3-Ha-glh"/>12 <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>13 </connections>14 </placeholder>15 <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>16 <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">17 <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>18 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>19 <subviews>20 <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="PictureNo.png" translatesAutoresizingMaskIntoConstraints="NO" id="4Qp-uk-KAb">21 <rect key="frame" x="205" y="225" width="190" height="150"/>22 <constraints>23 <constraint firstAttribute="height" constant="150" id="S/>24 <constraint firstAttribute="height" constant="150" id="VwM-i1-atB"/>25 <constraint firstAttribute="width" constant="190" id="mUh-Bu-tUd"/>26 <constraint firstAttribute="width" constant="190" id="mdJ-1c-QFa"/>27 <constraint firstAttribute="width" constant="190" id="sVS-bU-Ty9"/>28 <constraint firstAttribute="height" constant="150" id="uMG-oN-J56"/>29 <constraint firstAttribute="height" constant="150" id="vws-Qw-UrB"/>30 </constraints>31 <variation key="default">32 <mask key="constraints">33 <exclude reference="SIp-Wd-idU"/>34 <exclude reference="VwM-i1-atB"/>35 <exclude reference="mUh-Bu-tUd"/>36 <exclude reference="mdJ-1c-QFa"/>37 <exclude reference="sVS-bU-Ty9"/>38 <exclude reference="uMG-oN-J56"/>39 <exclude reference="vws-Qw-UrB"/>40 </mask>41 </variation>42 </imageView>43 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sLs-f1-Gzc">44 <rect key="frame" x="230" y="500" width="140" height="50"/>45 <constraints>46 <constraint firstAttribute="width" constant="140" id="1jv-9K-mdH"/>47 <constraint firstAttribute="height" constant="50" id="Q2w-vR-9ac"/>48 </constraints>49 <state key="normal" title="加載網絡圖片">50 <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>51 </state>52 <connections>53 <action selector="loadImage:" destination="-1" eventType="touchUpInside" id="fdy-Ln-5oS"/>54 </connections>55 </button>56 </subviews>57 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>58 <constraints>59 <constraint firstItem="4Qp-uk-KAb" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="205" id="2a2-mS-WFa"/>60 <constraint firstItem="sLs-f1-Gzc" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="ES4-wl-RBz"/>61 <constraint firstItem="4Qp-uk-KAb" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" id="MUJ-WA-sUf"/>62 <constraint firstItem="4Qp-uk-KAb" firstAttribute="centerX" secondItem="sLs-f1-Gzc" secondAttribute="centerX" id="Q8a-1k-DzJ"/>63 <constraint firstAttribute="bottom" secondItem="4Qp-uk-KAb" secondAttribute="bottom" constant="71" id="V0a-9y-Dwa"/>64 <constraint firstAttribute="bottom" secondItem="sLs-f1-Gzc" secondAttribute="bottom" constant="50" id="VMG-CV-eeq"/>65 <constraint firstItem="4Qp-uk-KAb" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="-71" id="gqW-Wq-4Zv"/>66 <constraint firstItem="sLs-f1-Gzc" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="kNf-6d-EJ8"/>67 </constraints>68 <variation key="default">69 <mask key="constraints">70 <exclude reference="2a2-mS-WFa"/>71 <exclude reference="V0a-9y-Dwa"/>72 <exclude reference="gqW-Wq-4Zv"/>73 <exclude reference="ES4-wl-RBz"/>74 </mask>75 </variation>76 </view>77 </objects>78 <resources>79 <image name="PictureNo.png" width="190" height="150"/>80 </resources>81 </document>
SecondSampleViewController.h
1 #import <UIKit/UIKit.h>2 3 @interface SecondSampleViewController : UIViewController4 @property (assign, nonatomic) CGSize rescaleImageSize;5 @property (strong, nonatomic) NSMutableArray *mArrImageView;6 7 @property (strong, nonatomic) IBOutlet UIButton *btnLoadImage;8 9 @end
SecondSampleViewController.m
1 #import "SecondSampleViewController.h" 2 #import "UIImage+RescaleImage.h" 3 #import "Common.h" 4 5 #define kRowCount 4 6 #define kColumnCount 3 7 #define kCellSpacing 10.0 8 9 @interface SecondSampleViewController () 10 - (void)layoutUI; 11 - (void)updateImage:(NSData *)imageData withImageIndex:(NSInteger)imageIndex; 12 -(NSData *)requestData:(NSInteger)imageIndex; 13 - (void)loadImageFromNetwork:(NSInteger)imageIndex; 14 - (void)loadImageByDispatchAsync; 15 - (void)loadImageByDispatchApply; 16 - (void)loadImageByDispatchGroupAsync; 17 - (void)additionalInfo; 18 @end 19 20 @implementation SecondSampleViewController 21 22 - (void)viewDidLoad { 23 [super viewDidLoad]; 24 25 [self layoutUI]; 26 } 27 28 - (void)didReceiveMemoryWarning { 29 [super didReceiveMemoryWarning]; 30 // Dispose of any resources that can be recreated. 31 } 32 33 - (void)dealloc { 34 _mArrImageView = nil; 35 } 36 37 - (void)layoutUI { 38 self.view.backgroundColor = [UIColor colorWithWhite:0.957 alpha:1.000]; 39 40 CGFloat width = ([[UIScreen mainScreen] bounds].size.width - ((kColumnCount + 1) * kCellSpacing)) / kColumnCount; 41 _rescaleImageSize = CGSizeMake(width, width); 42 43 CGFloat heightOfStatusAndNav = 20.0 + 44.0; 44 NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"]; 45 UIImage *img = [UIImage imageWithContentsOfFile:path]; 46 _mArrImageView = [NSMutableArray arrayWithCapacity:kRowCount * kColumnCount]; 47 //初始化多個圖片視圖 48 for (NSUInteger i=0; i<kRowCount; i++) { 49 for (NSUInteger j=0; j<kColumnCount; j++) { 50 UIImageView *imgV = [[UIImageView alloc] initWithFrame: 51 CGRectMake(_rescaleImageSize.width * j + kCellSpacing * (j+1), 52 _rescaleImageSize.height * i + kCellSpacing * (i+1) + heightOfStatusAndNav, 53 _rescaleImageSize.width, 54 _rescaleImageSize.height)]; 55 imgV.image = img; 56 [self.view addSubview:imgV]; 57 [_mArrImageView addObject:imgV]; 58 } 59 } 60 61 _btnLoadImage.tintColor = [UIColor darkGrayColor]; 62 _btnLoadImage.layer.masksToBounds = YES; 63 _btnLoadImage.layer.cornerRadius = 10.0; 64 _btnLoadImage.layer.borderColor = [UIColor grayColor].CGColor; 65 _btnLoadImage.layer.borderWidth = 1.0; 66 } 67 68 - (void)updateImage:(NSData *)imageData withImageIndex:(NSInteger)imageIndex { 69 UIImage *img = [UIImage imageWithData:imageData]; 70 UIImageView *imgVCurrent = _mArrImageView[imageIndex]; 71 imgVCurrent.image = [img rescaleImageToSize:_rescaleImageSize]; 72 } 73 74 -(NSData *)requestData:(NSInteger)imageIndex { 75 //對于多線程操作,建議把線程操作放到 @autoreleasepool 中 76 @autoreleasepool { 77 NSURL *url = [Common randomImageURL]; 78 NSData *data = [NSData dataWithContentsOfURL:url]; 79 return data; 80 } 81 } 82 83 - (void)loadImageFromNetwork:(NSInteger)imageIndex { 84 NSData *data = [self requestData:imageIndex]; 85 86 NSLog(@"Current thread:%@, imageIndex:%ld", [NSThread currentThread], (long)imageIndex); //dispatch_async、dispatch_apply、dispatch_group_async 的全局并發隊列,會開啟多個新線程去執行多個任務;按系統管理,也可能存在一個線程執行兩個任務的情況 87 88 dispatch_async(dispatch_get_main_queue(), ^{ //主隊列異步(同步也一樣):不會開啟新線程、串行執行任務 89 NSLog(@"主隊列異步(同步也一樣),線程:%@", [NSThread currentThread]); //主隊列所對應的主線程 90 [self updateImage:data withImageIndex:imageIndex]; 91 }); 92 } 93 94 #pragma mark - 三種方法實現多線程并發執行多個任務,并且優先加載最后一張圖片 95 - (void)loadImageByDispatchAsync { 96 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //全局并發隊列 97 98 dispatch_async(globalQueue, ^{ 99 NSInteger len = kRowCount * kColumnCount - 1;100 101 NSLog(@"全局并發隊列異步,線程:%@", [NSThread currentThread]); //開啟的新線程102 [self loadImageFromNetwork:len]; //優先加載最后一張圖片103 104 for (NSUInteger i=0; i<len; i++) {105 dispatch_async(globalQueue, ^{106 [self loadImageFromNetwork:i];107 });108 }109 });110 }111 112 - (void)loadImageByDispatchApply {113 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //全局并發隊列114 115 //使用 dispatch_async,全局并發隊列異步:開啟新線程、并發執行任務;此時會并發無序地逐個圖片加載并顯示出來116 //PS:如果使用 dispatch_sync,全局并發隊列同步:不會開啟新線程、串行執行任務;此時會等到全部圖片加載完才全部顯示出來117 dispatch_async(globalQueue, ^{118 size_t len = kRowCount * kColumnCount - 1;119 120 NSLog(@"全局并發隊列異步,線程:%@", [NSThread currentThread]); //開啟的新線程121 [self loadImageFromNetwork:len]; //優先加載最后一張圖片122 123 //使用 dispatch_apply 或 dispatch_apply_f 來循環迭代執行任務;前提是迭代的任務相互獨立,而且任務執行順序是無關緊要的;124 //他每次循環迭代會將指定的任務提交到 queue,queue 會開啟多個新線程(線程會盡量復用,線程更少,系統多線程之間切換調度更快)去執行多個任務,執行順序是不會像 for 循環那樣有序執行;125 //但是他像 for 循環一樣會在所有任務循環迭代執行完后才返回,會阻塞當前線程,所以只用于并發隊列。如果用于串行隊列,會造成死鎖,無法正常執行126 dispatch_apply(len, globalQueue, ^(size_t i) {127 [self loadImageFromNetwork:i];128 });129 });130 }131 132 - (void)loadImageByDispatchGroupAsync {133 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //全局并發隊列134 135 //使用 dispatch_async,全局并發隊列異步:開啟新線程、并發執行任務;此時會并發無序地逐個圖片加載并顯示出來136 //PS:如果使用 dispatch_sync,全局并發隊列同步:不會開啟新線程、串行執行任務;此時會等到全部圖片加載完才全部顯示出來137 dispatch_async(globalQueue, ^{138 NSInteger len = kRowCount * kColumnCount - 1;139 140 NSLog(@"全局并發隊列異步,線程:%@", [NSThread currentThread]); //開啟的新線程141 [self loadImageFromNetwork:len]; //優先加載最后一張圖片142 143 dispatch_group_t group = dispatch_group_create(); //自定義組144 for (NSUInteger i=0; i<len; i++) {145 //把一系列任務提交到隊列中,并統一關聯到自定義組中;這樣并發全部執行完畢后,能通過 dispatch_group_notify 進行后續處理146 dispatch_group_async(group, globalQueue, ^{147 [self loadImageFromNetwork:i];148 });149 }150 151 //自定義組通知;會根據隊列類型決定是否開啟新線程;如果是并發隊列就會開啟新線程(實際上是:復用自定義組執行最后一個任務的對應線程),否則是串行隊列就不會152 dispatch_group_notify(group, dispatch_get_main_queue(), ^{153 NSLog(@"自定義組通知,線程:%@", [NSThread currentThread]);154 NSLog(@"全部的 dispatch_group_async 任務執行完畢后,做一些事情,只執行一次");155 });156 });157 }158 159 - (void)additionalInfo {160 //dispatch_suspend(queue); //掛起隊列方法;當前正在執行的任務不會停下來,只是不再繼續執行未啟動的任務161 //dispatch_resume(queue); //恢復隊列方法;確保他跟 dispatch_suspend 成對調用162 163 //單次執行任務方法;方法是線程安全的164 static dispatch_once_t onceToken;165 dispatch_once(&onceToken, ^{166 NSLog(@"dispatch_once:單次執行任務;只會執行一次,重復調用方法也不會重復執行(可用于應用程序中初始化全局數據的情況,例如:單例模式)");167 });168 169 //類似 - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay 的延時執行方法170 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{171 NSLog(@"dispatch_after:延時執行任務,這里2.0秒后做一些事情");172 });173 }174 175 - (IBAction)loadImage:(id)sender {176 //三種方法實現多線程并發執行多個任務,并且優先加載最后一張圖片177 //方法一:dispatch_async178 //[self loadImageByDispatchAsync];179 180 //方法二:dispatch_apply181 //[self loadImageByDispatchApply];182 183 //方法三:dispatch_group_async184 [self loadImageByDispatchGroupAsync];185 186 187 //附加信息188 [self additionalInfo];189 }190 191 @end
SecondSampleViewController.xib
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"> 3 <dependencies> 4 <deployment identifier="iOS"/> 5 <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/> 6 </dependencies> 7 <objects> 8 <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="SecondSampleViewController"> 9 <connections>10 <outlet property="btnLoadImage" destination="F5h-ZI-gGL" id="I40-e2-bAa"/>11 <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>12 </connections>13 </placeholder>14 <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>15 <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">16 <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>17 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>18 <subviews>19 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F5h-ZI-gGL">20 <rect key="frame" x="230" y="530" width="140" height="50"/>21 <constraints>22 <constraint firstAttribute="height" constant="50" id="HWd-Xc-Wk7"/>23 <constraint firstAttribute="width" constant="140" id="vrH-qE-PNK"/>24 </constraints>25 <state key="normal" title="加載網絡圖片">26 <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>27 </state>28 <connections>29 <action selector="loadImage:" destination="-1" eventType="touchUpInside" id="Hgw-q8-lHy"/>30 </connections>31 </button>32 </subviews>33 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>34 <constraints>35 <constraint firstAttribute="bottom" secondItem="F5h-ZI-gGL" secondAttribute="bottom" constant="20" id="jPY-fY-9XJ"/>36 <constraint firstAttribute="centerX" secondItem="F5h-ZI-gGL" secondAttribute="centerX" id="rH1-sV-pST"/>37 </constraints>38 </view>39 </objects>40 </document>
AppDelegate.h
1 #import <UIKit/UIKit.h>2 3 @interface AppDelegate : UIResponder <UIApplicationDelegate>4 5 @property (strong, nonatomic) UIWindow *window;6 @property (strong, nonatomic) UINavigationController *navigationController;7 8 @end
AppDelegate.m
1 #import "AppDelegate.h" 2 #import "ViewController.h" 3 4 @interface AppDelegate () 5 6 @end 7 8 @implementation AppDelegate 9 10 11 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {12 _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];13 ViewController *viewController = [[ViewController alloc] initWithSampleNameArray:@[@"請求單張網絡圖片(解決線程阻塞)", @"請求多張網絡圖片(多個線程并發)"]];14 _navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];15 _window.rootViewController = _navigationController;16 //[_window addSubview:_navigationController.view]; //當_window.rootViewController關聯時,這一句可有可無17 [_window makeKeyAndVisible];18 return YES;19 }20 21 - (void)applicationWillResignActive:(UIApplication *)application {22 }23 24 - (void)applicationDidEnterBackground:(UIApplication *)application {25 }26 27 - (void)applicationWillEnterForeground:(UIApplication *)application {28 }29 30 - (void)applicationDidBecomeActive:(UIApplication *)application {31 }32 33 - (void)applicationWillTerminate:(UIApplication *)application {34 }35 36 @end
輸出結果:
方法三:dispatch_group_async
1 2015-08-30 17:19:44.168 GCDDemo[4776:70114] 全局并發隊列異步,線程:<NSThread: 0x7f9511c7a920>{number = 4, name = (null)} 2 2015-08-30 17:19:44.168 GCDDemo[4776:70265] dispatch_once:單次執行任務;只會執行一次,重復調用方法也不會重復執行(可用于應用程序中初始化全局數據的情況,例如:單例模式) 3 2015-08-30 17:19:44.229 GCDDemo[4776:70265] Current thread:<NSThread: 0x7f9511c7a920>{number = 4, name = (null)}, imageIndex:11 4 2015-08-30 17:19:44.230 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main} 5 2015-08-30 17:19:44.283 GCDDemo[4776:70265] Current thread:<NSThread: 0x7f9511c7a920>{number = 4, name = (null)}, imageIndex:0 6 2015-08-30 17:19:44.283 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main} 7 2015-08-30 17:19:44.301 GCDDemo[4776:70248] Current thread:<NSThread: 0x7f9511f2b3f0>{number = 2, name = (null)}, imageIndex:1 8 2015-08-30 17:19:44.301 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main} 9 2015-08-30 17:19:44.516 GCDDemo[4776:70269] Current thread:<NSThread: 0x7f9511ef52f0>{number = 5, name = (null)}, imageIndex:210 2015-08-30 17:19:44.516 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}11 2015-08-30 17:19:44.887 GCDDemo[4776:70241] Current thread:<NSThread: 0x7f9511d94230>{number = 6, name = (null)}, imageIndex:612 2015-08-30 17:19:44.888 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}13 2015-08-30 17:19:44.999 GCDDemo[4776:70299] Current thread:<NSThread: 0x7f9511d93d40>{number = 7, name = (null)}, imageIndex:314 2015-08-30 17:19:44.999 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}15 2015-08-30 17:19:45.152 GCDDemo[4776:70301] Current thread:<NSThread: 0x7f9511d78f90>{number = 8, name = (null)}, imageIndex:516 2015-08-30 17:19:45.152 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}17 2015-08-30 17:19:45.190 GCDDemo[4776:70300] Current thread:<NSThread: 0x7f9511ef0340>{number = 9, name = (null)}, imageIndex:418 2015-08-30 17:19:45.190 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}19 2015-08-30 17:19:45.240 GCDDemo[4776:70304] Current thread:<NSThread: 0x7f9511d8e9d0>{number = 10, name = (null)}, imageIndex:820 2015-08-30 17:19:45.240 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}21 2015-08-30 17:19:45.314 GCDDemo[4776:70305] Current thread:<NSThread: 0x7f9511ecd030>{number = 11, name = (null)}, imageIndex:1022 2015-08-30 17:19:45.314 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}23 2015-08-30 17:19:45.443 GCDDemo[4776:70302] Current thread:<NSThread: 0x7f9511d8fc50>{number = 12, name = (null)}, imageIndex:724 2015-08-30 17:19:45.443 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}25 2015-08-30 17:19:45.615 GCDDemo[4776:70303] Current thread:<NSThread: 0x7f9511d91740>{number = 13, name = (null)}, imageIndex:926 2015-08-30 17:19:45.615 GCDDemo[4776:70114] 主隊列異步(同步也一樣),線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}27 2015-08-30 17:19:45.620 GCDDemo[4776:70114] 自定義組通知,線程:<NSThread: 0x7f9511d0a9a0>{number = 1, name = main}28 2015-08-30 17:19:45.620 GCDDemo[4776:70114] 全部的 dispatch_group_async 任務執行完畢后,做一些事情,只執行一次29 2015-08-30 17:19:46.360 GCDDemo[4776:70114] dispatch_after:延時執行任務,這里2.0秒后做一些事情
方法一:dispatch_async
1 2015-08-30 17:15:08.490 GCDDemo[4668:66939] 全局并發隊列異步,線程:<NSThread: 0x7fc5be00c810>{number = 2, name = (null)} 2 2015-08-30 17:15:08.490 GCDDemo[4668:66894] dispatch_once:單次執行任務;只會執行一次,重復調用方法也不會重復執行(可用于應用程序中初始化全局數據的情況,例如:單例模式) 3 2015-08-30 17:15:08.862 GCDDemo[4668:66939] Current thread:<NSThread: 0x7fc5be00c810>{number = 2, name = (null)}, imageIndex:11 4 2015-08-30 17:15:08.862 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main} 5 2015-08-30 17:15:09.062 GCDDemo[4668:66981] Current thread:<NSThread: 0x7fc5be05ac90>{number = 4, name = (null)}, imageIndex:3 6 2015-08-30 17:15:09.062 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main} 7 2015-08-30 17:15:09.066 GCDDemo[4668:66939] Current thread:<NSThread: 0x7fc5be00c810>{number = 2, name = (null)}, imageIndex:0 8 2015-08-30 17:15:09.067 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main} 9 2015-08-30 17:15:09.323 GCDDemo[4668:66941] Current thread:<NSThread: 0x7fc5bbf60040>{number = 5, name = (null)}, imageIndex:210 2015-08-30 17:15:09.323 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main}11 2015-08-30 17:15:09.323 GCDDemo[4668:66940] Current thread:<NSThread: 0x7fc5be05bf50>{number = 6, name = (null)}, imageIndex:112 2015-08-30 17:15:09.324 GCDDemo[4668:66942] Current thread:<NSThread: 0x7fc5bbd05bf0>{number = 7, name = (null)}, imageIndex:413 2015-08-30 17:15:09.327 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main}14 2015-08-30 17:15:09.330 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main}15 2015-08-30 17:15:09.486 GCDDemo[4668:66991] Current thread:<NSThread: 0x7fc5bbd1f880>{number = 8, name = (null)}, imageIndex:716 2015-08-30 17:15:09.486 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main}17 2015-08-30 17:15:10.156 GCDDemo[4668:66988] Current thread:<NSThread: 0x7fc5be05ae70>{number = 9, name = (null)}, imageIndex:518 2015-08-30 17:15:10.156 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main}19 2015-08-30 17:15:10.208 GCDDemo[4668:66990] Current thread:<NSThread: 0x7fc5bbd26b00>{number = 10, name = (null)}, imageIndex:620 2015-08-30 17:15:10.209 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main}21 2015-08-30 17:15:10.239 GCDDemo[4668:66992] Current thread:<NSThread: 0x7fc5bbe9ff30>{number = 11, name = (null)}, imageIndex:822 2015-08-30 17:15:10.239 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main}23 2015-08-30 17:15:10.491 GCDDemo[4668:66894] dispatch_after:延時執行任務,這里2.0秒后做一些事情24 2015-08-30 17:15:10.838 GCDDemo[4668:66993] Current thread:<NSThread: 0x7fc5bbea3940>{number = 12, name = (null)}, imageIndex:925 2015-08-30 17:15:10.838 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main}26 2015-08-30 17:15:11.489 GCDDemo[4668:66989] Current thread:<NSThread: 0x7fc5bbea4dd0>{number = 13, name = (null)}, imageIndex:1027 2015-08-30 17:15:11.490 GCDDemo[4668:66894] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fc5bbe280d0>{number = 1, name = main}
方法二:dispatch_apply
1 2015-08-30 17:15:50.873 GCDDemo[4694:67547] 全局并發隊列異步,線程:<NSThread: 0x7fe57149bb60>{number = 2, name = (null)} 2 2015-08-30 17:15:50.873 GCDDemo[4694:67503] dispatch_once:單次執行任務;只會執行一次,重復調用方法也不會重復執行(可用于應用程序中初始化全局數據的情況,例如:單例模式) 3 2015-08-30 17:15:50.973 GCDDemo[4694:67547] Current thread:<NSThread: 0x7fe57149bb60>{number = 2, name = (null)}, imageIndex:11 4 2015-08-30 17:15:50.974 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main} 5 2015-08-30 17:15:51.018 GCDDemo[4694:67546] Current thread:<NSThread: 0x7fe5714f8490>{number = 4, name = (null)}, imageIndex:1 6 2015-08-30 17:15:51.018 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main} 7 2015-08-30 17:15:51.078 GCDDemo[4694:67546] Current thread:<NSThread: 0x7fe5714f8490>{number = 4, name = (null)}, imageIndex:4 8 2015-08-30 17:15:51.078 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main} 9 2015-08-30 17:15:51.087 GCDDemo[4694:67562] Current thread:<NSThread: 0x7fe5717bd740>{number = 5, name = (null)}, imageIndex:210 2015-08-30 17:15:51.087 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main}11 2015-08-30 17:15:51.200 GCDDemo[4694:67547] Current thread:<NSThread: 0x7fe57149bb60>{number = 2, name = (null)}, imageIndex:012 2015-08-30 17:15:51.200 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main}13 2015-08-30 17:15:51.271 GCDDemo[4694:67562] Current thread:<NSThread: 0x7fe5717bd740>{number = 5, name = (null)}, imageIndex:614 2015-08-30 17:15:51.271 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main}15 2015-08-30 17:15:51.628 GCDDemo[4694:67549] Current thread:<NSThread: 0x7fe57152d910>{number = 6, name = (null)}, imageIndex:316 2015-08-30 17:15:51.628 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main}17 2015-08-30 17:15:51.701 GCDDemo[4694:67546] Current thread:<NSThread: 0x7fe5714f8490>{number = 4, name = (null)}, imageIndex:518 2015-08-30 17:15:51.701 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main}19 2015-08-30 17:15:51.800 GCDDemo[4694:67547] Current thread:<NSThread: 0x7fe57149bb60>{number = 2, name = (null)}, imageIndex:720 2015-08-30 17:15:51.800 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main}21 2015-08-30 17:15:52.063 GCDDemo[4694:67549] Current thread:<NSThread: 0x7fe57152d910>{number = 6, name = (null)}, imageIndex:922 2015-08-30 17:15:52.064 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main}23 2015-08-30 17:15:52.109 GCDDemo[4694:67562] Current thread:<NSThread: 0x7fe5717bd740>{number = 5, name = (null)}, imageIndex:824 2015-08-30 17:15:52.110 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main}25 2015-08-30 17:15:52.295 GCDDemo[4694:67546] Current thread:<NSThread: 0x7fe5714f8490>{number = 4, name = (null)}, imageIndex:1026 2015-08-30 17:15:52.295 GCDDemo[4694:67503] 主隊列異步(同步也一樣),線程:<NSThread: 0x7fe571428b10>{number = 1, name = main}27 2015-08-30 17:15:52.874 GCDDemo[4694:67503] dispatch_after:延時執行任務,這里2.0秒后做一些事情
新聞熱點
疑難解答