iOS开发 - 多线程实现方案之NSThread篇
NSThread API
//类方法:创建一个线程 + (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); //类方法:直接创建并启动线程 + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument; // 判断当前线程是否是多线程 + (BOOL)isMultiThreaded; //返回线程对象的字典 @property (readonly, retain) NSMutableDictionary *threadDictionary; //阻塞(暂停)线程 + (void)sleepUntilDate:(NSDate *)date; // 使当前线程睡眠指定的时间,单位为秒 + (void)sleepForTimeInterval:(NSTimeInterval)ti; // 退出当前线程 + (void)exit; //线程的调度优先级,取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高 + (double)threadPriority; + (BOOL)setThreadPriority:(double)p; // To be deprecated; use qualityOfService below 设置优先级程度在iOS8.0之后,建议使用qualityOfService属性 @property double threadPriority NS_AVAILABLE(10_6, 4_0); //NSQualityOfService是个枚举,取值有如下优先级从高到低五种: //1.NSQualityOfServiceUserInteractive = 0x21 最高:主要用于与UI交互的操作,各种事件处理以及绘制图像等。 //2.NSQualityOfServiceUserInitiated = 0x19 次高:执行一些明确需要立即返回结果的任务。例如,用户在邮件列表中选择邮件后加载电子邮件。 //3.NSQualityOfServiceDefault = -1 默认:默认的优先级,介于次高级和普通级之间。 //4.NSQualityOfServiceUtility = 0x11 普通:用于执行不许要立即返回结果、耗时的操作,下载或者一些媒体操作等。 //5.NSQualityOfServiceBackground = 0x09 后台:后台执行一些用户不需要知道的操作,它将以最有效的方式运行。例如一些预处理的操作,备份或者同步数据等等。 @property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0); //线程的名字 用于区分线程 @property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0); //接收器的堆栈大小,以字节为单位 @property NSUInteger stackSize NS_AVAILABLE(10_5, 2_0); //是否为主线程 @property (readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0); //实例方法,可以获得线程对象 - (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)); //当前线程对象是否正在执行任务 @property (readonly, getter=isExecuting) BOOL executing NS_AVAILABLE(10_5, 2_0); //当前线程对象是否已执行完任务 @property (readonly, getter=isFinished) BOOL finished NS_AVAILABLE(10_5, 2_0); //当前线程对象是否被取消 @property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0); //取消当前线程 - (void)cancel NS_AVAILABLE(10_5, 2_0); // 开启线程 - (void)start NS_AVAILABLE(10_5, 2_0); // thread body method //You should never invoke this method directly. You should always start your thread by invoking the start method.这个方法是线程的入口函数,当线程开启,默认会调用这个方法,并将线程入口函数selector和target传入,在该方法中对target调用selector。默认情况下,调用完毕,线程就被自动关闭了 - (void)main NS_AVAILABLE(10_5, 2_0); //这个通知只会被NSThread触发一次,条件是当第一个进程在调用了start或者detachNewThreadSelector:toTarget:withObject:方法.这个通知的接收者的一些处理方法都是在主线程上进行的;这是因为这个通知是在系统生产新的子线程之前进行的,所以在监听这个通知的时候,调用监听者的通知方法都会在主线程进行. FOUNDATION_EXPORT NSNotificationName const NSWillBecomeMultiThreadedNotification; //这个通知目前没有实际意义,苹果也仅仅是保留这个扩展通知而已,并没有在NSThread的方法中,来触发这个消息,不过根据这个通知的字面意思来理解,是进程又回到单一线程的时候,会发送这个通知;但在多线程环境下, 这个没有什么实际的处理工作,可暂时忽略; FOUNDATION_EXPORT NSNotificationName const NSDidBecomeSingleThreadedNotification; //在线程被终止前会发送NSThreadWillExitNotification通知给通知中心。由于通知是同步发送的,因此可以确保在线程终止前,通知中心已经收到通知了。应该慎重调用这个方法,因为它无法保证资源的释放,造成内存泄露。 FOUNDATION_EXPORT NSNotificationName const NSThreadWillExitNotification; //线程间通信的方法 //多了一个modes参数,是用来设置runLoop模式 - (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; //可以从任意的两个线程之间作转换 - (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); //隐式创建并启动线程 - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
实例运用及理解说明
1.创建线程的几种方式
//几种创建分线程的方式 //1.实例方法,需要手动开启线程,可以设置线程的更多属性 NSThread *threadOne = [[NSThread alloc]initWithTarget:self selector:@selector(startANewThreadOne:) object:nil]; [threadOne start]; //2.类方法,自动开启线程,不可设置线程更多属性 [NSThread detachNewThreadSelector:@selector(startANewThreadTwo:) toTarget:self withObject:nil]; //3.隐式创建方法 [self performSelectorInBackground:@selector(startANewThreadThree:) withObject:nil]; //4.ios10之后可以使用 NSThread *threadFour = [[NSThread alloc]initWithBlock:^{ //在此执行耗时操作 }]; [threadFour start]; //5.ios10之后可以使用 NSThreadWillExitNotification监听不到线程执行完毕 [NSThread detachNewThreadWithBlock:^{ //执行耗时操作 }];
2.相关属性及API简单使用
- (void)viewDidLoad { [super viewDidLoad]; //监听线程退出的通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(threadWillExitNotice) name:NSThreadWillExitNotification object:nil]; //注意:线程的生命周期,当线程中的任务执行完成之后被释放掉 NSThread *threadOne = [[NSThread alloc]initWithTarget:self selector:@selector(startANewThreadOne:) object:@"分线程一"]; threadOne.name = @"threadOne"; threadOne.qualityOfService = NSQualityOfServiceUserInitiated; NSMutableDictionary *dic = threadOne.threadDictionary; [dic setObject:@"Bob" forKey:@"name"]; [dic setObject:@"18" forKey:@"age"]; [threadOne start]; } -(void)startANewThreadOne:(id)param { if ([NSThread isMultiThreaded]) { for (int i = 0; i < 1000; i++) { NSLog(@"耗时操作 %d",i); if (i == 998) { NSLog(@"睡会在干"); [NSThread sleepForTimeInterval:10]; } } NSThread *thread = [NSThread currentThread]; NSLog(@"-----> %@",thread.threadDictionary); } }
3.线程的状态
init 后会创建一个线程对象,此时线程处于新建New的状态,调用start方法后,会将线程加入到可调度线程池中,此时进入就绪Runnable状态,当CPU调度到当前线程的时候,这时就会处于运行Running的状态,当CPU调度其他线程的时候,当前线程又回到就绪状态,当我们对当前线程调用了sleep或者等待同步锁的时候,此时又会进入线程阻塞Blocked状态, 这时线程对象会被移除可调度线程池,但还存在内存中,当时间到了的时候,重新进入到就绪状态,加入到可调度线程池中,当任务执行完毕或强制退出后,进入到死亡Dead状态,此时线程对象被释放
4.线程安全
多条线程抢夺同一块资源,可能会导致数据错乱和数据安全的问题。解决:互斥锁,加锁可以使线程同步(多条线程在同一条线上按顺序的执行任务)同时需要耗费性能
优点:有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源
- (void)viewDidLoad { [super viewDidLoad]; _ticketCount = 100;//票的总量 NSThread *threadOne = [[NSThread alloc]initWithTarget:self selector:@selector(startSellTickets) object:nil]; threadOne.name = @"售票员1"; [threadOne start]; NSThread *threadTwo = [[NSThread alloc]initWithTarget:self selector:@selector(startSellTickets) object:nil]; threadTwo.name = @"售票员2"; [threadTwo start]; NSThread *threadThree = [[NSThread alloc]initWithTarget:self selector:@selector(startSellTickets) object:nil]; threadThree.name = @"售票员3"; [threadThree start]; } - (void)startSellTickets { while (1) { //self : 锁对象,必须全局唯一,一般选择填self @synchronized (self) { if (_ticketCount > 0) { [NSThread sleepForTimeInterval:0.3];//模拟卖票的耗时操作 _ticketCount--; NSLog(@"%@卖出去一张票,还剩%d张票",[NSThread currentThread].name,_ticketCount); }else{ NSLog(@"end"); break; } } } }
5.原子和非原子属性
atomic:原子属性,线程安全,需要消耗大量的资源,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,非线程安全,适合内存小的移动设备
原文地址:去查看
//@property(nonatomic, retain) UITextField *userName; - (UITextField *) userName { return userName; } - (void) setUserName:(UITextField *)userName_ { [userName_ retain]; [userName release]; userName = userName_; } //@property(retain) UITextField *userName; - (UITextField *) userName { UITextField *retval = nil; @synchronized(self) { retval = [[userName retain] autorelease]; } return retval; } - (void) setUserName:(UITextField *)userName_ { @synchronized(self) { [userName release]; userName = [userName_ retain]; } }
6.线程间的通信
一个线程传递数据给另一个线程,在一个线程中执行完特定任务后,转到另一个线程继续执行任务,比如:在子线程中下载图片,在回到主线程展示图片
- (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 NS_AVAILABLE(10_5, 2_0);
以上博文如有理解有误的地方,欢迎指正!

浙公网安备 33010602011771号