1.进程与线程的区别与联系
1.1.进程
广义上来讲,进程是系统中正在运行的一个应用程序,每个进程间都是独立的,每个进程都是运行在独有的空间内,空间是受保护的,这就意味着一个进程是不能访问另一个进程的独有空间的。平时接触到的应用程序都可以认为是一个进程,比如Xcode,SourceTree等。可以通过活动监视器查看Mac上运行的进程。
进程的五态模型:
- 进程的五态模型
1.2.线程
- 线程是进程的基本执行单元
- 进程的所有任务都在线程中执行,就是说一个进程想执行任务必须有一个线程,通常是主线程,也就是iOS中的UI线程
1.3.进程与线程的关系
2.任务执行的方式
2.1.串行
- 串行
2.2.并行
- 并行
3.多线程的实现方案
3.1.pThread
基于c的,没用过,不解释,有兴趣的可以研究下#import <pthread.h>
3.2.NSThread
3.2.1.类方法
无返回值,自动异步执行。但无法设置属性
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
3.2.2.实例方法
需要手动调用start
异步执行,因为有返回值,所以可以设置属性
- (instancetype)init NS_AVAILABLE(10_5, 2_0) NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
3.2.3.NSObject的category方法
以下方法的aSelector
均不能有返回值,如果arg
不为空则设置一个id
类型参数,否则不可设置参数。arg
为执行aSelector
的入参。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
wait
参数:如果在子线程执行以上两个方法,YES代表阻塞当前子线程直到主线程的aSelector
执行完毕,NO代表不阻塞当前子线程;如果在主线程执行以上两个方法,YES代表阻塞当前主线程的方法执行直到主线程的aSelector
执行完毕,NO代表不阻塞当前主线程,也就是当前主线程的方法执行完毕再执行aSelector
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
// equivalent to the first method with kCFRunLoopCommonModes
thr
:多了个线程参数。
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
创建一个子线程执行
3.3.GCD
3.3.1串行与并行
使用串行队列执行任务只会创建一个线程。
dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.queue.serial", DISPATCH_QUEUE_SERIAL);
dispatch_get_main_queue()
使用并行队列会创建多个线程
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.gcd.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_get_global_queue(0, 0)
Xcode的log会显示进程与线程ID
3.3.2.dispatch_group
当多个任务dispatch_group_async
执行完毕后会在dispatch_group_notify
回调。但是注意dispatch_group_async
中执行的应该是同步的代码,如果是异步的代码比如本身就是异步的网络请求不会达到请求完毕的回调效果。
如果要达到上述效果,需要如下操作
- (void)GCDAction{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self request1:^{
NSLog(@"request1 done");
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self request2:^{
NSLog(@"request2 done");
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"回调完成");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主线程刷新UI");
});
});
}
- (void)request1:(void(^)())block{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task 1");
sleep(2);
NSLog(@"end task 1");
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block();
}
});
});
}
- (void)request2:(void(^)())block{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task 2");
sleep(2);
NSLog(@"end task 2");
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block();
}
});
});
}
3.4.NSOperation
NSOperation
是对GCD的面向对象的封装。
3.4.1.NSInvocationOperation
NSInvocationOperation
会阻塞当前线程同步执行。
3.4.2.NSBlockOperation
同NSInvocationOperation
一样会阻塞当前线程同步执行。
3.4.3.NSOperationQueue
如果想要异步执行,需要使用NSOperationQueue
的以下方法来实现。
- (void)addOperation:(NSOperation *)op;
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);
3.4.4.自定义NSOperation
自定义的NSOperation
需要重写- (void)main
方法,方法里面是具体的业务实现。对于设置异步的依赖不起作用的问题,可以这样实现。
#import "CustomOperation.h"
@interface CustomOperation ()
@property (nonatomic, assign) BOOL over;
@end
@implementation CustomOperation
- (void)main{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(1);
if (self.isCancelled) {
return ;
}
NSLog(@"%@", self.name);
self.over = YES;
});
while (!self.over && !self.isCancelled) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
@end
- (void)NSOperationAction{
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
CustomOperation *op1 = [[CustomOperation alloc]init];
op1.name = @"op1";
CustomOperation *op2 = [[CustomOperation alloc]init];
op2.name = @"op2";
CustomOperation *op3 = [[CustomOperation alloc]init];
op3.name = @"op3";
CustomOperation *op4 = [[CustomOperation alloc]init];
op4.name = @"op4";
[op1 addDependency:op2];
[op2 addDependency:op3];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
}
Xcode打印日志
上一篇:iOS图片滤镜