iOS基础 - NSThread | NSObject
▶ 前言
进程:一个正在运行的程序可以看做是一个进程,它拥有独立运行所需要的全部资源;一个进程行由一个或多个线程组成;进程只负责资源的调度和分配,线程才是程序执行的真正单元(负责代码的执行)
线程:程序中独立运行的代码段
主线程:一个正在运行的程序(即进程),至少包含一个线程,就是主线程,它在程序启动时被创建
子线程和主线程都是独立运行的单元,各自执行互不影响,能够并发执行
单线程程序:只有一个主线程的程序
多线程程序:拥有多个线程的程序
并发是指一个处理器同时处理多个任务,并非真正意义上的同时,其实质是任务间的超速轮切!并发是逻辑上的同时发生,而并行是物理上的同时发生,打个比喻,并发是指同一个人同时吃三个馒头(并不是真正意义上的同时);
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务!并行是指三个人同时吃三个馒头(真正意义上的同时)。注:当有多个线程在操作时如果系统只有一个 CPU,则它根本不可能实现真正的多线程
▶ NSObject | NSThread
A. NSObject 存在一个最简单的后台执行方法
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg
B. NSThread 是一个轻量级多线程,一个 NSThread对象 就代表一条线程
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument; // 开辟一个线程(需手动开启) - (instancetype)initWithBlock:(void (^)(void))block; + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;// 开辟一个线程(自动开启) + (void)detachNewThreadWithBlock:(void (^)(void))block); - (void)cancel; // 取消线程 - (void)start; // 开启线程
// 是否主线程 - (BOOL)isMainThread; + (BOOL)isMainThread; // 调度优先级:取值范围是 0.0 ~ 1.0,默认 0.5(值越大则优先级越高) + (double)threadPriority; + (BOOL)setThreadPriority:(double)p; // 线程名 - (void)setName:(NSString *)n; - (NSString *)name;
开辟线程
1 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 2 3 // 当前线程 4 NSThread *CTH = [NSThread currentThread]; 5 NSLog(@"%@",CTH); // <_NSMainThread: 0x600002a240c0>{number = 1, name = main} 6 // 主线程 7 NSThread *MTH = [NSThread mainThread]; 8 NSLog(@"%@",MTH); // <_NSMainThread: 0x600002a240c0>{number = 1, name = main} 9 10 //------------------------------------------ 11 12 // 方式一 13 // 开辟一个线程 threadA 14 NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"threadA"]; 15 threadA.name = @"线程A"; 16 [threadA start]; // 手动开启 17 // 以同样的方式开辟一个线程 threadB 18 NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"threadB"]; 19 threadB.name = @"线程B"; 20 [threadB start]; 21 22 23 // 方式二 24 // 开辟一个线程 threadC:自动开启 25 [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"threadC:自动开启"]; 26 27 28 // 方式三:NSObject 29 // 开辟一个线程 threadD:自动开启 30 [self performSelectorInBackground:@selector(run:) withObject:@"threadD:隐式创建且自动开启"]; 31 32 } 33 34 -(void)run:(NSString *)str{ 35 36 NSThread *current = [NSThread currentThread]; 37 for (int i = 0; i < 5; i++) { 38 // 验证多线程是以轮切方式工作的 39 NSLog(@"run%d---%@:%@",i,current,str); 40 } 41 }
日志信息
线程阻塞:下面代码中,在主线程中执行动画。当有一个耗时操作同样要在主线程执行时,就会阻塞当前动画运行!而使用多线程就可以保证动画、耗时操作两者 "同时" 运行
1 #import "ViewController.h" 2 #define MainScreen_W [UIScreen mainScreen].bounds.size.width 3 @interface ViewController () 4 5 @property (strong,nonatomic) UIImageView *gossipIV;// 旋转动画 6 7 @end 8 9 @implementation ViewController 10 11 - (void)viewDidLoad { 12 [super viewDidLoad]; 13 14 // 八卦图 15 self.gossipIV = [[UIImageView alloc] initWithFrame:CGRectMake((MainScreen_W-220)/2.0, 80, 220, 220)]; 16 _gossipIV.image = [UIImage imageNamed:@"gossip.jpg"]; 17 [self.view addSubview:_gossipIV]; 18 19 // 耗时操作:主线程 20 UIButton *mainBT = [UIButton buttonWithType:UIButtonTypeCustom]; 21 [mainBT setTitle:@"耗时操作:主线程" forState:UIControlStateNormal]; 22 [mainBT setTitle:@"" forState:UIControlStateHighlighted]; 23 mainBT.backgroundColor = [UIColor redColor]; 24 mainBT.frame = CGRectMake(40, 400, MainScreen_W-80, 40); 25 [mainBT addTarget:self action:@selector(chokeIV) forControlEvents:UIControlEventTouchUpInside]; 26 [self.view addSubview:mainBT]; 27 28 // 耗时操作:子线程 29 UIButton *threadBT = [UIButton buttonWithType:UIButtonTypeCustom]; 30 [threadBT setTitle:@"耗时操作:子线程" forState:UIControlStateNormal]; 31 [threadBT setTitle:@"" forState:UIControlStateHighlighted]; 32 threadBT.backgroundColor = [UIColor redColor]; 33 threadBT.frame = CGRectMake(75, 500, MainScreen_W-150, 40); 34 [threadBT addTarget:self action:@selector(noChokeIV) forControlEvents:UIControlEventTouchUpInside]; 35 [self.view addSubview:threadBT]; 36 37 // 使用定时器实现动画旋转:运行在主线程 38 [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(ratateImageView) userInfo:nil repeats:YES]; 39 } 40 41 // 旋转动画 42 - (void)ratateImageView{ 43 self.gossipIV.transform = CGAffineTransformRotate(self.gossipIV.transform, M_PI_4/4); 44 } 45 46 // 在遍历完成之前,动画处受阻状态(会静止不动) 47 - (void)chokeIV{ 48 49 for (int i = 0; i < 5; i++) { 50 // 线程休眠 51 [NSThread sleepForTimeInterval:0.5]; 52 NSString *str = [NSString stringWithFormat:@"i = %d",i]; 53 NSLog(@"%@", str); 54 } 55 } 56 57 // 动画、遍历同时进行 58 - (void)noChokeIV{ 59 [NSThread detachNewThreadSelector:@selector(timeConsuming) toTarget:self withObject:nil]; 60 } 61 62 - (void)timeConsuming{ 63 64 // ARC 环境 65 for (int i = 0; i < 10; i++) { 66 [NSThread sleepForTimeInterval:0.5]; 67 NSString *str = [NSString stringWithFormat:@"i = %d",i]; 68 NSLog(@"%@", str); 69 } 70 71 // 回到主线程刷新 UI 72 [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:YES]; 73 74 //--------------------- NSTimer 在子线程中使用 ------------------- 75 // // 子线程中 NSTimer 是默认不能使用,如果想要使之生效,则需要开启循环事件 76 // [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(ratateImageView) userInfo:nil repeats:YES]; 77 // // 开启循环事件 78 // [[NSRunLoop currentRunLoop] run]; 79 // // 如果在子线程执行动画,虽不会发生致命错误,但会有警告,如下 80 // // [UIView transform] must be used from main thread only 81 82 //---------------------- MRC环境中 ------------------------ 83 // // MRC 环境下,子线程中处理的操作需要添加自动释放池 84 // @autoreleasepool { 85 // 86 // for (int i = 0; i < 5; i++) { 87 // [NSThread sleepForTimeInterval:0.5]; 88 // NSString *str = [NSString stringWithFormat:@"i = %d",i]; 89 // NSLog(@"%@", str); 90 // } 91 // } 92 } 93 94 // 更新 UI 95 - (void)updateUI{ 96 self.view.backgroundColor = [UIColor cyanColor]; 97 } 98 99 @end
运行效果:UI更新前 | UI更新后