1.多线程的基本概念和用法
①进程:每一个进程都一是一个应用程序,都有独立的内存空间。(一个程序也可以由多个进程)同一个进程中的线程共享内存中的存储空间和资源
②线程:
每一个程序都有一个主线程,调用main来启动。
主线程的生命周期和应用程序绑定,程序退出时主线程停止。
任何可以阻塞主线程的任务不要再主线程中执行(比如访问网络)
2.多线程使用的注意
①线程使用不是无节制的:
iOS中的主线程的堆栈大小是1M(不可变)
从第二个线程开始都是512KB(不可变)
②只有主线程可以修改UI,渲染引擎工作在主线程中
3.IOS的三种多线程技术(本文主题)
①NSThread:每个NSThread对象对应一个线程
优:量级较轻
缺:需要自己管理线程的生命周期、线程同步、加锁、睡眠和唤醒等,
②NSOperation/NSOperationQueue:面向对象的线程技术
优:不需要关心线程管理、数据同步的事情,且是面向对象的
③GCD(Grand Central Dispatch):是基于C语言的框架,可以充分利用多核,是苹果推荐使用的多线程技术
下面对三种技术,用代码逐一讲解:
①NSThread
- (void)viewDidLoad {
[super viewDidLoad];
for (int i=0; i<10; i++) {
//1.线程睡眠
[NSThread sleepForTimeInterval:1];
NSLog(@"主线程1: %d", i);
}
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(thread1:) object:@"multiThread1"];
//2.开始线程
[thread start];
for (int i=0; i<10; i++) {
//3.获取当前线程
NSThread *current = [NSThread currentThread];
//4.判断当前线程是否是主线程
BOOL isMain = [current isMainThread];
if (isMain) {
NSLog(@"主线程2: %d", i);
}
}
}
-(void) thread1:(NSString *) threadName{
for (int i=0; i<10; i++) {
NSLog(@"%@: %d", threadName, i);
if (i == 5) {
//5.退出线程
[NSThread exit];
}
}
}
②NSOperationQueue
- (void)viewDidLoad {
[super viewDidLoad];
//1.创建线程队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.创建线程任务
/*NSOperation介绍:
是Cocoa中的一个抽象类,用来封装单个任务和代码执行一项操作,由于是抽象类,所以不能直接实例化使用,必须定义子类继承该抽象类来实现(常用的为NSInvocationOperation类,但是也可以自己继承重写NSOperation类)
*/
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread1:) object:@"thread1"];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread2:) object:@"thread2"];
//3.设置队列属性
//1)设置并发数量
queue.maxConcurrentOperationCount = 1;
//2)暂停线程
queue.suspended = YES;
//3)设置线程优先级,需要先暂停线程才会生效
op1.queuePriority = NSOperationQueuePriorityHigh;
op2.queuePriority = NSOperationQueuePriorityVeryLow;
//4.任务完成时调用block
op1.completionBlock = ^{
NSLog(@"op1已经完成");
};
//5.创建任务的另一种方法:block方式
[queue addOperationWithBlock:^{
//也是一个线程入口,和op1、op2一样是一个线程
for (int k=0; k<10; k++) {
NSLog(@"blockThread: %d", k);
}
}];
[queue addOperation:op2];
[queue addOperation:op1];
//6.开始线程
queue.suspended = NO;
for (int k=0; k<10; k++) {
NSLog(@"main: %i", k);
}
}
-(void) thread1:(NSString *) threadName {
for (int i=0; i<10; i++) {
NSLog(@"%@: %d", threadName, i);
}
}
-(void) thread2:(NSString *) threadName {
for (int j=0; j<10; j++) {
NSLog(@"%@: %d", threadName, j);
}
}
③GCD
传送门:http://blog.csdn.net/u012526801/article/details/49004871
补充1:线程中的内存管理问题,NSRunloop和如何跳出线程
NSRunloop:
①线程的生命周期存在5个状态:新建、就绪、运行、阻塞、死亡
②NSRunLoop可以保持一个线程一直为活动状态,不会马上销毁掉
③应用举例:多线程中使用定时器时,必须使用Runloop。因为只有开启Runloop保持线程为活动状态,定时器才不会失效跳出线程方法:
调用cancel方法,然后再线程方法中根据线程的isCancelled属性可以break出线程方法
①跳出单个线程:[thread1 cancel];
②队列中,跳出多个线程:[queue cancelAllOperations];内存问题:
多线程创建的实例对象不会放入自动释放池,可能会导致内存泄露。需要手动放入自动重生池
举例:创建一个线程队列queue和一个线程类MyOperation,在queue中添加2个继承于MyOperation类的任务,实现以上补充内容中提到的知识点
以下是viewController中的代码:
- (void)viewDidLoad {
[super viewDidLoad];
_queue = [[NSOperationQueue alloc] init];
_op1 = [[MyOperation alloc] initwithName:@"thread1"];
_op2 = [[MyOperation alloc] initwithName:@"thread2"];
[_queue addOperation:_op1];
[_queue addOperation:_op2];
_op1.completionBlock = ^ {
NSLog(@"op1已经完成------------------");
};
_op2.completionBlock = ^ {
NSLog(@"op2已经完成------------------");
};
// [self performSelector:@selector(afterDelay) withObject:nil afterDelay:0.1];
[self performSelector:@selector(cancelAllThread) withObject:nil afterDelay:0.5];
}
-(void) afterDelay {
[_op1 cancel];
}
-(void) cancelAllThread {
[_queue cancelAllOperations];
}
以下是MyOperation类的具体实现:
#import <Foundation/Foundation.h>
@interface MyOperation : NSOperation
@property (nonatomic, copy) NSString *mask;
-(instancetype) initwithName:(NSString *)mask;
@end
//##############################################################
#import "MyOperation.h"
@implementation MyOperation
-(instancetype) initwithName:(NSString *)mask {
if (self == [super init]) {
_mask = mask;
}
return self;
}
-(void) main {
//1.多线程创建的实例对象不会放入自动释放池,可能会导致内存泄露。需要手动放入自动重生池
@autoreleasepool {
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeAction) userInfo:nil repeats:YES];
//2.开启Runloop来保持线程的存活状态
[[NSRunLoop currentRunLoop] run];
for (int i=0; i<1000; i++) {
//3.cancel方法只是改变isCancelled属性,在线程实现方法中做判断,用于跳出线程方法。
if (self.isCancelled) {
break;
}
NSLog(@"%@: %d", self.mask, i);
}
}
}
-(void) timeAction {
}
@end