zoukankan      html  css  js  c++  java
  • 多线程开发之一 NSThread

    每个 iOS 应用程序都有个专门用来更新显示 UI 界面、处理用户的触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带来不好的用户体验。

    一般的解决方案就是:将那些耗时的操作放到另外一个线程中去执行,多线程编程就是防止主线程堵塞和增加运行效率的最佳方法。

    iOS 支持多个层次的多线程编程,层次越高的抽象程度越高,使用也越方便,也是 Apple 最推荐使用的方法。

    ​下面根据抽象层次从低到高依次列出 iOS 所支持的多线程编程方法:

    1. NSThread :是三种方法里面相对轻量级的,但需要管理线程的生命周期、同步、加锁问题,这会导致一定的性能开销

    2. NSOperation:是基于 OC 实现的,NSOperation 以面向对象的方式封装了需要执行的操作,不必关心线程管理、同步等问题。

      NSOperation 是一个抽象基类,iOS 提供了两种默认实现:NSInvocationOperation 和 NSBlockOperation,当然也可以自定义 NSOperation

    3. Grand Central Dispatch(简称 GCD ,iOS4 才开始支持):提供了一些新特性、运行库来支持多核并行编程,他的关注点更高:如何在多个 CPU 上提升效率

    效果如下:

    ViewController.h

    1 #import <UIKit/UIKit.h>
    2 
    3 @interface ViewController : UITableViewController
    4 @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 #import "ThirdSampleViewController.h"
     5 
     6 @interface ViewController ()
     7 - (void)layoutUI;
     8 @end
     9 
    10 @implementation ViewController
    11 - (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 : NSObject
    4 @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 : UIViewController
    4 @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 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     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:data
    59                         waitUntilDone:YES];
    60 }
    61 
    62 - (IBAction)loadImage:(id)sender {
    63     //方法一:使用线程对象实例方法创建一个线程
    64     //NSThread *thread = [[NSThread alloc] initWithTarget:self
    65     //                                           selector:@selector(loadImageFromNetwork)
    66     //                                             object:nil];
    67     //[thread start]; //启动一个线程;注意启动一个线程并非就一定立即执行,而是处于就绪状态,当系统调度时才真正执行
    68     
    69     //方法二:使用线程类方法创建一个线程
    70     [NSThread detachNewThreadSelector:@selector(loadImageFromNetwork)
    71                              toTarget:self
    72                            withObject:nil];
    73 }
    74 
    75 @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="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 : UIViewController
    4 @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:self
    100                                                    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 <?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>

    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:imageData
    110 //                            waitUntilDone:YES];
    111         
    112         @try {
    113             //在指定的线程上执行一个方法,需要用户创建一个线程对象实例
    114             [self performSelector:@selector(updateImage:)
    115                          onThread:thread
    116                        withObject:imageData
    117                     waitUntilDone:YES];
    118         }
    119         @catch (NSException *exception) {
    120             NSLog(@"Exception: %@", [exception description]);
    121         }
    122     }
    123 }
    124 
    125 - (IBAction)loadImage:(id)sender {
    126     NSUInteger len = kRowCount * kColumnCount;
    127     _mArrThread = [NSMutableArray arrayWithCapacity:len];
    128     
    129     //循环创建多个线程
    130     for (NSUInteger i=0; i<len; i++) {
    131         NSThread *thread = [[NSThread alloc] initWithTarget:self
    132                                                    selector:@selector(loadImageFromNetwork:)
    133                                                      object:[NSNumber numberWithInteger:i]];
    134         thread.name = [NSString stringWithFormat:@"Thread %lu", (unsigned long)i];
    135         thread.threadPriority = i == len-1 ? 1.0 : 0.0; //设置线程优先级;0.0-1.0,值越高优先级越高,这样可以提高他被优先加载的机率,但是他也未必就第一个加载。因为首先其他线程是先启动的,其次网络状况我们没办法修改
    136         [_mArrThread addObject:thread];
    137     }
    138     
    139     //循环启动线程
    140     for (NSUInteger i=0; i<len; i++) {
    141         NSThread *thread = _mArrThread[i];
    142         [thread start];
    143     }
    144 }
    145 
    146 - (IBAction)stopLoadImage:(id)sender { //主线程中执行
    147     for (NSUInteger i=0, len=kRowCount * kColumnCount; i<len; i++) {
    148         NSThread *thread = _mArrThread[i];
    149         if (!thread.isFinished) { //判断线程是否完成,如果没有完成则设置为取消状态;取消状态仅仅是改变了线程状态而言,并不能终止线程
    150             [thread cancel];
    151         }
    152     }
    153 }
    154 
    155 @end

    ThirdSampleViewController.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="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.
  • 相关阅读:
    error:undefined reference to 'net_message_processor::net_message_processor()'
    android 网络检测
    eclipse 安装 ndk 组件
    eclipse下编译cocos2dx 3.0
    Cocos2dx3.0 TextField 输入中文的问题
    记录与骗子进行的一次交锋. 与技术无关
    关于继承的设计
    kubernetes1.5.2--部署dashboard服务
    kubernetes1.5.2--部署DNS服务
    kubernetes1.5.2集群部署过程--安全模式
  • 原文地址:https://www.cnblogs.com/huangjianwu/p/4756197.html
Copyright © 2011-2022 走看看