iOS多线程总览 --By 吴帮雷
阅读原文时间:2023年07月11日阅读:1

在iOS中每个进程启动后都会建立一个主线程(UI线程),这个线程是其他线程的父线程。由于在iOS中除了主线程,其他子线程是独立于Cocoa Touch的,所以只有主线程可以更新UI界面(新版iOS中,使用其他线程更新UI可能也能成功,但是不推荐)。iOS中多线程使用并不复杂,关键是如何控制好各个线程的执行顺序、处理好资源竞争问题。常用的多线程开发有三种方式:

1.NSThread

2.NSOperation

3.GCD

三种方式是随着iOS的发展逐渐引入的,所以相比而言后者比前者更加简单易用,并且GCD也是目前苹果官方比较推荐的方式(它充分利用了多核处理器的运算性能)。

一 、NSThread是轻量级的多线程开发,使用起来也并不复杂,但是使用NSThread需要自己管理线程生命周期。

//
// RootViewController.m
// 多线程Demo
//
// Created by 吴帮雷 on 15/5/22.
// Copyright (c) 2015年 吴帮雷. All rights reserved.
//

#import "RootViewController.h"
#import "TwoViewController.h"
@interface RootViewController ()
{
UIImageView *_imageView;
}
@end

@implementation RootViewController

  • (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [self initUI];
    }

  • (void)initUI
    {
    _imageView =[[UIImageView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame];
    _imageView.contentMode=UIViewContentModeScaleAspectFit;
    [self.view addSubview:_imageView];
    }

  • (IBAction)btnClick:(id)sender {
    UIButton *b=(UIButton *)sender;
    if (b.tag == 1) {
    [self loadImageWithMultiThread];
    }
    else
    {
    TwoViewController *two = [TwoViewController new];
    [self presentViewController:two animated:YES completion:^{

    }\];  

    }

}
#pragma mark 将图片显示到界面
-(void)updateImage:(NSData *)imageData{
UIImage *image=[UIImage imageWithData:imageData];
_imageView.image=image;
}

#pragma mark 请求图片数据
-(NSData *)requestData{
//对于多线程操作建议把线程操作放到@autoreleasepool中
@autoreleasepool {
NSURL *url=[NSURL URLWithString:@"http://www.sinaimg.cn/dy/slidenews/4_img/2015_21/704_1633803_604865.jpg"];
NSData *data=[NSData dataWithContentsOfURL:url];
return data;
}
}
#pragma mark 加载图片
-(void)loadImage{
//请求数据
NSData *data= [self requestData];
/*将数据显示到UI控件,注意只能在主线程中更新UI,
另外performSelectorOnMainThread方法是NSObject的分类方法,每个NSObject对象都有此方法,
它调用的selector方法是当前调用控件的方法,例如使用UIImageView调用的时候selector就是UIImageView的方法
Object:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)
waitUntilDone:是否线程任务完成执行
*/
[self performSelectorOnMainThread:@selector(updateImage:) withObject:data waitUntilDone:YES];
}

#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
//方法1:使用对象方法
//创建一个线程,第一个参数是请求的操作,第二个参数是操作方法的参数
// NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
// //启动一个线程,注意启动一个线程并非就一定立即执行,而是处于就绪状态,当系统调度时才真正执行
// [thread start];

//方法2:使用类方法 直接调用  
\[NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil\];  

}

  • (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation

  • (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
    }
    */

@end

接下来将创建多个UIImageView并创建多个线程用于往UIImageView中填充图片。

//
// TwoViewController.m
// 多线程Demo
//
// Created by 吴帮雷 on 15/5/22.
// Copyright (c) 2015年 吴帮雷. All rights reserved.
//

#define ROW_COUNT 5
#define COLUMN_COUNT 3
#define ROW_HEIGHT 100
#define ROW_WIDTH ROW_HEIGHT
#define CELL_SPACING 10

#import "TwoViewController.h"
#import "ImageModel.h"

@interface TwoViewController ()
{
NSMutableArray *_imageViews;
}
@end

@implementation TwoViewController

  • (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor lightGrayColor];
    [self initUI];
    }

  • (void)initUI
    {
    //创建多个图片控件用于显示图片
    _imageViews=[NSMutableArray array];
    for (int r=0; r<ROW_COUNT; r++) {
    for (int c=0; c<COLUMN_COUNT; c++) {
    UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING ), ROW_WIDTH, ROW_HEIGHT)];
    imageView.contentMode=UIViewContentModeScaleAspectFit;
    [self.view addSubview:imageView];
    [_imageViews addObject:imageView];

    }  

    }

    UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    button.frame=CGRectMake(50, 500, 220, 25);
    [button setTitle:@"加载图片" forState:UIControlStateNormal];
    //添加方法
    [button addTarget:self action:@selector(loadImageWithMultiThreadForOrder) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    }
    #pragma mark 将图片显示到界面
    -(void)updateImage:(ImageModel *)imageData{
    UIImage *image=[UIImage imageWithData:imageData.data];
    UIImageView *imageView= _imageViews[imageData.index];
    imageView.image=image;
    }

#pragma mark 请求图片数据
-(NSData *)requestData:(int )index{
//对于多线程操作建议把线程操作放到@autoreleasepool中
@autoreleasepool {
NSURL *url=[NSURL URLWithString:@"http://campus-belles.biyixia.com/sites/campus-belles.biyixia.com/files/styles/medium/public/files/images/product/fashion/tv/campus_belles/886.png?itok=vqvr5yY2"];
NSData *data=[NSData dataWithContentsOfURL:url];
return data;
}
}

#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
//currentThread方法可以取得当前操作线程
int i=[index integerValue];

 NSLog(@"\\n当前加载图片%i",i);//未必按顺序输出

NSData \*data= \[self requestData:i\];

ImageModel \*imageData=\[\[ImageModel alloc\]init\];  
imageData.index=i;  
imageData.data=data;  
\[self performSelectorOnMainThread:@selector(updateImage:) withObject:imageData waitUntilDone:YES\];  

}
#pragma mark 多线程下载图片 优先某一个
-(void)loadImageWithMultiThreadForOrder{
NSMutableArray *threads=[NSMutableArray array];
int count=ROW_COUNT*COLUMN_COUNT;
//创建多个线程用于填充图片
for (int i=0; i<count; ++i) {
NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage:) object:[NSNumber numberWithInt:i]];
thread.name=[NSString stringWithFormat:@"myThread%i",i];//设置线程名称
if(i==(5)){
thread.threadPriority=1.0;
}else{
thread.threadPriority=0.0;
}
[threads addObject:thread];
}

for (int i=0; i<count; i++) {  
    NSThread \*thread=threads\[i\];  
    \[thread start\];  
}  

}
#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
//创建多个线程用于填充图片
for (int i=0; i<ROW_COUNT*COLUMN_COUNT; ++i) {
// [NSThread detachNewThreadSelector:@selector(loadImage:) toTarget:self withObject:[NSNumber numberWithInt:i]];
NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage:) object:[NSNumber numberWithInt:i]];
thread.name=[NSString stringWithFormat:@"myThread%i",i];//设置线程名称
[thread start];
}
}

  • (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation

  • (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
    }
    */

@end

在线程操作过程中可以让某个线程休眠等待,优先执行其他线程操作,而且在这个过程中还可以修改某个线程的状态或者终止某个指定线程。为了解决上面优先加载最后一张图片的问题,不妨让其他线程先休眠一会等待最后一个线程执行。修改图片加载方法如下即可

-(NSData *)requestData:(int )index{
//对于多线程操作建议把线程操作放到@autoreleasepool中
@autoreleasepool {
//对非最后一张图片加载线程休眠2秒
if (index!=(ROW_COUNT*COLUMN_COUNT-1)) {
[NSThread sleepForTimeInterval:2.0];
}
NSURL *url=[NSURL URLWithString:_imageNames[index]];
NSData *data=[NSData dataWithContentsOfURL:url];

    return data;  
}  

}

假设在图片加载过程中点击停止按钮让没有完成的线程停止加载,可以改造程序如下:

#import "KCMainViewController.h"
#import "KCImageData.h"
#define ROW_COUNT 5
#define COLUMN_COUNT 3
#define ROW_HEIGHT 100
#define ROW_WIDTH ROW_HEIGHT
#define CELL_SPACING 10

@interface KCMainViewController (){
NSMutableArray *_imageViews;
NSMutableArray *_imageNames;
NSMutableArray *_threads;
}

@end

@implementation KCMainViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

    [self layoutUI];
    }

#pragma mark 界面布局
-(void)layoutUI{
//创建多个图片空间用于显示图片
_imageViews=[NSMutableArray array];
for (int r=0; r<ROW_COUNT; r++) {
for (int c=0; c<COLUMN_COUNT; c++) {
UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING ), ROW_WIDTH, ROW_HEIGHT)];
imageView.contentMode=UIViewContentModeScaleAspectFit;
// imageView.backgroundColor=[UIColor redColor];
[self.view addSubview:imageView];
[_imageViews addObject:imageView];

    }  
}

//加载按钮  
UIButton \*buttonStart=\[UIButton buttonWithType:UIButtonTypeRoundedRect\];  
buttonStart.frame=CGRectMake(50, 500, 100, 25);  
\[buttonStart setTitle:@"加载图片" forState:UIControlStateNormal\];  
\[buttonStart addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside\];  
\[self.view addSubview:buttonStart\];

//停止按钮  
UIButton \*buttonStop=\[UIButton buttonWithType:UIButtonTypeRoundedRect\];  
buttonStop.frame=CGRectMake(160, 500, 100, 25);  
\[buttonStop setTitle:@"停止加载" forState:UIControlStateNormal\];  
\[buttonStop addTarget:self action:@selector(stopLoadImage) forControlEvents:UIControlEventTouchUpInside\];  
\[self.view addSubview:buttonStop\];

//创建图片链接  
\_imageNames=\[NSMutableArray array\];  
\[\_imageNames addObject:@    for (int i=0; i<IMAGE\_COUNT; i++) {  
    \[\_imageNames addObject:\[NSString stringWithFormat:@"http://images.cnblogs.com/cnblogs\_com/kenshincui/613474/o\_%i.jpg",i\]\];  
}  

}

#pragma mark 将图片显示到界面
-(void)updateImage:(KCImageData *)imageData{
UIImage *image=[UIImage imageWithData:imageData.data];
UIImageView *imageView= _imageViews[imageData.index];
imageView.image=image;
}

#pragma mark 请求图片数据
-(NSData *)requestData:(int )index{
//对于多线程操作建议把线程操作放到@autoreleasepool中
@autoreleasepool {
NSURL *url=[NSURL URLWithString:_imageNames[index]];
NSData *data=[NSData dataWithContentsOfURL:url];

    return data;  
}  

}

#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
int i=[index integerValue];

NSData \*data= \[self requestData:i\];

NSThread \*currentThread=\[NSThread currentThread\];

// 如果当前线程处于取消状态,则退出当前线程
if (currentThread.isCancelled) {
NSLog(@"thread(%@) will be cancelled!",currentThread);
[NSThread exit];//取消当前线程
}

KCImageData \*imageData=\[\[KCImageData alloc\]init\];  
imageData.index=i;  
imageData.data=data;  
\[self performSelectorOnMainThread:@selector(updateImage:) withObject:imageData waitUntilDone:YES\];  

}

#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
int count=ROW_COUNT*COLUMN_COUNT;
_threads=[NSMutableArray arrayWithCapacity:count];

//创建多个线程用于填充图片  
for (int i=0; i<count; ++i) {  
    NSThread \*thread=\[\[NSThread alloc\]initWithTarget:self selector:@selector(loadImage:) object:\[NSNumber numberWithInt:i\]\];  
    thread.name=\[NSString stringWithFormat:@"myThread%i",i\];//设置线程名称  
    \[\_threads addObject:thread\];  
}  
//循环启动线程  
for (int i=0; i<count; ++i) {  
    NSThread \*thread= \_threads\[i\];  
    \[thread start\];  
}  

}

#pragma mark 停止加载图片
-(void)stopLoadImage{
for (int i=0; i<ROW_COUNT*COLUMN_COUNT; i++) {
NSThread *thread= _threads[i];
//判断线程是否完成,如果没有完成则设置为取消状态
//注意设置为取消状态仅仅是改变了线程状态而言,并不能终止线程
if (!thread.isFinished) {
[thread cancel];

    }  
}  

}
@end

-(void)loadImage:(NSNumber *)index{
int i=[index integerValue];

NSData \*data= \[self requestData:i\];

NSThread \*currentThread=\[NSThread currentThread\];

// 如果当前线程处于取消状态,则退出当前线程
if (currentThread.isCancelled) {
NSLog(@"thread(%@) will be cancelled!",currentThread);
[NSThread exit];//取消当前线程
}

KCImageData \*imageData=\[\[KCImageData alloc\]init\];  
imageData.index=i;  
imageData.data=data;  
\[self performSelectorOnMainThread:@selector(updateImage:) withObject:imageData waitUntilDone:YES\];  

}

#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
int count=ROW_COUNT*COLUMN_COUNT;
_threads=[NSMutableArray arrayWithCapacity:count];

//创建多个线程用于填充图片  
for (int i=0; i<count; ++i) {  
    NSThread \*thread=\[\[NSThread alloc\]initWithTarget:self selector:@selector(loadImage:) object:\[NSNumber numberWithInt:i\]\];  
    thread.name=\[NSString stringWithFormat:@"myThread%i",i\];//设置线程名称  
    \[\_threads addObject:thread\];  
}  
//循环启动线程  
for (int i=0; i<count; ++i) {  
    NSThread \*thread= \_threads\[i\];  
    \[thread start\];  
}  

}

#pragma mark 停止加载图片
-(void)stopLoadImage{
for (int i=0; i<ROW_COUNT*COLUMN_COUNT; i++) {
NSThread *thread= _threads[i];
//判断线程是否完成,如果没有完成则设置为取消状态
//注意设置为取消状态仅仅是改变了线程状态而言,并不能终止线程
if (!thread.isFinished) {
[thread cancel];

    }  
}  

}

首先使用NSInvocationOperation进行一张图片的加载演示,整个过程就是:创建一个操作,在这个操作中指定调用方法和参数,然后加入到操作队列。其他代码基本不用修改,直接修加载图片方法如下:

-(void)loadImageWithMultiThread{
/*创建一个调用操作
object:调用方法参数
*/
NSInvocationOperation *invocationOperation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
//创建完NSInvocationOperation对象并不会调用,它由一个start方法启动操作,但是注意如果直接调用start方法,则此操作会在主线程中调用,一般不会这么操作,而是添加到NSOperationQueue中
// [invocationOperation start];

//创建操作队列  
NSOperationQueue \*operationQueue=\[\[NSOperationQueue alloc\]init\];  
//注意添加到操作队后,队列会开启一个线程执行此操作  
\[operationQueue addOperation:invocationOperation\];  

}

在GCD中提供了一种信号机制,也可以解决资源抢占问题(和同步锁的机制并不一样)。GCD中信号量是dispatch_semaphore_t类型,支持信号通知和信号等待。每当发送一个信号通知,则信号量+1;每当发送一个等待信号时信号量-1,;如果信号量为0则信号会处于等待状态,直到信号量大于0开始执行。根据这个原理我们可以初始化一个信号量变量,默认信号量设置为1,每当有线程进入“加锁代码”之后就调用信号等待命令(此时信号量为0)开始等待,此时其他线程无法进入,执行完后发送信号通知(此时信号量为1),其他线程开始进入执行,如此一来就达到了线程同步目的。

#import "KCMainViewController.h"
#import "KCImageData.h"
#define ROW_COUNT 5
#define COLUMN_COUNT 3
#define ROW_HEIGHT 100
#define ROW_WIDTH ROW_HEIGHT
#define CELL_SPACING 10
#define IMAGE_COUNT 9

@interface KCMainViewController (){
NSMutableArray *_imageViews;
NSLock *_lock;
dispatch_semaphore_t _semaphore;//定义一个信号量
}

@end

@implementation KCMainViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

    [self layoutUI];
    }

#pragma mark 界面布局
-(void)layoutUI{
//创建多个图片控件用于显示图片
_imageViews=[NSMutableArray array];
for (int r=0; r<ROW_COUNT; r++) {
for (int c=0; c<COLUMN_COUNT; c++) {
UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING ), ROW_WIDTH, ROW_HEIGHT)];
imageView.contentMode=UIViewContentModeScaleAspectFit;
[self.view addSubview:imageView];
[_imageViews addObject:imageView];

    }  
}

UIButton \*button=\[UIButton buttonWithType:UIButtonTypeRoundedRect\];  
button.frame=CGRectMake(50, 500, 220, 25);  
\[button setTitle:@"加载图片" forState:UIControlStateNormal\];  
//添加方法  
\[button addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside\];  
\[self.view addSubview:button\];

//创建图片链接  
\_imageNames=\[NSMutableArray array\];  
for (int i=0; i<IMAGE\_COUNT; i++) {  
    \[\_imageNames addObject:\[NSString stringWithFormat:@"http://images.cnblogs.com/cnblogs\_com/kenshincui/613474/o\_%i.jpg",i\]\];  
}

/\*初始化信号量  
 参数是信号量初始值  
 \*/  
\_semaphore=dispatch\_semaphore\_create(1);

}

#pragma mark 将图片显示到界面
-(void)updateImageWithData:(NSData *)data andIndex:(int )index{
UIImage *image=[UIImage imageWithData:data];
UIImageView *imageView= _imageViews[index];
imageView.image=image;
}

#pragma mark 请求图片数据
-(NSData *)requestData:(int )index{
NSData *data;
NSString *name;

/\*信号等待  
 第二个参数:等待时间  
 \*/  
dispatch\_semaphore\_wait(\_semaphore, DISPATCH\_TIME\_FOREVER);  
if (\_imageNames.count>0) {  
    name=\[\_imageNames lastObject\];  
    \[\_imageNames removeObject:name\];  
}  
//信号通知  
dispatch\_semaphore\_signal(\_semaphore);

if(name){  
    NSURL \*url=\[NSURL URLWithString:name\];  
    data=\[NSData dataWithContentsOfURL:url\];  
}

return data;  

}

#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
int i=[index integerValue];
//请求数据
NSData *data= [self requestData:i];
//更新UI界面,此处调用了GCD主线程队列的方法
dispatch_queue_t mainQueue= dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
[self updateImageWithData:data andIndex:i];
});
}

#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
int count=ROW_COUNT*COLUMN_COUNT;
// dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//这里创建一个并发队列(使用全局并发队列也可以)  
dispatch\_queue\_t queue=dispatch\_queue\_create("myQueue", DISPATCH\_QUEUE\_CONCURRENT);

for (int i=0; i<count; i++) {  
    dispatch\_async(queue, ^{  
        \[self loadImage:\[NSNumber numberWithInt:i\]\];  
    });  
}  

}

@end

假设当前imageNames没有任何图片,而整个界面能够加载15张图片(每张都不能重复),现在创建15个线程分别从imageNames中取图片加载到界面中。由于imageNames中没有任何图片,那么15个线程都处于等待状态,只有当调用图片创建方法往imageNames中添加图片后(每次创建一个)并且唤醒其他线程(这里只唤醒一个线程)才能继续执行加载图片。如此,每次创建一个图片就会唤醒一个线程去加载,这个过程其实就是一个典型的生产者-消费者模式。下面通过NSCondition实现这个流程的控制:

#import "KCMainViewController.h"
#import "KCImageData.h"
#define ROW_COUNT 5
#define COLUMN_COUNT 3
#define ROW_HEIGHT 100
#define ROW_WIDTH ROW_HEIGHT
#define CELL_SPACING 10
#define IMAGE_COUNT 9

@interface KCMainViewController (){
NSMutableArray *_imageViews;
NSCondition *_condition;
}

@end

@implementation KCMainViewController

#pragma mark - 事件

  • (void)viewDidLoad {
    [super viewDidLoad];

    [self layoutUI];
    }

#pragma mark - 内部私有方法
#pragma mark 界面布局
-(void)layoutUI{
//创建多个图片控件用于显示图片
_imageViews=[NSMutableArray array];
for (int r=0; r<ROW_COUNT; r++) {
for (int c=0; c<COLUMN_COUNT; c++) {
UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING ), ROW_WIDTH, ROW_HEIGHT)];
imageView.contentMode=UIViewContentModeScaleAspectFit;
[self.view addSubview:imageView];
[_imageViews addObject:imageView];

    }  
}

UIButton \*btnLoad=\[UIButton buttonWithType:UIButtonTypeRoundedRect\];  
btnLoad.frame=CGRectMake(50, 500, 100, 25);  
\[btnLoad setTitle:@"加载图片" forState:UIControlStateNormal\];  
\[btnLoad addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside\];  
\[self.view addSubview:btnLoad\];

UIButton \*btnCreate=\[UIButton buttonWithType:UIButtonTypeRoundedRect\];  
btnCreate.frame=CGRectMake(160, 500, 100, 25);  
\[btnCreate setTitle:@"创建图片" forState:UIControlStateNormal\];  
\[btnCreate addTarget:self action:@selector(createImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside\];  
\[self.view addSubview:btnCreate\];

//创建图片链接  
\_imageNames=\[NSMutableArray array\];

//初始化锁对象  
\_condition=\[\[NSCondition alloc\]init\];

\_currentIndex=0;

}

#pragma mark 创建图片
-(void)createImageName{
[_condition lock];
//如果当前已经有图片了则不再创建,线程处于等待状态
if (_imageNames.count>0) {
NSLog(@"createImageName wait, current:%i",_currentIndex);
[_condition wait];
}else{
NSLog(@"createImageName work, current:%i",_currentIndex);
//生产者,每次生产1张图片
[_imageNames addObject:[NSString stringWithFormat:@"http://images.cnblogs.com/cnblogs_com/kenshincui/613474/o_%i.jpg",_currentIndex++]];

    //创建完图片则发出信号唤醒其他等待线程  
    \[\_condition signal\];  
}  
\[\_condition unlock\];  

}

#pragma mark 加载图片并将图片显示到界面
-(void)loadAnUpdateImageWithIndex:(int )index{
//请求数据
NSData *data= [self requestData:index];
//更新UI界面,此处调用了GCD主线程队列的方法
dispatch_queue_t mainQueue= dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
UIImage *image=[UIImage imageWithData:data];
UIImageView *imageView= _imageViews[index];
imageView.image=image;
});
}

#pragma mark 请求图片数据
-(NSData *)requestData:(int )index{
NSData *data;
NSString *name;
name=[_imageNames lastObject];
[_imageNames removeObject:name];
if(name){
NSURL *url=[NSURL URLWithString:name];
data=[NSData dataWithContentsOfURL:url];
}
return data;
}

#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
int i=(int)[index integerValue];
//加锁
[_condition lock];
//如果当前有图片资源则加载,否则等待
if (_imageNames.count>0) {
NSLog(@"loadImage work,index is %i",i);
[self loadAnUpdateImageWithIndex:i];
[_condition broadcast];
}else{
NSLog(@"loadImage wait,index is %i",i);
NSLog(@"%@",[NSThread currentThread]);
//线程等待
[_condition wait];
NSLog(@"loadImage resore,index is %i",i);
//一旦创建完图片立即加载
[self loadAnUpdateImageWithIndex:i];
}
//解锁
[_condition unlock];
}

#pragma mark - UI调用方法
#pragma mark 异步创建一张图片链接
-(void)createImageWithMultiThread{
dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建图片链接
dispatch_async(globalQueue, ^{
[self createImageName];
});
}

#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
int count=ROW_COUNT*COLUMN_COUNT;
dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

for (int i=0; i<count; ++i) {  
    //加载图片  
    dispatch\_async(globalQueue, ^{  
        \[self loadImage:\[NSNumber numberWithInt:i\]\];  
    });  
}  

}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章