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

[操作系统]多线程开发之一 NSThread


效果如下:

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 #import "ThirdSampleViewController.h" 5  6 @interface ViewController () 7 - (void)layoutUI; 8 @end 9 10 @implementation ViewController11 - (void)viewDidLoad {12   [super viewDidLoad];13   14   [self layoutUI];15 }16 17 - (void)didReceiveMemoryWarning {18   [super didReceiveMemoryWarning];19   // Dispose of any resources that can be recreated.20 }21 22 - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName {23   if (self = [super initWithStyle:UITableViewStyleGrouped]) {24     self.navigationItem.title = @"多线程开发之一 NSThread";25     self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回首页" style:UIBarButtonItemStylePlain target:nil action:nil];26     27     _arrSampleName = arrSampleName;28   }29   return self;30 }31 32 - (void)layoutUI {33   34 }35 36 #pragma mark - UITableViewController相关方法重写37 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {38   return 0.1;39 }40 41 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {42   return 1;43 }44 45 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {46   return [_arrSampleName count];47 }48 49 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {50   static NSString *cellIdentifier = @"cell";51   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];52   if (!cell) {53     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];54   }55   cell.textLabel.text = _arrSampleName[indexPath.row];56   return cell;57 }58 59 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {60   switch (indexPath.row) {61     case 0: {62       FirstSampleViewController *firstSampleVC = [FirstSampleViewController new];63       [self.navigationController pushViewController:firstSampleVC animated:YES];64       break;65     }66     case 1: {67       SecondSampleViewController *secondSampleVC = [SecondSampleViewController new];68       [self.navigationController pushViewController:secondSampleVC animated:YES];69       break;70     }71     case 2: {72       ThirdSampleViewController *thirdSampleVC = [ThirdSampleViewController new];73       [self.navigationController pushViewController:thirdSampleVC animated:YES];74       break;75       76       /*77        类似堆栈的先进后出的原理:78        返回到(上一级)、(任意级)、(根级)导航79       [self.navigationController popViewControllerAnimated:YES];80       [self.navigationController popToViewController:thirdSampleVC animated:YES];81       [self.navigationController popToRootViewControllerAnimated:YES];82       */83     }84     default:85       break;86   }87 }88 89 @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

KMImageData.h

1 #import <Foundation/Foundation.h>2 3 @interface KMImageData : NSObject4 @property (assign, nonatomic) NSInteger index;5 @property (strong, nonatomic) NSData *data;6 7 - (instancetype)initWithData:(NSData *)data withIndex:(NSInteger)index;8 9 @end

KMImageData.m

 1 #import "KMImageData.h" 2  3 @implementation KMImageData 4  5 - (instancetype)initWithData:(NSData *)data withIndex:(NSInteger)index { 6   if (self = [super init]) { 7     _data = data; 8     _index = index; 9   }10   return self;11 }12 13 @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  4 @interface FirstSampleViewController () 5 - (void)layoutUI; 6 - (void)updateImage:(NSData *)imageData; 7 - (void)loadImageFromNetwork; 8 @end 9 10 @implementation FirstSampleViewController11 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   CGFloat width = [[UIScreen mainScreen] bounds].size.width; //bounds 返回整个屏幕大小;applicationFrame 返回去除状态栏后的屏幕大小29   CGFloat height = width * 150.0 / 190.0;30   _rescaleImageSize = CGSizeMake(width, height);31   32   //NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"];33   //_imgV.image = [UIImage imageWithContentsOfFile:path];34   35   _btnLoadImage.tintColor = [UIColor darkGrayColor];36   _btnLoadImage.layer.masksToBounds = YES;37   _btnLoadImage.layer.cornerRadius = 10.0;38   _btnLoadImage.layer.borderColor = [UIColor grayColor].CGColor;39   _btnLoadImage.layer.borderWidth = 1.0;40 }41 42 - (void)updateImage:(NSData *)imageData {43   UIImage *img = [UIImage imageWithData:imageData];44   _imgV.image = [img rescaleImageToSize:_rescaleImageSize];45 }46 47 - (void)loadImageFromNetwork {48   NSURL *url = [NSURL URLWithString:@"http://images.apple.com/v/macbook/c/overview/images/hero_static_large.jpg"];49   NSData *data = [NSData dataWithContentsOfURL:url];50   51   /*将数据显示到UI控件,注意只能在主线程中更新UI;52    另外 performSelectorOnMainThread 方法是 NSObject 的分类方法,每个 NSObject 对象都有此方法;53    它调用的 selector 方法是当前调用控件的方法,例如使用 UIImageView 调用的时候 selector 就是 UIImageView 的方法54    withObject:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)55    waitUntilDone:是否线程任务完成执行56   */57   [self performSelectorOnMainThread:@selector(updateImage:)58               withObject:data59             waitUntilDone:YES];60 }61 62 - (IBAction)loadImage:(id)sender {63   //方法一:使用线程对象实例方法创建一个线程64   //NSThread *thread = [[NSThread alloc] initWithTarget:self65   //                      selector:@selector(loadImageFromNetwork)66   //                       object:nil];67   //[thread start]; //启动一个线程;注意启动一个线程并非就一定立即执行,而是处于就绪状态,当系统调度时才真正执行68   69   //方法二:使用线程类方法创建一个线程70   [NSThread detachNewThreadSelector:@selector(loadImageFromNetwork)71                toTarget:self72               withObject:nil];73 }74 75 @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 "KMImageData.h" 3 #import "UIImage+RescaleImage.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:(KMImageData *)imageData; 12 -(KMImageData *)requestData:(NSInteger)imageIndex; 13 - (void)loadImageFromNetwork:(NSNumber *)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:(KMImageData *)imageData { 63   UIImage *img = [UIImage imageWithData:imageData.data]; 64   UIImageView *imgVCurrent = _mArrImageView[imageData.index]; 65   imgVCurrent.image = [img rescaleImageToSize:_rescaleImageSize]; 66 } 67  68 -(KMImageData *)requestData:(NSInteger)imageIndex { 69   //对于多线程操作,建议把线程操作放到 @autoreleasepool 中 70   @autoreleasepool { 71     if (imageIndex != kRowCount * kColumnCount - 1) { //虽然设置了最后一个线程的优先级为1.0,但无法保证他是第一个加载的。所以我们这里让其他线程挂起0.5秒,这样基本能保证最后一个线程第一个加载,除非网络非常差的情况 72       [NSThread sleepForTimeInterval:0.5]; 73     } 74      75     NSURL *url = [NSURL URLWithString:@"http://images.apple.com/v/macbook/c/overview/images/hero_static_large.jpg"]; 76     NSData *data = [NSData dataWithContentsOfURL:url]; 77     KMImageData *imageData = [[KMImageData alloc] initWithData:data 78                              withIndex:imageIndex]; 79     return imageData; 80   } 81 } 82  83 - (void)loadImageFromNetwork:(NSNumber *)imageIndex { 84   NSLog(@"Current thread:%@",[NSThread currentThread]); 85    86   /*将数据显示到UI控件,注意只能在主线程中更新UI; 87    另外 performSelectorOnMainThread 方法是 NSObject 的分类方法,每个 NSObject 对象都有此方法; 88    它调用的 selector 方法是当前调用控件的方法,例如使用 UIImageView 调用的时候 selector 就是 UIImageView 的方法 89    withObject:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装) 90    waitUntilDone:是否线程任务完成执行 91   */ 92   [self performSelectorOnMainThread:@selector(updateImage:) 93               withObject:[self requestData:[imageIndex integerValue]] 94             waitUntilDone:YES]; 95 } 96  97 - (IBAction)loadImage:(id)sender { 98   for (NSUInteger i=0, len=kRowCount * kColumnCount; i<len; i++) { 99     NSThread *thread = [[NSThread alloc] initWithTarget:self100                           selector:@selector(loadImageFromNetwork:)101                           object:[NSNumber numberWithInteger:i]];102     thread.name = [NSString stringWithFormat:@"Thread %lu", (unsigned long)i];103     thread.threadPriority = i == len-1 ? 1.0 : 0.0; //设置线程优先级;0.0-1.0,值越高优先级越高,这样可以提高他被优先加载的机率,但是他也未必就第一个加载。因为首先其他线程是先启动的,其次网络状况我们没办法修改104     [thread start];105   }106 }107 108 @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>

ThirdSampleViewController.h

 1 #import <UIKit/UIKit.h> 2  3 @interface ThirdSampleViewController : UIViewController 4 @property (assign, nonatomic) CGSize rescaleImageSize; 5 @property (strong, nonatomic) NSMutableArray *mArrImageView; 6 @property (strong, nonatomic) NSMutableArray *mArrThread; 7  8 @property (strong, nonatomic) IBOutlet UIButton *btnLoadImage; 9 @property (strong, nonatomic) IBOutlet UIButton *btnStopLoadImage;10 11 @end

ThirdSampleViewController.m

 1 #import "ThirdSampleViewController.h" 2 #import "KMImageData.h" 3 #import "UIImage+RescaleImage.h" 4  5 #define kRowCount 4 6 #define kColumnCount 3 7 #define kCellSpacing 10.0 8  9 @interface ThirdSampleViewController () 10 - (void)layoutUI; 11 - (void)updateImage:(KMImageData *)imageData; 12 -(KMImageData *)requestData:(NSInteger)imageIndex; 13 - (void)loadImageFromNetwork:(NSNumber *)imageIndex; 14 @end 15  16 @implementation ThirdSampleViewController 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   _mArrThread = nil; 32 } 33  34 - (void)layoutUI { 35   CGFloat width = ([[UIScreen mainScreen] bounds].size.width - ((kColumnCount + 1) * kCellSpacing)) / kColumnCount; 36   _rescaleImageSize = CGSizeMake(width, width); 37    38   CGFloat heightOfStatusAndNav = 20.0 + 44.0; 39   NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"]; 40   UIImage *img = [UIImage imageWithContentsOfFile:path]; 41   _mArrImageView = [NSMutableArray arrayWithCapacity:kRowCount * kColumnCount]; 42   //初始化多个图片视图 43   for (NSUInteger i=0; i<kRowCount; i++) { 44     for (NSUInteger j=0; j<kColumnCount; j++) { 45       UIImageView *imgV = [[UIImageView alloc] initWithFrame: 46                 CGRectMake(_rescaleImageSize.width * j + kCellSpacing * (j+1), 47                       _rescaleImageSize.height * i + kCellSpacing * (i+1) + heightOfStatusAndNav, 48                       _rescaleImageSize.width, 49                       _rescaleImageSize.height)]; 50       imgV.image = img; 51       [self.view addSubview:imgV]; 52       [_mArrImageView addObject:imgV]; 53     } 54   } 55    56   void (^beautifulButton)(UIButton *, UIColor *) = ^(UIButton *btn, UIColor *tintColor) { 57     btn.tintColor = tintColor; 58     btn.layer.masksToBounds = YES; 59     btn.layer.cornerRadius = 10.0; 60     btn.layer.borderColor = [UIColor grayColor].CGColor; 61     btn.layer.borderWidth = 1.0; 62   }; 63    64   beautifulButton(_btnLoadImage, [UIColor darkGrayColor]); 65   beautifulButton(_btnStopLoadImage, [UIColor redColor]); 66 } 67  68 - (void)updateImage:(KMImageData *)imageData { 69   UIImage *img = [UIImage imageWithData:imageData.data]; 70   UIImageView *imgVCurrent = _mArrImageView[imageData.index]; 71   imgVCurrent.image = [img rescaleImageToSize:_rescaleImageSize]; 72 } 73  74 -(KMImageData *)requestData:(NSInteger)imageIndex { 75   //对于多线程操作,建议把线程操作放到 @autoreleasepool 中 76   @autoreleasepool { 77     if (imageIndex != kRowCount * kColumnCount - 1) { //虽然设置了最后一个线程的优先级为1.0,但无法保证他是第一个加载的。所以我们这里让其他线程挂起0.5秒,这样基本能保证最后一个线程第一个加载,除非网络非常差的情况 78       [NSThread sleepForTimeInterval:0.5]; 79     } 80      81     NSURL *url = [NSURL URLWithString:@"http://images.apple.com/v/macbook/c/overview/images/hero_static_large.jpg"]; 82     NSData *data = [NSData dataWithContentsOfURL:url]; 83     KMImageData *imageData = [[KMImageData alloc] initWithData:data 84                              withIndex:imageIndex]; 85     return imageData; 86   } 87 } 88  89 - (void)loadImageFromNetwork:(NSNumber *)imageIndex { //子线程中执行 90   //线程状态:thread.isExecuting(是否执行中)、thread.isFinished(是否已完成)、thread.isCancelled(是否已取消) 91   //thread.isMainThread(是否主线程)、[NSThread isMultiThreaded](是否多线程) 92    93   KMImageData *imageData = [self requestData:[imageIndex integerValue]]; 94    95   NSThread *thread = _mArrThread[[imageIndex integerValue]]; 96   if (thread.isCancelled) { //判断线程是否已经取消,如是就退出当前线程;这里注意需让 exit 操作有足够的时间进行占用资源的释放,否则有可能出现异常;比如 Demo 中:当点击「停止加载」按钮后,再次快速点击「加载网络图片」按钮 97     NSLog(@"Current thread:%@ will be cancelled.", thread); 98     [NSThread exit]; 99   } else {100 //    //在后台执行一个方法,本质就是重新创建一个线程来执行方法101 //    [self performSelectorInBackground:@selector(updateImage:) withObject:imageData];102 //    103 //    /*将数据显示到UI控件,注意只能在主线程中更新UI;104 //     另外 performSelectorOnMainThread 方法是 NSObject 的分类方法,每个 NSObject 对象都有此方法;它调用的 selector 方法是当前调用控件的方法,例如使用 UIImageView 调用的时候 selector 就是 UIImageView 的方法105 //     withObject:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)106 //     waitUntilDone:是否线程任务完成后执行107 //     */108 //    [self performSelectorOnMainThread:@selector(updateImage:)109 //                withObject:imageData110 //              waitUntilDone:YES];111     112     //在指定的线程上执行一个方法,需要用户创建一个线程对象实例113     [self performSelector:@selector(updateImage:)114            onThread:thread115           withObject:imageData116         waitUntilDone:YES];117   }118 }119 120 - (IBAction)loadImage:(id)sender {121   NSUInteger len = kRowCount * kColumnCount;122   _mArrThread = [NSMutableArray arrayWithCapacity:len];123   124   //循环创建多个线程125   for (NSUInteger i=0; i<len; i++) {126     NSThread *thread = [[NSThread alloc] initWithTarget:self127                           selector:@selector(loadImageFromNetwork:)128                           object:[NSNumber numberWithInteger:i]];129     thread.name = [NSString stringWithFormat:@"Thread %lu", (unsigned long)i];130     thread.threadPriority = i == len-1 ? 1.0 : 0.0; //设置线程优先级;0.0-1.0,值越高优先级越高,这样可以提高他被优先加载的机率,但是他也未必就第一个加载。因为首先其他线程是先启动的,其次网络状况我们没办法修改131     [_mArrThread addObject:thread];132   }133   134   //循环启动线程135   for (NSUInteger i=0; i<len; i++) {136     NSThread *thread = _mArrThread[i];137     [thread start];138   }139 }140 141 - (IBAction)stopLoadImage:(id)sender { //主线程中执行142   for (NSUInteger i=0, len=kRowCount * kColumnCount; i<len; i++) {143     NSThread *thread = _mArrThread[i];144     if (!thread.isFinished) { //判断线程是否完成,如果没有完成则设置为取消状态;取消状态仅仅是改变了线程状态而言,并不能终止线程145       [thread cancel];146     }147   }148 }149 150 @end

ThirdSampleViewController.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="ThirdSampleViewController"> 9       <connections>10         <outlet property="btnLoadImage" destination="F5h-ZI-gGL" id="I40-e2-bAa"/>11         <outlet property="btnStopLoadImage" destination="gnu-KE-bGq" id="tOA-t9-OA9"/>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         <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F5h-ZI-gGL">21           <rect key="frame" x="20" y="530" width="130" height="50"/>22           <constraints>23             <constraint firstAttribute="height" constant="50" id="HWd-Xc-Wk7"/>24             <constraint firstAttribute="width" constant="130" id="vrH-qE-PNK"/>25           </constraints>26           <state key="normal" title="加载网络图片">27             <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>28           </state>29           <connections>30             <action selector="loadImage:" destination="-1" eventType="touchUpInside" id="Hgw-q8-lHy"/>31           </connections>32         </button>33         <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gnu-KE-bGq">34           <rect key="frame" x="450" y="530" width="130" height="50"/>35           <constraints>36             <constraint firstAttribute="height" constant="50" id="1R7-22-hMk"/>37             <constraint firstAttribute="width" constant="130" id="64l-yF-IFO"/>38           </constraints>39           <state key="normal" title="停止加载">40             <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>41           </state>42           <connections>43             <action selector="loadImage:" destination="-1" eventType="touchUpInside" id="PyX-RW-cbL"/>44             <action selector="stopLoadImage:" destination="-1" eventType="touchUpInside" id="1Xa-Ek-D4B"/>45           </connections>46         </button>47       </subviews>48       <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>49       <constraints>50         <constraint firstItem="F5h-ZI-gGL" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="20" id="Glo-vW-SXd"/>51         <constraint firstAttribute="trailing" secondItem="gnu-KE-bGq" secondAttribute="trailing" constant="20" id="IdF-1v-bg2"/>52         <constraint firstAttribute="bottom" secondItem="gnu-KE-bGq" secondAttribute="bottom" constant="20" id="cyx-dg-K8M"/>53         <constraint firstAttribute="bottom" secondItem="F5h-ZI-gGL" secondAttribute="bottom" constant="20" id="jPY-fY-9XJ"/>54         <constraint firstAttribute="centerX" secondItem="F5h-ZI-gGL" secondAttribute="centerX" id="rH1-sV-pST"/>55       </constraints>56       <variation key="default">57         <mask key="constraints">58           <exclude reference="rH1-sV-pST"/>59         </mask>60       </variation>61     </view>62   </objects>63 </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-24 22:18:38.945 NSThreadDemo[5361:204621] INFO: Reveal Server started (Protocol Version 18). 2 2015-08-24 22:18:57.863 NSThreadDemo[5361:204847] Current thread:<NSThread: 0x7f9f60cd1f80>{number = 5, name = Thread 0} 3 2015-08-24 22:18:57.863 NSThreadDemo[5361:204848] Current thread:<NSThread: 0x7f9f60c74800>{number = 6, name = Thread 1} 4 2015-08-24 22:18:57.863 NSThreadDemo[5361:204849] Current thread:<NSThread: 0x7f9f60cb8f30>{number = 7, name = Thread 2} 5 2015-08-24 22:18:57.863 NSThreadDemo[5361:204850] Current thread:<NSThread: 0x7f9f60cb8500>{number = 8, name = Thread 3} 6 2015-08-24 22:18:57.864 NSThreadDemo[5361:204851] Current thread:<NSThread: 0x7f9f60cd0c60>{number = 9, name = Thread 4} 7 2015-08-24 22:18:57.865 NSThreadDemo[5361:204857] Current thread:<NSThread: 0x7f9f60dee540>{number = 11, name = Thread 6} 8 2015-08-24 22:18:57.864 NSThreadDemo[5361:204856] Current thread:<NSThread: 0x7f9f60cd2150>{number = 10, name = Thread 5} 9 2015-08-24 22:18:57.866 NSThreadDemo[5361:204859] Current thread:<NSThread: 0x7f9f60daf730>{number = 13, name = Thread 8}10 2015-08-24 22:18:57.866 NSThreadDemo[5361:204860] Current thread:<NSThread: 0x7f9f60db8530>{number = 14, name = Thread 9}11 2015-08-24 22:18:57.866 NSThreadDemo[5361:204862] Current thread:<NSThread: 0x7f9f60f3f7e0>{number = 16, name = Thread 11}12 2015-08-24 22:18:57.866 NSThreadDemo[5361:204858] Current thread:<NSThread: 0x7f9f60db2390>{number = 12, name = Thread 7}13 2015-08-24 22:18:57.867 NSThreadDemo[5361:204861] Current thread:<NSThread: 0x7f9f60f231c0>{number = 15, name = Thread 10}14 15 2015-08-24 22:19:07.073 NSThreadDemo[5361:204952] Current thread:<NSThread: 0x7f9f63010930>{number = 39, name = Thread 10} will be cancelled.16 2015-08-24 22:19:07.691 NSThreadDemo[5361:204951] Current thread:<NSThread: 0x7f9f60df0210>{number = 38, name = Thread 9} will be cancelled.17 2015-08-24 22:19:07.697 NSThreadDemo[5361:204957] Current thread:<NSThread: 0x7f9f60db8ee0>{number = 29, name = Thread 0} will be cancelled.18 2015-08-24 22:19:07.729 NSThreadDemo[5361:204958] Current thread:<NSThread: 0x7f9f60db2390>{number = 30, name = Thread 1} will be cancelled.19 2015-08-24 22:19:07.857 NSThreadDemo[5361:204949] Current thread:<NSThread: 0x7f9f63011190>{number = 36, name = Thread 7} will be cancelled.20 2015-08-24 22:19:08.025 NSThreadDemo[5361:204960] Current thread:<NSThread: 0x7f9f6300c2d0>{number = 32, name = Thread 3} will be cancelled.21 2015-08-24 22:19:08.047 NSThreadDemo[5361:204959] Current thread:<NSThread: 0x7f9f60dd4200>{number = 31, name = Thread 2} will be cancelled.22 2015-08-24 22:19:08.191 NSThreadDemo[5361:204942] Current thread:<NSThread: 0x7f9f60db8ee0>{number = 29, name = Thread 0} will be cancelled.23 2015-08-24 22:19:08.250 NSThreadDemo[5361:204965] Current thread:<NSThread: 0x7f9f630110b0>{number = 37, name = Thread 8} will be cancelled.24 2015-08-24 22:19:08.416 NSThreadDemo[5361:204962] Current thread:<NSThread: 0x7f9f60df7bc0>{number = 34, name = Thread 5} will be cancelled.25 2015-08-24 22:19:08.464 NSThreadDemo[5361:204963] Current thread:<NSThread: 0x7f9f60dc5ca0>{number = 35, name = Thread 6} will be cancelled.26 2015-08-24 22:19:08.495 NSThreadDemo[5361:204964] Current thread:<NSThread: 0x7f9f63011190>{number = 36, name = Thread 7} will be cancelled.27 2015-08-24 22:19:08.661 NSThreadDemo[5361:204966] Current thread:<NSThread: 0x7f9f60df0210>{number = 38, name = Thread 9} will be cancelled.28 2015-08-24 22:19:10.033 NSThreadDemo[5361:204961] Current thread:<NSThread: 0x7f9f60da3330>{number = 33, name = Thread 4} will be cancelled.29 2015-08-24 22:19:10.056 NSThreadDemo[5361:204967] Current thread:<NSThread: 0x7f9f63010930>{number = 39, name = Thread 10} will be cancelled.