一、線程方式:NSThread挽拔,NSQueueOperation, GCD, pthread
二但校、僵尸線程問題(NSThead操作)
- 在子線程添加定時器,如果在主線程中移除定時器會造成僵尸線程状囱。
1毅戈、在子線程添加定時器到腥,必須要執(zhí)行[[NSRunLoop currentRunLoop] run]
播演。因為runloop中的資源沒被移除讶坯。
2袍镀、在子線程中無法執(zhí)行到NSLog(@"runloop finished")
這段代碼。
3绸吸、觸摸屏幕時设江,將定時器從runloop中移除,但是定時器的時間依舊還在執(zhí)行码俩。
- (void)viewDidLoad {
[super viewDidLoad];
NSString * str = @"12211212";
[NSThread detachNewThreadSelector:@selector(threadEvent:) toTarget:self withObject:str];
}
- (void)threadEvent:(id)thread{
NSLog(@"%@",[NSThread currentThread]);
//self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:@"2121" repeats:YES];
self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:nil repeats:YES];
//使用 NSRunLoopCommonModes 模式稿存,這個模式相當(dāng)于 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode 的結(jié)合
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"runloop finished");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.timer invalidate];
}
- (void)timeEvent:(NSTimer *)sender{
//定時器事件在子線程中執(zhí)行
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"%s",__func__);
}
線程依舊存在瞳秽,如圖一所示练俐。
圖一 僵尸線程圖.png
- 如何解決僵尸線程
在該線程中移除掉定時器。
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
[self.thread start];
}
- (void)threadEvent:(id)thread{
NSLog(@"%@",[NSThread currentThread]);
//self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:@"2121" repeats:YES];
self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:nil repeats:YES];
//使用 NSRunLoopCommonModes 模式,這個模式相當(dāng)于 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode 的結(jié)合
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"runloop finished");
}
- (void)timeInvalidate:(id)sender{
[self.timer invalidate];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
/*
You cannot cancel messages queued using this method.
If you want the option of canceling a message on the current thread,
you must use either the performSelector:withObject:afterDelay: or performSelector:withObject:afterDelay:inModes: method.
要想自線程消滅掉丘喻,必須在其子線程中執(zhí)行performSelector:withObject:afterDelay:或者performSelector:withObject:afterDelay:inModes: 方法
*/
[self performSelector:@selector(timeInvalidate:) onThread:self.thread withObject:@"e" waitUntilDone:YES];
}
- (void)timeEvent:(NSTimer *)sender{
//定時器事件在子線程中執(zhí)行
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"%s",__func__);
}
但是依舊不能解決僵尸線程的問題泉粉,因為在移除定時器的同時榴芳,給runLoop中添加一個timeInvalidate
方法。
- 徹底解決僵尸線程
1讨彼、方法一:
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
[self.thread start];
}
- (void)threadEvent:(id)thread{
NSLog(@"%@",[NSThread currentThread]);
//self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:@"2121" repeats:YES];
self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:nil repeats:YES];
//使用 NSRunLoopCommonModes 模式柿祈,這個模式相當(dāng)于 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode 的結(jié)合
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[self performSelector:@selector(timeInvalidate:) withObject:nil afterDelay:5];
[[NSRunLoop currentRunLoop] run];
NSLog(@"runloop finished");
}
- (void)timeInvalidate:(id)sender{
[self.timer invalidate];
}
- (void)timeEvent:(NSTimer *)sender{
//定時器事件在子線程中執(zhí)行
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"%s",__func__);
}
2、方法二:
- (void)viewDidLoad {
[super viewDidLoad];
_flag = NO;
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
[self.thread start];
}
- (void)threadEvent:(id)thread{
NSLog(@"%@",[NSThread currentThread]);
self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:nil repeats:YES];
//使用 NSRunLoopCommonModes 模式菩貌,這個模式相當(dāng)于 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode 的結(jié)合
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"runloop finished");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.flag = YES;
}
- (void)timeEvent:(NSTimer *)sender{
//定時器事件在子線程中執(zhí)行
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"%s",__func__);
if(self.flag == YES){
[self.timer invalidate];
self.timer = nil;
}
}
- 在runloop中添加port重荠,如果在其它線程中移除掉,會產(chǎn)生僵尸線程仇参。
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
[self.thread start];
}
- (void)threadEvent:(id)thread{
NSLog(@"start");
self.myRunLoop = [NSRunLoop currentRunLoop];
self.port = [[NSPort alloc] init];
[self.myRunLoop addPort:self.port forMode:NSRunLoopCommonModes];
[self.myRunLoop run];
NSLog(@"finished");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.myRunLoop removePort:self.port forMode:NSRunLoopCommonModes];
}
- 解決在runloop中添加port產(chǎn)生僵尸線程
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
[self.thread start];
}
- (void)threadEvent:(id)thread{
NSLog(@"start");
self.myRunLoop = [NSRunLoop currentRunLoop];
self.port = [[NSPort alloc] init];
[self.myRunLoop addPort:self.port forMode:NSRunLoopCommonModes];
[self performSelector:@selector(portCancel) withObject:nil afterDelay:5];
[self.myRunLoop run];
NSLog(@"finished");
}
- (void)portCancel{
[self.myRunLoop removePort:self.port forMode:NSRunLoopCommonModes];
}
結(jié)論:
在子線程中冈敛,給runloop添加資源鸣皂,必須在該線程中進(jìn)行釋放,否則會產(chǎn)生僵尸線程寞缝。
三癌压、線程隊列NSQueueOperation
3.1 Operation對象的并發(fā)和非并發(fā)
3.2 系統(tǒng)的Operation對象
3.2.1 NSBlockOperation
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 2;
NSBlockOperation * blockOperation = [[NSBlockOperation alloc] init];
[blockOperation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[queue addOperation:blockOperation];
}
打印結(jié)果:
<NSThread: 0x608000270b40>{number = 5, name = (null)}
<NSThread: 0x608000270940>{number = 4, name = (null)}
<NSThread: 0x608000270b80>{number = 6, name = (null)}
<NSThread: 0x608000270900>{number = 3, name = (null)}
<NSThread: 0x608000270b40>{number = 5, name = (null)}
<NSThread: 0x608000270940>{number = 4, name = (null)}
<NSThread: 0x608000270b80>{number = 6, name = (null)}
3.3.2 NSInvocationOperation(NSInvocation)
- 使用initWithTarget:selector:object:開啟線程。缺點:只能傳遞一個參數(shù)荆陆。
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationEvent) object:nil];
[queue addOperation:invocationOperation];
}
- (void)invocationOperationEvent{
NSLog(@"%@",[NSThread currentThread]);
}
- 使用NSInvocation傳遞多個參數(shù)
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSMethodSignature * signature = [self methodSignatureForSelector:@selector(name:andAge:andSex:)];
NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = @selector(name:andAge:andSex:);
invocation.target = self;
NSString * name = @"Jerry";
NSString * age = @"2";
NSString * sex = @"男";
//這里的index要從2開始滩届,前兩個已經(jīng)被target和selector占用
[invocation setArgument:&name atIndex:2];
[invocation setArgument:&age atIndex:3];
[invocation setArgument:&sex atIndex:4];
NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc] initWithInvocation:invocation];
[queue addOperation:invocationOperation];
}
- (void)name:(NSString *)name andAge:(NSString *)age andSex:(NSString *)sex{
NSLog(@"name = %@,age = %@,sex = %@",name,age,sex);
}
3.3 自定義Operation對象
3.3.1 Operation狀態(tài)(KVO通知)
- 當(dāng)isFinished狀態(tài)為NO時,NSOperation不能執(zhí)行dealloc被啼。
@interface CustomOperation : NSOperation
@end
@implementation CustomOperation
@synthesize finished = _finished;
- (void)dealloc{
NSLog(@"%s",__func__);
}
- (BOOL)isFinished{
return _finished;
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue * queue = [NSOperationQueue new];
CustomOperation * customOperation = [CustomOperation new];
[queue addOperation:customOperation];
}
CustomOperation
不能執(zhí)行dealloc
,用KVO的形式帜消,改變isFinished的值為YES,才能執(zhí)行CustomOperation才能被釋放。
[self willChangeValueForKey:@"isFinished"];//_finished,finished
_finished = YES;
[self didChangeValueForKey:@"isFinished"];
3.3.2 配置Operation執(zhí)行行為(依賴關(guān)系泡挺,優(yōu)先級等)
NSOperationQueue * operationQueue = [[NSOperationQueue alloc] init];
NSBlockOperation * blockOperation = [[NSBlockOperation alloc] init];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation = %@",[NSThread currentThread]);
}];
NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationEvent) object:nil];
NSBlockOperation * blockOperation2 = [[NSBlockOperation alloc] init];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation2 = %@",[NSThread currentThread]);
}];
//在入隊前處理好依賴關(guān)系
[blockOperation addDependency:invocationOperation];
[blockOperation2 addDependency:blockOperation];
[operationQueue addOperation:blockOperation];
[operationQueue addOperation:invocationOperation];
[operationQueue addOperation:blockOperation2];
結(jié)果:
invocationOperation = <NSThread: 0x6080000740c0>{number = 3, name = (null)}
NSBlockOperation = <NSThread: 0x600000079580>{number = 4, name = (null)}
NSBlockOperation2 = <NSThread: 0x6080000740c0>{number = 3, name = (null)}
??:A線程依賴B線程,B依賴C命浴,C依賴A娄猫,這樣會產(chǎn)生死鎖。