iOS多线程总览 --By 吴帮雷
在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
首先使用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中提供了一种信号机制,也可以解决资源抢占问题(和同步锁的机制并不一样)。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]];
});
}
}
iOS多线程总览 --By 吴帮雷的更多相关文章
- iOS - TableViewCell分割线 --By吴帮雷
千万别小看UI中得线,否则你的设计师和测试组会无休止地来找你的!!(如果是美女还好,如果是恐龙....) 在开发中运用最多的是什么,对,表格--TableView,之所以称作表格,是因为他天生带有分割 ...
- iOS,开发准备之申请证书 ---by吴帮雷
一.申请真机调试证书 打开iOS Dev Center,选择Sign in,登陆(至少99美元账号),登陆选择Certificates,Identifiers & Profiles --> ...
- NSSet和NSMutableSet - By吴帮雷
1.NSSet的使用 [NSSet setWithSet:(NSSet *)set]; 用另外一个set对象构造 [NSSet setWithArray:(NSArray *)array];用数组构造 ...
- iOS多线程杂论
iOS多线程的分布 (1) NSThread (2) NSOperation (3) GCD 现在对下面三个进行一个个的分析,希望那里说得不对的地方希望简友们帮我指点一二. 1,NSThread 优点 ...
- iOS多线程知识总结--GCD
iOS多线程知识总结--GCD 1. iOS中苹果提供4钟方案来帮助我们实现多线程: (1) 纯C语言的pthread,偏底层,需要程序员手动管理线程的生命周期,基本不用. (2) OC语言的NSTr ...
- iOS 多线程:『RunLoop』详尽总结
1. RunLoop 简介 1.1 什么是 RunLoop? 可以理解为字面意思:Run 表示运行,Loop 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停 ...
- iOS多线程主题
下面是:2个并发进程.和2个并发线程的示意图: 下面介绍三种多线程技术(Thread.Cocoa Operation.Grand Central Dispatch): 1.最轻量级Thread(需要自 ...
- iOS多线程技术方案
iOS多线程技术方案 目录 一.多线程简介 1.多线程的由来 2.耗时操作的模拟试验 3.进程和线程 4.多线程的概念及原理 5.多线程的优缺点和一个Tip 6.主线程 7.技术方案 二.Pthrea ...
- iOS 多线程GCD的基本使用
<iOS多线程简介>中提到:GCD中有2个核心概念:1.任务(执行什么操作)2.队列(用来存放任务) 那么多线程GCD的基本使用有哪些呢? 可以分以下多种情况: 1.异步函数 + 并发队列 ...
随机推荐
- Lombok的利弊
1.介绍 Lombok是一个可以大量减少代码的工具, 通过Pluggable Annotation Processing API的方式解析注解, 在编译期为class文件注入getter,setter ...
- 访问局域网内其他主机的VMware虚拟机上的mysql数据库和redis缓存
上一篇写了访问局域网内其他主机的虚拟机上的项目 ,现在说说访问局域网内其他主机的虚拟机上的数据库和缓存 博主使用的linux是Ubuntu16.04: 一.安装数据库和缓存 这里连接的数据库和缓存以m ...
- TestNG 运行Webdriver测试用例
1.单击选中的新建工程的名称,按Ctrl+N组合键,弹出对话框选择"TestNG"下的"TestNG class"选项,点击"next" 2 ...
- CTF-sql-宽字节注入
本文章主要涉及sql宽字节注入注入的原理讲解,如有错误,望指出.(附有目录,如需查看请点右下角) 一.首先介绍一下本篇文章所用到的知识点: 常用到的url编码: 空格:%20 单引号:%27 在sql ...
- Solon 开发
Solon 开发 一.注入或手动获取配置 二.注入或手动获取Bean 三.构建一个Bean的三种方式 四.Bean 扫描的三种方式 五.切面与环绕拦截 六.提取Bean的函数进行定制开发 七.自定义注 ...
- RHCSA 第五天
1. a.创建普通变量local_data=1并访问 [root@sss ~]#local_data=1 [root@sss ~]#echo local_data local_data [root@s ...
- 《剑指offer》面试题09. 用两个栈实现队列
问题描述 用两个栈实现一个队列.队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能.(若队列中没有元素,dele ...
- 《剑指offer》面试题48. 最长不含重复字符的子字符串
问题描述 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串 ...
- IPX下载安装
IPX下载安装 该软件需要事先安装LAPACK与openblas,相关安装教程请点击链接. 1.下载 mkdir IPX cd IPX git clone https://github.com/ERG ...
- T-SQL的存储过程
1.简介 存储过程可以说是一个记录集,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块取一个名字,在用到这个功能的时 ...