每个 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

 #import <UIKit/UIKit.h>

 @interface ViewController : UITableViewController
@property (copy, nonatomic) NSArray *arrSampleName; - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName; @end

ViewController.m

 #import "ViewController.h"
#import "FirstSampleViewController.h"
#import "SecondSampleViewController.h"
#import "ThirdSampleViewController.h" @interface ViewController ()
- (void)layoutUI;
@end @implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad]; [self layoutUI];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName {
if (self = [super initWithStyle:UITableViewStyleGrouped]) {
self.navigationItem.title = @"多线程开发之一 NSThread";
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回首页" style:UIBarButtonItemStylePlain target:nil action:nil]; _arrSampleName = arrSampleName;
}
return self;
} - (void)layoutUI { } #pragma mark - UITableViewController相关方法重写
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 0.1;
} - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_arrSampleName count];
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = _arrSampleName[indexPath.row];
return cell;
} - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case : {
FirstSampleViewController *firstSampleVC = [FirstSampleViewController new];
[self.navigationController pushViewController:firstSampleVC animated:YES];
break;
}
case : {
SecondSampleViewController *secondSampleVC = [SecondSampleViewController new];
[self.navigationController pushViewController:secondSampleVC animated:YES];
break;
}
case : {
ThirdSampleViewController *thirdSampleVC = [ThirdSampleViewController new];
[self.navigationController pushViewController:thirdSampleVC animated:YES];
break; /*
类似堆栈的先进后出的原理:
返回到(上一级)、(任意级)、(根级)导航
[self.navigationController popViewControllerAnimated:YES];
[self.navigationController popToViewController:thirdSampleVC animated:YES];
[self.navigationController popToRootViewControllerAnimated:YES];
*/
}
default:
break;
}
} @end

UIImage+RescaleImage.h

 #import <UIKit/UIKit.h>

 @interface UIImage (RescaleImage)
/**
* 根据宽高大小,获取对应的缩放图片
*
* @param size 宽高大小
*
* @return 对应的缩放图片
*/
- (UIImage *)rescaleImageToSize:(CGSize)size; @end

UIImage+RescaleImage.m

 #import "UIImage+RescaleImage.h"

 @implementation UIImage (RescaleImage)

 - (UIImage *)rescaleImageToSize:(CGSize)size {
CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height); UIGraphicsBeginImageContext(rect.size);
[self drawInRect:rect];
UIImage *imgScale = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext(); return imgScale;
} @end

KMImageData.h

 #import <Foundation/Foundation.h>

 @interface KMImageData : NSObject
@property (assign, nonatomic) NSInteger index;
@property (strong, nonatomic) NSData *data; - (instancetype)initWithData:(NSData *)data withIndex:(NSInteger)index; @end

KMImageData.m

 #import "KMImageData.h"

 @implementation KMImageData

 - (instancetype)initWithData:(NSData *)data withIndex:(NSInteger)index {
if (self = [super init]) {
_data = data;
_index = index;
}
return self;
} @end

FirstSampleViewController.h

 #import <UIKit/UIKit.h>

 @interface FirstSampleViewController : UIViewController
@property (assign, nonatomic) CGSize rescaleImageSize; @property (strong, nonatomic) IBOutlet UIImageView *imgV;
@property (strong, nonatomic) IBOutlet UIButton *btnLoadImage; @end

FirstSampleViewController.m

 #import "FirstSampleViewController.h"
#import "UIImage+RescaleImage.h" @interface FirstSampleViewController ()
- (void)layoutUI;
- (void)updateImage:(NSData *)imageData;
- (void)loadImageFromNetwork;
@end @implementation FirstSampleViewController - (void)viewDidLoad {
[super viewDidLoad]; [self layoutUI];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} - (void)dealloc {
_imgV.image = nil;
} - (void)layoutUI {
CGFloat width = [[UIScreen mainScreen] bounds].size.width; //bounds 返回整个屏幕大小;applicationFrame 返回去除状态栏后的屏幕大小
CGFloat height = width * 150.0 / 190.0;
_rescaleImageSize = CGSizeMake(width, height); //NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"];
//_imgV.image = [UIImage imageWithContentsOfFile:path]; _btnLoadImage.tintColor = [UIColor darkGrayColor];
_btnLoadImage.layer.masksToBounds = YES;
_btnLoadImage.layer.cornerRadius = 10.0;
_btnLoadImage.layer.borderColor = [UIColor grayColor].CGColor;
_btnLoadImage.layer.borderWidth = 1.0;
} - (void)updateImage:(NSData *)imageData {
UIImage *img = [UIImage imageWithData:imageData];
_imgV.image = [img rescaleImageToSize:_rescaleImageSize];
} - (void)loadImageFromNetwork {
NSURL *url = [NSURL URLWithString:@"http://images.apple.com/v/macbook/c/overview/images/hero_static_large.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url]; /*将数据显示到UI控件,注意只能在主线程中更新UI;
另外 performSelectorOnMainThread 方法是 NSObject 的分类方法,每个 NSObject 对象都有此方法;
它调用的 selector 方法是当前调用控件的方法,例如使用 UIImageView 调用的时候 selector 就是 UIImageView 的方法
withObject:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)
waitUntilDone:是否线程任务完成执行
*/
[self performSelectorOnMainThread:@selector(updateImage:)
withObject:data
waitUntilDone:YES];
} - (IBAction)loadImage:(id)sender {
//方法一:使用线程对象实例方法创建一个线程
//NSThread *thread = [[NSThread alloc] initWithTarget:self
// selector:@selector(loadImageFromNetwork)
// object:nil];
//[thread start]; //启动一个线程;注意启动一个线程并非就一定立即执行,而是处于就绪状态,当系统调度时才真正执行 //方法二:使用线程类方法创建一个线程
[NSThread detachNewThreadSelector:@selector(loadImageFromNetwork)
toTarget:self
withObject:nil];
} @end

FirstSampleViewController.xib

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="FirstSampleViewController">
<connections>
<outlet property="btnLoadImage" destination="sLs-f1-Gzc" id="kX8-J0-v0V"/>
<outlet property="imgV" destination="4Qp-uk-KAb" id="RM3-Ha-glh"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="PictureNo.png" translatesAutoresizingMaskIntoConstraints="NO" id="4Qp-uk-KAb">
<rect key="frame" x="205" y="225" width="190" height="150"/>
<constraints>
<constraint firstAttribute="height" constant="150" id="SIp-Wd-idU"/>
<constraint firstAttribute="height" constant="150" id="VwM-i1-atB"/>
<constraint firstAttribute="width" constant="190" id="mUh-Bu-tUd"/>
<constraint firstAttribute="width" constant="190" id="mdJ-1c-QFa"/>
<constraint firstAttribute="width" constant="190" id="sVS-bU-Ty9"/>
<constraint firstAttribute="height" constant="150" id="uMG-oN-J56"/>
<constraint firstAttribute="height" constant="150" id="vws-Qw-UrB"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="SIp-Wd-idU"/>
<exclude reference="VwM-i1-atB"/>
<exclude reference="mUh-Bu-tUd"/>
<exclude reference="mdJ-1c-QFa"/>
<exclude reference="sVS-bU-Ty9"/>
<exclude reference="uMG-oN-J56"/>
<exclude reference="vws-Qw-UrB"/>
</mask>
</variation>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sLs-f1-Gzc">
<rect key="frame" x="230" y="500" width="140" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="140" id="1jv-9K-mdH"/>
<constraint firstAttribute="height" constant="50" id="Q2w-vR-9ac"/>
</constraints>
<state key="normal" title="加载网络图片">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="loadImage:" destination="-1" eventType="touchUpInside" id="fdy-Ln-5oS"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="4Qp-uk-KAb" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="205" id="2a2-mS-WFa"/>
<constraint firstItem="sLs-f1-Gzc" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="ES4-wl-RBz"/>
<constraint firstItem="4Qp-uk-KAb" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" id="MUJ-WA-sUf"/>
<constraint firstItem="4Qp-uk-KAb" firstAttribute="centerX" secondItem="sLs-f1-Gzc" secondAttribute="centerX" id="Q8a-1k-DzJ"/>
<constraint firstAttribute="bottom" secondItem="4Qp-uk-KAb" secondAttribute="bottom" constant="71" id="V0a-9y-Dwa"/>
<constraint firstAttribute="bottom" secondItem="sLs-f1-Gzc" secondAttribute="bottom" constant="50" id="VMG-CV-eeq"/>
<constraint firstItem="4Qp-uk-KAb" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="-71" id="gqW-Wq-4Zv"/>
<constraint firstItem="sLs-f1-Gzc" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="kNf-6d-EJ8"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="2a2-mS-WFa"/>
<exclude reference="V0a-9y-Dwa"/>
<exclude reference="gqW-Wq-4Zv"/>
<exclude reference="ES4-wl-RBz"/>
</mask>
</variation>
</view>
</objects>
<resources>
<image name="PictureNo.png" width="190" height="150"/>
</resources>
</document>

SecondSampleViewController.h

 #import <UIKit/UIKit.h>

 @interface SecondSampleViewController : UIViewController
@property (assign, nonatomic) CGSize rescaleImageSize;
@property (strong, nonatomic) NSMutableArray *mArrImageView; @property (strong, nonatomic) IBOutlet UIButton *btnLoadImage; @end

SecondSampleViewController.m

 #import "SecondSampleViewController.h"
#import "KMImageData.h"
#import "UIImage+RescaleImage.h" #define kRowCount 4
#define kColumnCount 3
#define kCellSpacing 10.0 @interface SecondSampleViewController ()
- (void)layoutUI;
- (void)updateImage:(KMImageData *)imageData;
-(KMImageData *)requestData:(NSInteger)imageIndex;
- (void)loadImageFromNetwork:(NSNumber *)imageIndex;
@end @implementation SecondSampleViewController - (void)viewDidLoad {
[super viewDidLoad]; [self layoutUI];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} - (void)dealloc {
_mArrImageView = nil;
} - (void)layoutUI {
CGFloat width = ([[UIScreen mainScreen] bounds].size.width - ((kColumnCount + ) * kCellSpacing)) / kColumnCount;
_rescaleImageSize = CGSizeMake(width, width); CGFloat heightOfStatusAndNav = 20.0 + 44.0;
NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"];
UIImage *img = [UIImage imageWithContentsOfFile:path];
_mArrImageView = [NSMutableArray arrayWithCapacity:kRowCount * kColumnCount];
//初始化多个图片视图
for (NSUInteger i=; i<kRowCount; i++) {
for (NSUInteger j=; j<kColumnCount; j++) {
UIImageView *imgV = [[UIImageView alloc] initWithFrame:
CGRectMake(_rescaleImageSize.width * j + kCellSpacing * (j+),
_rescaleImageSize.height * i + kCellSpacing * (i+) + heightOfStatusAndNav,
_rescaleImageSize.width,
_rescaleImageSize.height)];
imgV.image = img;
[self.view addSubview:imgV];
[_mArrImageView addObject:imgV];
}
} _btnLoadImage.tintColor = [UIColor darkGrayColor];
_btnLoadImage.layer.masksToBounds = YES;
_btnLoadImage.layer.cornerRadius = 10.0;
_btnLoadImage.layer.borderColor = [UIColor grayColor].CGColor;
_btnLoadImage.layer.borderWidth = 1.0;
} - (void)updateImage:(KMImageData *)imageData {
UIImage *img = [UIImage imageWithData:imageData.data];
UIImageView *imgVCurrent = _mArrImageView[imageData.index];
imgVCurrent.image = [img rescaleImageToSize:_rescaleImageSize];
} -(KMImageData *)requestData:(NSInteger)imageIndex {
//对于多线程操作,建议把线程操作放到 @autoreleasepool 中
@autoreleasepool {
if (imageIndex != kRowCount * kColumnCount - ) { //虽然设置了最后一个线程的优先级为1.0,但无法保证他是第一个加载的。所以我们这里让其他线程挂起0.5秒,这样基本能保证最后一个线程第一个加载,除非网络非常差的情况
[NSThread sleepForTimeInterval:0.5];
} NSURL *url = [NSURL URLWithString:@"http://images.apple.com/v/macbook/c/overview/images/hero_static_large.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
KMImageData *imageData = [[KMImageData alloc] initWithData:data
withIndex:imageIndex];
return imageData;
}
} - (void)loadImageFromNetwork:(NSNumber *)imageIndex {
NSLog(@"Current thread:%@",[NSThread currentThread]); /*将数据显示到UI控件,注意只能在主线程中更新UI;
另外 performSelectorOnMainThread 方法是 NSObject 的分类方法,每个 NSObject 对象都有此方法;
它调用的 selector 方法是当前调用控件的方法,例如使用 UIImageView 调用的时候 selector 就是 UIImageView 的方法
withObject:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)
waitUntilDone:是否线程任务完成执行
*/
[self performSelectorOnMainThread:@selector(updateImage:)
withObject:[self requestData:[imageIndex integerValue]]
waitUntilDone:YES];
} - (IBAction)loadImage:(id)sender {
for (NSUInteger i=, len=kRowCount * kColumnCount; i<len; i++) {
NSThread *thread = [[NSThread alloc] initWithTarget:self
selector:@selector(loadImageFromNetwork:)
object:[NSNumber numberWithInteger:i]];
thread.name = [NSString stringWithFormat:@"Thread %lu", (unsigned long)i];
thread.threadPriority = i == len- ? 1.0 : 0.0; //设置线程优先级;0.0-1.0,值越高优先级越高,这样可以提高他被优先加载的机率,但是他也未必就第一个加载。因为首先其他线程是先启动的,其次网络状况我们没办法修改
[thread start];
}
} @end

SecondSampleViewController.xib

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="SecondSampleViewController">
<connections>
<outlet property="btnLoadImage" destination="F5h-ZI-gGL" id="I40-e2-bAa"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F5h-ZI-gGL">
<rect key="frame" x="230" y="530" width="140" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="HWd-Xc-Wk7"/>
<constraint firstAttribute="width" constant="140" id="vrH-qE-PNK"/>
</constraints>
<state key="normal" title="加载网络图片">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="loadImage:" destination="-1" eventType="touchUpInside" id="Hgw-q8-lHy"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="F5h-ZI-gGL" secondAttribute="bottom" constant="20" id="jPY-fY-9XJ"/>
<constraint firstAttribute="centerX" secondItem="F5h-ZI-gGL" secondAttribute="centerX" id="rH1-sV-pST"/>
</constraints>
</view>
</objects>
</document>

ThirdSampleViewController.h

 #import <UIKit/UIKit.h>

 @interface ThirdSampleViewController : UIViewController
@property (assign, nonatomic) CGSize rescaleImageSize;
@property (strong, nonatomic) NSMutableArray *mArrImageView;
@property (strong, nonatomic) NSMutableArray *mArrThread; @property (strong, nonatomic) IBOutlet UIButton *btnLoadImage;
@property (strong, nonatomic) IBOutlet UIButton *btnStopLoadImage; @end

ThirdSampleViewController.m

 #import "ThirdSampleViewController.h"
#import "KMImageData.h"
#import "UIImage+RescaleImage.h" #define kRowCount 4
#define kColumnCount 3
#define kCellSpacing 10.0 @interface ThirdSampleViewController ()
- (void)layoutUI;
- (void)updateImage:(KMImageData *)imageData;
-(KMImageData *)requestData:(NSInteger)imageIndex;
- (void)loadImageFromNetwork:(NSNumber *)imageIndex;
@end @implementation ThirdSampleViewController - (void)viewDidLoad {
[super viewDidLoad]; [self layoutUI];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} - (void)dealloc {
_mArrImageView = nil;
_mArrThread = nil;
} - (void)layoutUI {
CGFloat width = ([[UIScreen mainScreen] bounds].size.width - ((kColumnCount + ) * kCellSpacing)) / kColumnCount;
_rescaleImageSize = CGSizeMake(width, width); CGFloat heightOfStatusAndNav = 20.0 + 44.0;
NSString *path = [[NSBundle mainBundle] pathForResource:@"PictureNo@2x" ofType:@"png"];
UIImage *img = [UIImage imageWithContentsOfFile:path];
_mArrImageView = [NSMutableArray arrayWithCapacity:kRowCount * kColumnCount];
//初始化多个图片视图
for (NSUInteger i=; i<kRowCount; i++) {
for (NSUInteger j=; j<kColumnCount; j++) {
UIImageView *imgV = [[UIImageView alloc] initWithFrame:
CGRectMake(_rescaleImageSize.width * j + kCellSpacing * (j+),
_rescaleImageSize.height * i + kCellSpacing * (i+) + heightOfStatusAndNav,
_rescaleImageSize.width,
_rescaleImageSize.height)];
imgV.image = img;
[self.view addSubview:imgV];
[_mArrImageView addObject:imgV];
}
} void (^beautifulButton)(UIButton *, UIColor *) = ^(UIButton *btn, UIColor *tintColor) {
btn.tintColor = tintColor;
btn.layer.masksToBounds = YES;
btn.layer.cornerRadius = 10.0;
btn.layer.borderColor = [UIColor grayColor].CGColor;
btn.layer.borderWidth = 1.0;
}; beautifulButton(_btnLoadImage, [UIColor darkGrayColor]);
beautifulButton(_btnStopLoadImage, [UIColor redColor]);
} - (void)updateImage:(KMImageData *)imageData {
UIImage *img = [UIImage imageWithData:imageData.data];
UIImageView *imgVCurrent = _mArrImageView[imageData.index];
imgVCurrent.image = [img rescaleImageToSize:_rescaleImageSize];
} -(KMImageData *)requestData:(NSInteger)imageIndex {
//对于多线程操作,建议把线程操作放到 @autoreleasepool 中
@autoreleasepool {
if (imageIndex != kRowCount * kColumnCount - ) { //虽然设置了最后一个线程的优先级为1.0,但无法保证他是第一个加载的。所以我们这里让其他线程挂起0.5秒,这样基本能保证最后一个线程第一个加载,除非网络非常差的情况
[NSThread sleepForTimeInterval:0.5];
} NSURL *url = [NSURL URLWithString:@"http://images.apple.com/v/macbook/c/overview/images/hero_static_large.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
KMImageData *imageData = [[KMImageData alloc] initWithData:data
withIndex:imageIndex];
return imageData;
}
} - (void)loadImageFromNetwork:(NSNumber *)imageIndex { //子线程中执行
//线程状态:thread.isExecuting(是否执行中)、thread.isFinished(是否已完成)、thread.isCancelled(是否已取消)
//thread.isMainThread(是否主线程)、[NSThread isMultiThreaded](是否多线程) KMImageData *imageData = [self requestData:[imageIndex integerValue]]; NSThread *thread = _mArrThread[[imageIndex integerValue]];
if (thread.isCancelled) { //判断线程是否已经取消,如是就退出当前线程;这里注意需让 exit 操作有足够的时间进行占用资源的释放,否则有可能出现异常;比如 Demo 中:当点击「停止加载」按钮后,再次快速点击「加载网络图片」按钮
NSLog(@"Current thread:%@ will be cancelled.", thread);
[NSThread exit];
} else {
// //在后台执行一个方法,本质就是重新创建一个线程来执行方法
// [self performSelectorInBackground:@selector(updateImage:) withObject:imageData];
//
// /*将数据显示到UI控件,注意只能在主线程中更新UI;
// 另外 performSelectorOnMainThread 方法是 NSObject 的分类方法,每个 NSObject 对象都有此方法;它调用的 selector 方法是当前调用控件的方法,例如使用 UIImageView 调用的时候 selector 就是 UIImageView 的方法
// withObject:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)
// waitUntilDone:是否线程任务完成后执行
// */
// [self performSelectorOnMainThread:@selector(updateImage:)
// withObject:imageData
// waitUntilDone:YES]; @try {
//在指定的线程上执行一个方法,需要用户创建一个线程对象实例
[self performSelector:@selector(updateImage:)
onThread:thread
withObject:imageData
waitUntilDone:YES];
}
@catch (NSException *exception) {
NSLog(@"Exception: %@", [exception description]);
}
}
} - (IBAction)loadImage:(id)sender {
NSUInteger len = kRowCount * kColumnCount;
_mArrThread = [NSMutableArray arrayWithCapacity:len]; //循环创建多个线程
for (NSUInteger i=; i<len; i++) {
NSThread *thread = [[NSThread alloc] initWithTarget:self
selector:@selector(loadImageFromNetwork:)
object:[NSNumber numberWithInteger:i]];
thread.name = [NSString stringWithFormat:@"Thread %lu", (unsigned long)i];
thread.threadPriority = i == len- ? 1.0 : 0.0; //设置线程优先级;0.0-1.0,值越高优先级越高,这样可以提高他被优先加载的机率,但是他也未必就第一个加载。因为首先其他线程是先启动的,其次网络状况我们没办法修改
[_mArrThread addObject:thread];
} //循环启动线程
for (NSUInteger i=; i<len; i++) {
NSThread *thread = _mArrThread[i];
[thread start];
}
} - (IBAction)stopLoadImage:(id)sender { //主线程中执行
for (NSUInteger i=, len=kRowCount * kColumnCount; i<len; i++) {
NSThread *thread = _mArrThread[i];
if (!thread.isFinished) { //判断线程是否完成,如果没有完成则设置为取消状态;取消状态仅仅是改变了线程状态而言,并不能终止线程
[thread cancel];
}
}
} @end

ThirdSampleViewController.xib

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ThirdSampleViewController">
<connections>
<outlet property="btnLoadImage" destination="F5h-ZI-gGL" id="I40-e2-bAa"/>
<outlet property="btnStopLoadImage" destination="gnu-KE-bGq" id="tOA-t9-OA9"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F5h-ZI-gGL">
<rect key="frame" x="20" y="530" width="130" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="HWd-Xc-Wk7"/>
<constraint firstAttribute="width" constant="130" id="vrH-qE-PNK"/>
</constraints>
<state key="normal" title="加载网络图片">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="loadImage:" destination="-1" eventType="touchUpInside" id="Hgw-q8-lHy"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gnu-KE-bGq">
<rect key="frame" x="450" y="530" width="130" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="1R7-22-hMk"/>
<constraint firstAttribute="width" constant="130" id="64l-yF-IFO"/>
</constraints>
<state key="normal" title="停止加载">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="loadImage:" destination="-1" eventType="touchUpInside" id="PyX-RW-cbL"/>
<action selector="stopLoadImage:" destination="-1" eventType="touchUpInside" id="1Xa-Ek-D4B"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="F5h-ZI-gGL" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="20" id="Glo-vW-SXd"/>
<constraint firstAttribute="trailing" secondItem="gnu-KE-bGq" secondAttribute="trailing" constant="20" id="IdF-1v-bg2"/>
<constraint firstAttribute="bottom" secondItem="gnu-KE-bGq" secondAttribute="bottom" constant="20" id="cyx-dg-K8M"/>
<constraint firstAttribute="bottom" secondItem="F5h-ZI-gGL" secondAttribute="bottom" constant="20" id="jPY-fY-9XJ"/>
<constraint firstAttribute="centerX" secondItem="F5h-ZI-gGL" secondAttribute="centerX" id="rH1-sV-pST"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="rH1-sV-pST"/>
</mask>
</variation>
</view>
</objects>
</document>

AppDelegate.h

 #import <UIKit/UIKit.h>

 @interface AppDelegate : UIResponder <UIApplicationDelegate>

 @property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UINavigationController *navigationController; @end

AppDelegate.m

 #import "AppDelegate.h"
#import "ViewController.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController *viewController = [[ViewController alloc] initWithSampleNameArray:@[@"请求单张网络图片(解决线程阻塞)", @"请求多张网络图片(多个线程并发)", @"请求多张网络图片(线程状态)"]];
_navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
_window.rootViewController = _navigationController;
//[_window addSubview:_navigationController.view]; //当_window.rootViewController关联时,这一句可有可无
[_window makeKeyAndVisible];
return YES;
} - (void)applicationWillResignActive:(UIApplication *)application {
} - (void)applicationDidEnterBackground:(UIApplication *)application {
} - (void)applicationWillEnterForeground:(UIApplication *)application {
} - (void)applicationDidBecomeActive:(UIApplication *)application {
} - (void)applicationWillTerminate:(UIApplication *)application {
} @end

输出结果:

 -- ::38.945 NSThreadDemo[:]  INFO: Reveal Server started (Protocol Version ).
-- ::57.863 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60cd1f80>{number = , name = Thread }
-- ::57.863 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60c74800>{number = , name = Thread }
-- ::57.863 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60cb8f30>{number = , name = Thread }
-- ::57.863 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60cb8500>{number = , name = Thread }
-- ::57.864 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60cd0c60>{number = , name = Thread }
-- ::57.865 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60dee540>{number = , name = Thread }
-- ::57.864 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60cd2150>{number = , name = Thread }
-- ::57.866 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60daf730>{number = , name = Thread }
-- ::57.866 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60db8530>{number = , name = Thread }
-- ::57.866 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60f3f7e0>{number = , name = Thread }
-- ::57.866 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60db2390>{number = , name = Thread }
-- ::57.867 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60f231c0>{number = , name = Thread } -- ::07.073 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f63010930>{number = , name = Thread } will be cancelled.
-- ::07.691 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60df0210>{number = , name = Thread } will be cancelled.
-- ::07.697 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60db8ee0>{number = , name = Thread } will be cancelled.
-- ::07.729 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60db2390>{number = , name = Thread } will be cancelled.
-- ::07.857 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f63011190>{number = , name = Thread } will be cancelled.
-- ::08.025 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f6300c2d0>{number = , name = Thread } will be cancelled.
-- ::08.047 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60dd4200>{number = , name = Thread } will be cancelled.
-- ::08.191 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60db8ee0>{number = , name = Thread } will be cancelled.
-- ::08.250 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f630110b0>{number = , name = Thread } will be cancelled.
-- ::08.416 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60df7bc0>{number = , name = Thread } will be cancelled.
-- ::08.464 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60dc5ca0>{number = , name = Thread } will be cancelled.
-- ::08.495 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f63011190>{number = , name = Thread } will be cancelled.
-- ::08.661 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60df0210>{number = , name = Thread } will be cancelled.
-- ::10.033 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f60da3330>{number = , name = Thread } will be cancelled.
-- ::10.056 NSThreadDemo[:] Current thread:<NSThread: 0x7f9f63010930>{number = , name = Thread } will be cancelled.

多线程开发之一 NSThread的更多相关文章

  1. iOS多线程开发--NSThread NSOperation GCD

    多线程 当用户播放音频.下载资源.进行图像处理时往往希望做这些事情的时候其他操作不会被中 断或者希望这些操作过程中更加顺畅.在单线程中一个线程只能做一件事情,一件事情处理不完另一件事就不能开始,这样势 ...

  2. iOS多线程开发资源抢夺和线程间的通讯问题

    说到多线程就不得不提多线程中的锁机制,多线程操作过程中往往多个线程是并发执行的,同一个资源可能被多个线程同时访问,造成资源抢夺,这个过程中如果没有锁机制往往会造成重大问题.举例来说,每年春节都是一票难 ...

  3. iOS多线程开发

    概览 大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的,一个复杂的多步操 ...

  4. ios多线程开发的常用三种方式

    1.NSThread 2.NSOperationQueue 3.GCD NSThread: 创建方式主要有两种: [NSThread detachNewThreadSelector:@selector ...

  5. 玩转iOS开发 - 多线程开发

    前言 本文主要介绍iOS多线程开发中使用的主要技术:NSOperation, GCD. NSThread, pthread. 内容依照开发中的优先推荐使用的顺序进行介绍,涉及多线程底层知识比較多的NS ...

  6. [ios2]使用NSOperationQueue简化多线程开发和队列的优先级 【转】

    多线程开发是一件需要特别精心的事情,即使是对有多年开发经验的工程师来说. 为了能让初级开发工程师也能使用多线程,同时还要简化复杂性.各种编程工具提供了各自的办法.对于iOS来说,建议在尽可能的情况下避 ...

  7. ios多线程开发总结

    1>无论使用哪种方法进行多线程开发,每个线程启动后并不一定立即执行相应的操作,具体什么时候由系统调度(CPU空闲时就会执行). 2>更新UI应该在主线程(UI线程)中进行,并且推荐使用同步 ...

  8. 多线程开发之三 GCD

    NSThread.NSOperation.GCD 总结: 无论使用哪种方法进行多线程开发,每个线程启动后并不一定立即执行相应的操作,具体什么时候由系统调度(CPU 空闲时就会执行) 更新 UI 应该在 ...

  9. 多线程开发之二 NSOperation

    效果如下: ViewController.h #import <UIKit/UIKit.h> @interface ViewController : UITableViewControll ...

随机推荐

  1. [HihoCoder1394]网络流四·最小路径覆盖

    题目大意:从有向无环图中选出若干点不想交的链,使得这些链覆盖所有的点,并且链的条数最小. 思路:设超级源点$S$.超级汇点$T$.将$N$个点复制一份,分为$A$部和$B$部.对于$A$部的所有点$A ...

  2. PHP Web项目开发学习,经验谈

    列个表格吧,看着也比较方便: 时间 事项 六月 七月 八月 九月 工作任务 前端页面 服务端页面 地图定位 服务器搭建 图片上传 视频文件信息的获取 关键词 Css+div+js PHP,谷歌maps ...

  3. Mysql 登录及用户切换、用户权限查询

    启动mysql: 方法一:net start mysql(或者是其他服务名) 方法二:在windows下启动MySQL服务 MySql安装目录:"d:\MySql\" 进入命令行输 ...

  4. memcached协议解析

    本文转载自:http://www.ccvita.com/306.html 协议memcached 的客户端使用TCP链接与服务器通讯.(UDP接口也同样有效,参考后文的 “UDP协议” )一个运行中的 ...

  5. linux命令(及解压tar.gz文件)

    https://wenku.baidu.com/view/f5805017866fb84ae45c8df3.html 1.压缩命令: 命令格式:tar  -zcvf   压缩文件名.tar.gz   ...

  6. php integer

    一.整数的表示方法: 整型值可以使用十进制,十六进制,八进制或二进制表示,前面可以加上可选的符号(- 或者 +) 要使用二进制表达,数字前必须加上 0b 要使用八进制表达,数字前必须加上 0. 要使用 ...

  7. VS2008 编译 libpng库

    一.下载libpng和zlib http://sourceforge.net/projects/libpng/ http://sourceforge.net/projects/libpng/files ...

  8. 怎么把html页面中共用的底部代码做成共享模块

    问: 很多时候,我们在设计网站时会发现,站内每一个页面的header跟footer其实都是一样的,如果每个页面都写header跟footer就会显示代码冗余而且维护也不方便, 这时候最好的做法就是把相 ...

  9. oracle索引原理(b-tree,bitmap,聚集,非聚集索引)

    B-TREE索引 一个B树索引只有一个根节点,它实际就是位于树的最顶端的分支节点. 可以用下图一来描述B树索引的结构.其中,B表示分支节点,而L表示叶子节点. 对于分支节点块(包括根节点块)来说,其所 ...

  10. 关于在win8系统下用VMware 9.0装系统导致物理机不断重启的解决办法

    一.问题描述 前段时间将操作系统换成了Win8,安装上VMware 9.0英文版.然后在VMware中安装centos系统,结果每次到安装系统的时候,物理机系统就会莫名其妙地自动重启,毫无征兆地出现, ...