在iOS中,NSThread是进行多线开发的一个类,其结构逻辑清晰,使用十分方便,但其封装度和性能不高,线程的生命周期、加锁等需要开发者手动处理。
1.NSThread 中常用的类方法
//Block形式的类方法
+ (void)detachNewThreadWithBlock:(void (^)(void))block;
//选择器形式的类方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//[NSThread currentThread]获取当前代码所执行的线程
@property (class, readonly, strong) NSThread *currentThread;
//获取主线程
@property (class, readonly, strong) NSThread *mainThread
//获取程序是否在多线程执行
+ (BOOL)isMultiThreaded;
//当前代码所执行的线程休眠,直到某个时间起被激活
+ (void)sleepUntilDate:(NSDate *)date;
//使当前线程休眠
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//当某个线程使用完之后,可以使用下面方法销毁
+ (void)exit;
简单的示例:
- (void)viewDidLoad {
[super viewDidLoad];
[NSThread detachNewThreadSelector:@selector(log) toTarget:self withObject:nil];
for (int i = 0; i<10; i++) {
NSLog(@"%@===%d",[NSThread currentThread],i);
}
}
- (void)log{
for (int i = 0; i<10; i++) {
NSLog(@"%@===%d",[NSThread currentThread],i);
}
}
控制台打印信息如下:
2020-10-14 23:59:43.970706+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===0
2020-10-14 23:59:43.970788+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===0
2020-10-14 23:59:43.970825+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===1
2020-10-14 23:59:43.970891+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===1
2020-10-14 23:59:43.970929+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===2
2020-10-14 23:59:43.971008+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===3
2020-10-14 23:59:43.971010+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===2
2020-10-14 23:59:43.971092+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===4
2020-10-14 23:59:43.971133+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===3
2020-10-14 23:59:43.971162+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===5
2020-10-14 23:59:43.971333+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===6
2020-10-14 23:59:43.971543+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===7
2020-10-14 23:59:43.971712+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===8
2020-10-14 23:59:43.971913+0800 FMDBActions[24053:7578495] <NSThread: 0x6000034ccf80>{number = 1, name = main}===9
2020-10-14 23:59:43.972281+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===4
2020-10-14 23:59:43.972474+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===5
2020-10-14 23:59:43.972690+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===6
2020-10-14 23:59:43.972902+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===7
2020-10-14 23:59:43.973118+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===8
2020-10-14 23:59:43.978866+0800 FMDBActions[24053:7578745] <NSThread: 0x6000034a8200>{number = 6, name = (null)}===9
从打印的信息可知,程序执行时,实际是两个线程对象在同步的执行(内存地址分别是0x6000034ccf80和0x6000034a8200)。从线程名称也可以看出,其中一个在主线程,另一个是我们新创建的匿名函数。
2.NSThread 实例方法和属性应用
2.1 实例方法
//Block形式的实例方法
- (instancetype)initWithBlock:(void (^)(void))block;
//选择器形式的实例方法
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument ;
// 取消
- (void)cancel
// 开启
- (void)start
2.1 属性
//线程是否正在执行
@property (readonly, getter=isExecuting) BOOL executing
//线程是否已经执行完毕
@property (readonly, getter=isFinished) BOOL finished
//线程是否已经取消执行
@property (readonly, getter=isCancelled) BOOL cancelled
3.隐式地使用NSThread进行多线程编程
直接调用NSThread的类方法和实例对象方法称为显式的NSThread编程,其实NSObject类中的一些方法的调用也会操作NSThread线程,我们可以称之为隐式NSThread编程。如下:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
//指定在主线程执行且默认是kCFRunLoopCommonModes
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array
/*
thr:那个线程执行
arg:所执行选择器的参数
wait:是否立即执行
其默认是kCFRunLoopCommonModes
*/
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait
//指定在后台线程执行选择器
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg
//延时执行线程
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
如果是带 afterDelay 的延时函数,会在内部创建一个 NSTimer,然后添加到当前线程的 Runloop 中。也就是如果当前线程没有开启 runloop,该方法会失效。在子线程中,需要启动 runloop(注 意调用顺序)