你的位置:首页 > 操作系统

[操作系统]多线程开发之二 NSOperation


效果如下:

 

ViewController.h

1 #import <UIKit/UIKit.h>2 3 @interface ViewController : UITableViewController4 @property (strong, 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 = @"多线程开发之二 NSOperation";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 @implementation Common 4  5 + (NSURL *)randomImageURL { 6   NSUInteger randomVal = (arc4random() % 20) + 1; //1-20的随机数 7   NSString *randomValStr = [NSString stringWithFormat:@"%lu", (unsigned long)randomVal]; 8   if (randomVal < 10) { 9     randomValStr = [@"0" stringByAppendingString:randomValStr];10   }11   12   NSString *imageURLStr = [NSString stringWithFormat:@"http://images.apple.com/v/watch/e/apple-watch/images/collection_%@_large.jpg", randomValStr];13   return [NSURL URLWithString:imageURLStr];14 }15 16 @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 - (void)loadImageFromNetwork; 9 @end10 11 @implementation FirstSampleViewController12 13 - (void)viewDidLoad {14   [super viewDidLoad];15   16   [self layoutUI];17 }18 19 - (void)didReceiveMemoryWarning {20   [super didReceiveMemoryWarning];21   // Dispose of any resources that can be recreated.22 }23 24 - (void)dealloc {25   _imgV.image = nil;26 }27 28 - (void)layoutUI {29   CGFloat width = [[UIScreen mainScreen] bounds].size.width; //bounds 返回整个屏幕大小;applicationFrame 返回去除状态栏后的屏幕大小30   CGFloat height = width * 438.0 / 366.0;31   const CGFloat percentVal = 3.0 / 4.0; //以屏幕宽度的3/4比例显示32   _rescaleImageSize = CGSizeMake(width * percentVal, height * percentVal);33   34   //NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"];35   //_imgV.image = [UIImage imageWithContentsOfFile:path];36   37   _btnLoadImage.tintColor = [UIColor darkGrayColor];38   _btnLoadImage.layer.masksToBounds = YES;39   _btnLoadImage.layer.cornerRadius = 10.0;40   _btnLoadImage.layer.borderColor = [UIColor grayColor].CGColor;41   _btnLoadImage.layer.borderWidth = 1.0;42 }43 44 - (void)updateImage:(NSData *)imageData {45   UIImage *img = [UIImage imageWithData:imageData];46   _imgV.image = [img rescaleImageToSize:_rescaleImageSize];47 }48 49 - (void)loadImageFromNetwork {50   NSURL *url = [Common randomImageURL];51   NSData *data = [NSData dataWithContentsOfURL:url];52   53   //mainQueue 是 UI 主线程,调用主线程队列的方法进行操作54   [[NSOperationQueue mainQueue] addOperationWithBlock:^{55     [self updateImage:data];56   }];57 }58 59 - (IBAction)loadImage:(id)sender {60   //方法一:使用 NSInvocationOperation61 //  NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]62 //                         initWithTarget:self63 //                         selector:@selector(loadImageFromNetwork)64 //                         object:nil];65 //  NSOperationQueue *operationQueue = [NSOperationQueue new]; //操作队列66 //  [operationQueue addOperation:invocationOperation];67 68   69   //方法二:使用 NSBlockOperation70   NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{71     [self loadImageFromNetwork];72   }];73   [blockOperation addExecutionBlock:^{ //还可以为 blockOperation 添加代码块;多个代码块会被分到多个线程去执行,存在两个代码块被分配到同一个线程执行的情况74     NSLog(@"代码块");75   }];76   [blockOperation addExecutionBlock:[blockOperation executionBlocks][1]]; //这里可以调用他的 executionBlocks 获取到添加的代码块数组;上面打印的内容「代码块」会被执行两次77   78   [blockOperation setCompletionBlock:^{79     NSLog(@"所有代码块操作执行完毕后(无论已取消还是已完成),做一些事情,只执行一次");80   }];81   82   //[blockOperation start]; //使用 start 方法,则此操作在主线程中执行;一般不这样操作,而是添加到操作队列中83   NSOperationQueue *operationQueue = [NSOperationQueue new]; //操作队列84   [operationQueue addOperation:blockOperation]; //添加到操作队列,这时队列会开启一个线程去执行此操作;一个线程可以执行多个操作85   86   //方法三:直接使用 NSOperationQueue87 //  NSOperationQueue *operationQueue = [NSOperationQueue new]; //操作队列88 //  [operationQueue addOperationWithBlock:^{89 //    [self loadImageFromNetwork];90 //  }];91 }92 93 @end 

FirstSampleViewController.xib

 1 <??> 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="SIp-Wd-idU"/>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 @end 15  16 @implementation SecondSampleViewController 17  18 - (void)viewDidLoad { 19   [super viewDidLoad]; 20    21   [self layoutUI]; 22 } 23  24 - (void)didReceiveMemoryWarning { 25   [super didReceiveMemoryWarning]; 26   // Dispose of any resources that can be recreated. 27 } 28  29 - (void)dealloc { 30   _mArrImageView = nil; 31 } 32  33 - (void)layoutUI { 34   CGFloat width = ([[UIScreen mainScreen] bounds].size.width - ((kColumnCount + 1) * kCellSpacing)) / kColumnCount; 35   _rescaleImageSize = CGSizeMake(width, width); 36    37   CGFloat heightOfStatusAndNav = 20.0 + 44.0; 38   NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"]; 39   UIImage *img = [UIImage imageWithContentsOfFile:path]; 40   _mArrImageView = [NSMutableArray arrayWithCapacity:kRowCount * kColumnCount]; 41   //初始化多个图片视图 42   for (NSUInteger i=0; i<kRowCount; i++) { 43     for (NSUInteger j=0; j<kColumnCount; j++) { 44       UIImageView *imgV = [[UIImageView alloc] initWithFrame: 45                 CGRectMake(_rescaleImageSize.width * j + kCellSpacing * (j+1), 46                       _rescaleImageSize.height * i + kCellSpacing * (i+1) + heightOfStatusAndNav, 47                       _rescaleImageSize.width, 48                       _rescaleImageSize.height)]; 49       imgV.image = img; 50       [self.view addSubview:imgV]; 51       [_mArrImageView addObject:imgV]; 52     } 53   } 54    55   _btnLoadImage.tintColor = [UIColor darkGrayColor]; 56   _btnLoadImage.layer.masksToBounds = YES; 57   _btnLoadImage.layer.cornerRadius = 10.0; 58   _btnLoadImage.layer.borderColor = [UIColor grayColor].CGColor; 59   _btnLoadImage.layer.borderWidth = 1.0; 60 } 61  62 - (void)updateImage:(NSData *)imageData withImageIndex:(NSInteger)imageIndex { 63   UIImage *img = [UIImage imageWithData:imageData]; 64   UIImageView *imgVCurrent = _mArrImageView[imageIndex]; 65   imgVCurrent.image = [img rescaleImageToSize:_rescaleImageSize]; 66 } 67  68 -(NSData *)requestData:(NSInteger)imageIndex { 69   //对于多线程操作,建议把线程操作放到 @autoreleasepool 中 70   @autoreleasepool { 71     NSURL *url = [Common randomImageURL]; 72     NSData *data = [NSData dataWithContentsOfURL:url]; 73     return data; 74   } 75 } 76  77 - (void)loadImageFromNetwork:(NSInteger)imageIndex { 78   /* 79    对比之前 NSThread 加载图片,你会发现核心代码简化了不少,有两点值得提的: 80    (1)使用 NSBlockOperation 方法,所有的操作不必单独定义方法,同时解决了只能传递一个参数的问题。类似的操作,例如:调用主线程队列的 addOperationWithBlock: 方法进行 UI 更新,不用再定义一个参数实体类来进行参数传递(之前必须定义一个 KMImageData 解决只能传递一个参数的问题)。 81    (2)使用 NSOperation 进行多线程开发可以设置最大并发操作数,有效的对操作进行控制(如 Demo 的代码设置最大并发操作数为5,则图片最多是五个一次加载的)。 82   */ 83   NSLog(@"Current thread:%@", [NSThread currentThread]); 84    85   //mainQueue 是 UI 主线程,调用主线程队列的方法进行操作 86   [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 87     [self updateImage:[self requestData:imageIndex] withImageIndex:imageIndex]; 88   }]; 89 } 90  91 - (IBAction)loadImage:(id)sender { 92   NSOperationQueue *operationQueue = [NSOperationQueue new]; 93   operationQueue.maxConcurrentOperationCount = 5; //设置最大并发操作数 94    95   NSUInteger len = kRowCount * kColumnCount; 96   NSBlockOperation *lastBlockOperation = [NSBlockOperation blockOperationWithBlock:^{ 97     [self loadImageFromNetwork:len - 1]; 98   }]; 99   100   for (NSUInteger i=0; i<len - 1; i++) {101     NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{102       [self loadImageFromNetwork:i];103     }];104     105     //实现控制「操作执行顺序」,例如:控制最后一个操作第一个执行,这里设置操作的依赖关系为:最后一张图片加载操作完成后才执行106     //PS:注意请勿进行循环依赖,否则循环依赖相关的操作是不会被执行的107     //这里添加依赖关系;其相对应的移除方法:- (void)removeDependency:(NSOperation *)op;108     [blockOperation addDependency:lastBlockOperation];109     110     //另外添加操作对象实例数组的方法:- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;111     [operationQueue addOperation:blockOperation];112   }113   114   [operationQueue addOperation:lastBlockOperation];115   116   /*117   lastBlockOperation.queuePriority = NSOperationQueuePriorityVeryHigh; //在某个操作队列中的队列优先级;这样可以提高他被优先加载的机率,但是他也未必就第一个加载;所以要实现控制「操作执行顺序」,就得用设置操作的依赖关系的方式118   119   typedef enum : NSInteger {120     NSOperationQueuePriorityVeryLow = -8,121     NSOperationQueuePriorityLow = -4,122     NSOperationQueuePriorityNormal = 0,123     NSOperationQueuePriorityHigh = 4,124     NSOperationQueuePriorityVeryHigh = 8125   } NSOperationQueuePriority;126   */127 }128 129 @end 

SecondSampleViewController.xib

 1 <??> 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 

输出结果:

 1 2015-08-28 00:26:36.878 NSOperationDemo[4228:48976] 代码块 2 2015-08-28 00:26:36.878 NSOperationDemo[4228:48977] 代码块 3 2015-08-28 00:26:37.366 NSOperationDemo[4228:48976] 所有代码块操作执行完毕后(无论已取消还是已完成),做一些事情,只执行一次 4  5 2015-08-28 00:26:42.505 NSOperationDemo[4228:48860] Current thread:<NSThread: 0x7fbd1bd335b0>{number = 3, name = (null)} 6 2015-08-28 00:26:42.506 NSOperationDemo[4228:48978] Current thread:<NSThread: 0x7fbd1be0ad90>{number = 4, name = (null)} 7 2015-08-28 00:26:42.507 NSOperationDemo[4228:48990] Current thread:<NSThread: 0x7fbd1e047390>{number = 5, name = (null)} 8 2015-08-28 00:26:42.507 NSOperationDemo[4228:48990] Current thread:<NSThread: 0x7fbd1e047390>{number = 5, name = (null)} 9 2015-08-28 00:26:42.507 NSOperationDemo[4228:48978] Current thread:<NSThread: 0x7fbd1be0ad90>{number = 4, name = (null)}10 2015-08-28 00:26:42.507 NSOperationDemo[4228:49025] Current thread:<NSThread: 0x7fbd1be07c70>{number = 6, name = (null)}11 2015-08-28 00:26:42.508 NSOperationDemo[4228:48978] Current thread:<NSThread: 0x7fbd1be0ad90>{number = 4, name = (null)}12 2015-08-28 00:26:42.508 NSOperationDemo[4228:49025] Current thread:<NSThread: 0x7fbd1be07c70>{number = 6, name = (null)}13 2015-08-28 00:26:42.508 NSOperationDemo[4228:48978] Current thread:<NSThread: 0x7fbd1be0ad90>{number = 4, name = (null)}14 2015-08-28 00:26:42.508 NSOperationDemo[4228:48990] Current thread:<NSThread: 0x7fbd1e047390>{number = 5, name = (null)}15 2015-08-28 00:26:42.508 NSOperationDemo[4228:48976] Current thread:<NSThread: 0x7fbd1e0335f0>{number = 7, name = (null)}16 2015-08-28 00:26:42.508 NSOperationDemo[4228:48978] Current thread:<NSThread: 0x7fbd1be0ad90>{number = 4, name = (null)}