您的位置:首页 > 移动开发 > IOS开发

IOS开发 多线程

2016-03-02 18:00 337 查看
一、什么是多线程

NSThread是轻量级的多线程开发,使用并不复杂,但使用NSThread需要自己管理线程的生命周期,这篇文章主要讲NSThread的使用

了解进程与线程

1. 线程是CPU执行任务的基本单位,一个进程能有多个线程,但同时只能执行一个任务

 2. 进程就是运行中的软件,是动态的

 3. 一个操作系统可以对应多个进程,一个进程可以有多条线程,但至少有一个线程

 4. 同一个进程内的线程共享进程里的资源

 

 2. 主线程

 1. 进程一启动就自动创建

 2. 显示和刷新UI界面

 3. 处理UI事件

 

 3. 子线程

 1. 处理耗时的操作

 2. 子线程不能用来刷新UI
文/涅槃广广(简书作者)

原文链接:http://www.jianshu.com/p/b1c2bd572e81

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

NSThread常用方法

使用NSThread开辟线程的两种方式:
创建并手动开启线程

NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(compete) object:nil];
[thread start];


创建并自动开启线程

[NSThread detachNewThreadSelector:@selector(compete) toTarget:self withObject:nil];


判断当前进程是否是多线程

BOOL isMultiThread = [NSThread isMultiThreaded];


获取当前线程对象

//输出当前线程的信息
NSLog(@"当前所在的线程=%@",[NSThread currentThread]);


控制台的信息

NSThread[10433:1182963] viewDidLoad 方法所在的线程=<NSThread: 0x7fca98507b10>{number = 1, name = main}


number = 1 : 线程的编号,由系统设置,主线程的编号为1
name = main:指当前所在的线程的名字叫做main,可以自己设置,主线程的名字默认是main,其他线程如果不给他设置名字默认是nil。

使当前线程睡眠指定的时间,单位为秒

//这句代码在哪个线程执行就让哪个线程睡眠。
[NSThread sleepForTimeInterval:2];


线程一旦休眠就进入阻塞状态,就是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。

设置线程的优先级

//取值范围(0.0~1.0),默认为0.5,取值越大,优先级越大。
thread.threadPriority = 1.0;


判断当前线程是否为主线程

[NSThread isMainThread];


 

给线程设置名字

[thread setName:@"线程名字"];


NSThread对象可知的三种状态

isExecuting:是否正在执行,只读

isFinished:是否已经完成,只读

isCancellled:是否已经取消,可通过[thread cancel]手动设置,线程取消意味着该线程处于准备退出状态,但不会影响线程的运行。

退出当前线程

//线程退出前,必须要要,该线程之后的代码将不在执行
if (thread.isCancelled == YES) {
[NSThread exit];
}


 

实例  加载一张图片      使用多线程加载一张url图片

在self.view上放一个UIImageView试图
开辟一条子线程
子线程
中将url图片转成image对象
回到
主线程


在主线程中将image对象给UIImageView视图

#import "ViewController.h"

#define kUrl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943"

@interface ViewController ()
{
UIImageView *imageView;
}

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

/*
* 1、在self.view上放一个UIImageView试图
*/
imageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 200, 200)];
[self.view addSubview:imageView];

/*
* 2、 开辟一条子线程(我这里采用创建并手动开启线程的方式)

* target: 信息发送者

* selector: 方法选择器选择一个方法

* object: 如果上面选择的方法有参数,则object便是这个方法的参数

*/
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kUrl];

//给线程起名字
thread.name = @"子线程";

// 开启线程
[thread start];

}

/*
* 3、 在`子线程`中将url图片转成image对象

*  downloadImage该方法的参数取决于创建线程时传给object的参数

*/
- (void)downloadImage:(NSString *)url{

//将图片的url地址转化为data对象
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrl]];

//将data对象转化为image对象
UIImage *image = [UIImage imageWithData:data];

/*
* 4. 是NSObject的一个方法,用来回到主线程

* 方法updataUI将在主线程中执行

* withObject:updateUI的参数

* waitUntilDone: 设为YES,会阻塞当前子线程,去主线程执行updateUI方法,也就是更新UI,直到UI更新完毕。设为NO,意味着在主线程updateUI方法执行到一半时可能会被打断去做其他线程的工作,也就是说我主线程的UI还没有显示完就程序就跳出了主线程。
*/
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];

/*

* 查看打印结果

* number = 1 :线程的编号,由系统设置,主线程的编号为1

* name = main:指当前所在的线程的名字叫做main,可以自己设置,主线程的名字默认是main,其他线程如果不给他设置名字默认是nil

*/
NSLog(@"downlaodImage方法所在的线程 = %@",[NSThread currentThread]);

}

/*
* 5、 在主线程中将image对象给UIImageView试图
*/

- (void)updateUI:(UIImage *)image{

imageView.image = image;

NSLog(@"downlaodImage方法所在的线程 = %@",[NSThread currentThread]);

}

@end


使用多线程加载多张图片
在self.view上放多个UIImageView视图
开辟多条子线程
子线程
中将url图片转成image对象
回到
主线程


在主线程中将image对象给UIImageView视图

#import "MoreImageViewViewController.h"

#define kUrl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943"

@interface MoreImageViewViewController ()
{
int imageIndex;

NSMutableArray *threadArrays;

UIImage *image;
}

@end

@implementation MoreImageViewViewController

- (void)viewDidLoad {
[super viewDidLoad];

UILabel *lable = [[UILabel alloc]initWithFrame:CGRectMake(100, 300, 0, 0)];
lable.text = @"点击屏幕停止加载";
lable.textColor = [UIColor blackColor];
[lable sizeToFit];
[self.view addSubview:lable];

//创建多个UIImageView
self.title = @"多线程加载多张图片";
self.edgesForExtendedLayout = UIRectEdgeNone;
self.view.backgroundColor = [UIColor whiteColor];

imageIndex = 100;

for (int  row= 0; row<3; row++) {
for (int list = 0; list<2; list++) {

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(10+list*200, 10+row*200, 200, 200)];

imageView.tag = imageIndex++;

[self.view addSubview:imageView];

}
}

threadArrays = [NSMutableArray array];

//创建多个线程
for (int index = 0; index<6; index++) {
//此时我传的参数是线程创建的顺序
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:@(index)];

//给线程设置优先级(0-1),优先级越高,被优先调用的几率越高。
//        thread.threadPriority = index/10.0;
thread.name = [NSString stringWithFormat:@"线程%d",index];
[thread start];

[threadArrays addObject:thread];

}
}

//每条线程都会走这个方法,来下载相应的图片,在这里为了方便起见,我采用了同一个url图片
- (void)downloadImage:(NSNumber *)index{

[NSThread sleepForTimeInterval:[index integerValue]];

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

NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrl]];

image = [UIImage imageWithData:data];

//用index找到相应线程的数据,赋给相应的图片试图。
[self performSelectorOnMainThread:@selector(updateUI:) withObject:index waitUntilDone:YES];

NSThread *thread = [NSThread currentThread];
NSLog(@"当前线程是 = %@",thread.name);

}

- (void)updateUI:(NSNumber *)ktest{

UIImageView *imageView = [self.view viewWithTag:100+[ktest integerValue]];

imageView.image = image;

}

//点击屏幕将没有完成的线程设置为取消状态
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

for (int i=0; i<6; i++) {
NSThread *thread= threadArrays[i];
//判断线程是否完成,如果没有完成则设置为取消状态
//注意设置为取消状态仅仅是改变了线程状态而言,并不能终止线程
if (!thread.isFinished) {
[thread cancel];

NSLog(@"============");

}
}

}

@end


顺序启动的线程一般不会按照启动顺序执行,这是因为线程都创建好以后,CPU会根据实际情况(网速、启动时间、优先级等)来决定执行线程的先后顺序,在这里我采用线程sleep的方式实现了线程的顺序执行。

文/涅槃广广(简书作者)

原文链接:http://www.jianshu.com/p/b1c2bd572e81

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

补充

为了简化多线程开发过程,苹果官方对NSObject进行分类扩展(本质还是创建NSThread),对于简单的多线程操作可以直接使用这些扩展方法。

//在后台执行一个操作,本质就是重新创建一个线程执行当前方法。
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg:

//在指定的线程上执行一个方法,需要用户创建一个线程对象。
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait:

//在主线程上执行一个方法(前面已经使用过)。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait:


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: