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.异步函数 + 并发队列 ...
随机推荐
- redis-ha手动切换slave节点为master
仅做个人记录,请慎重参考!! 问题描述:使用redis-ha启动了3个pod,现在还有一个pod正常运行,并且为slave(理论上第一个起来的pod应该为master) 通过info命令查看下图 尝试 ...
- xorm 条件查询时区的问题
问题描述:如果在查询的时候,直接传时间格式作为条件,时间会被驱动程序转为UTC格式,因此会有8个小时的误差. 解决方案1: 将查询时间转为字符串 db.where("time > ?& ...
- Docker下安装Nacos
1:使用docker获取nacos服务镜像 docker pull nacos/nacos-server(不加版本号表示获取最新版本) 2:查看是否成功下载nacos镜像 docker images ...
- python 插入mysql数据库字符串中含有单引号或双引号报错
出现问题场景:使用mysql数据库管理接口测试用例,新增接口用例时,传入的paras内容,有多层嵌套的时候,就会有["]双引号括住[']单引号的情况,可能在插入单双引号的数据到数据库的时候, ...
- 初识python 之 MongoDB 基本操作
MongoDB与SQL对比: MongoDB 三元素:数据库.集合.文档 MongoDB 基本操作命令: db 查看当前数据库 show dbs 查看所有数据库 use 数据库名 切换数据库,如果数据 ...
- centos7 单用户模式修改root密码
1. 在虚拟机重启客户机后.会出现下面进入界面.按e键 2.按了e键后,会出现下面的界面.此时按↓键.找到linux16 3.将光标移动到UTF-8后面,添加init=/bin/sh,并按 ctrl ...
- jmeter和JDK安装教程(Windows)
1.JDK的安装及环境变量配置 1.JDK的下载安装 JDK官网下载地址:https://www.oracle.com/java/technologies/downloads 然后注册账号,开始下载, ...
- [开发笔记usbTOcan]用树莓派搭建私有Git服务器
0 | 思路 在开始编程前,先创建一个版本管理库,以前一直用SVN,但目前用Git的还是比较,正好利用这个机会学习GIt. 想过使用Github提供的免费服务器,但项目目前还没有做开源的准备,于是就有 ...
- 【刷题-LeetCode】152 Maximum Product Subarray
Maximum Product Subarray Given an integer array nums, find the contiguous subarray within an array ( ...
- C++ 基本类型的大小
C++的基本类型: char bool (unsigned) short (int) (unsigned) int (unsigned) long (int) (unsigned) long long ...