iOS基础深入补完计划--NSThread

iOS基础深入补完计划--NSThread_第1张图片

(由于合在一起感觉一篇太长翻着累)

iOS多线程目前总结了四篇

  • iOS基础深入补完计划--多线程(面试题)汇总
  • iOS基础深入补完计划--NSThread
  • iOS基础深入补完计划--GCD
  • iOS基础深入补完计划--NSOperation

欢迎移步O(∩_∩)O

NSThread

NSThread是轻量级的多线程开发、使用起来也并不复杂、但是使用NSThread需要自己管理线程生命周期、实在不是特别优雅。

(个人)通常的用处分为以下几个地儿吧:
  • 判断/获取当前线程
    if (![[NSThread currentThread] isMainThread]) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            //主线程执行
        });
    }
  • 休眠当前线程
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
  • 给线程起个名字
 [NSThread currentThread].name =@"我要标记这个线程"];

总之NSThread基本都是对线程本身进行操作才会用到。

NSThread相关的几个坑:
  • [thread cancel][NSThread exit];
    • 调用实例对象的- (void)cancel;方法、并不会直接取消线程。其作用只是对应线程的cancel属性设置为YES、线程依旧继续执行。在此之后:
      thread.cancel为YES。
      thread.executing为YES。
      thread.finished为NO。(除非全执行完了)
    • 我们需要在适当的位置执行[NSThread exit];才可以真正关闭线程、余下的代码不会继续执行。同时:
      thread.cancel为你设置的状态(默认应该是NO)。
      thread.executing为NO。
      thread.finished为YES。
  • 一些NSObject的相关扩展方法(performSelector
(主要)依赖Runtime的扩展
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;

基本等于自己手动调用方法、但是其最基本原理是runtime、编译的时候也不会做任何校验。所有的performSelector方法、都是基于runtime进行消息转发的

(主要)依赖NSThread的扩展
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

举了两个例子、注意这里都只跟线程有关(OnMainThread/InBackground)。

(主要)依赖Runloop的扩展
- (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;

第一个很明显了、依赖Runloop的NSRunLoopMode。
第二个表面上看没什么关系、但内部依赖定时器。同时、定时器是依赖Runloop的。

NSThred的API
@interface NSThread : NSObject  {
@private
    id _private;
    uint8_t _bytes[44];
}

// 获取当前线程
+ (NSThread *)currentThread;

// 创建新线程
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

// 是否是多线程
+ (BOOL)isMultiThreaded;

/** 
*   每个线程都维护了一个“键-值”的字典,它可以在线程里面的任何地方被访问,
*   可以使用该字典来保存一些信息,这些信息在整个线程的执行过程中都保持不变。
*   比如,可以使用它来存储在整个线程过程中RunLoop里面多次迭代的状态信息。
*   使用:通过threadDictionary方法获取一个NSMutableDictionary对象,然后添加需要的字段和数据
*/
@property (readonly, retain) NSMutableDictionary *threadDictionary;

// 设置线程睡眠/堵塞
+ (void)sleepUntilDate:(NSDate *)date;

// 设置线程睡眠/堵塞
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

// 结束/退出进程
+ (void)exit;

// 获取线程的优先级
+ (double)threadPriority;

// 设置线程优先级,取值范围0.0~1.0
+ (BOOL)setThreadPriority:(double)p;

// 线程优先级,iOS8以后推荐使用qualityOfService属性,通过量化的优先级枚举值来设置
@property double threadPriority; 

/** 线程优先级
    qualityOfService的枚举值如下:
    NSQualityOfServiceUserInteractive:最高优先级,用于用户交互事件
    NSQualityOfServiceUserInitiated:次高优先级,用于用户需要马上执行的事件
    NSQualityOfServiceDefault:默认优先级,主线程和没有设置优先级的线程都默认为这个优先级
    NSQualityOfServiceUtility:普通优先级,用于普通任务
    NSQualityOfServiceBackground:最低优先级,用于不重要的任务
*/
@property NSQualityOfService qualityOfService;

// 返回当前线程在栈中所占的地址所组成的数组
+ (NSArray *)callStackReturnAddresses NS_AVAILABLE(10_5, 2_0);

// 返回栈空间的符号表
+ (NSArray *)callStackSymbols NS_AVAILABLE(10_6, 4_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);

// 判断当前线程是否是主线程
+ (BOOL)isMainThread NS_AVAILABLE(10_5, 2_0); // reports whether current thread is main

// 获取主线程
+ (NSThread *)mainThread 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);

// 是否正在执行
@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);

/** main是线程入口
*  - (void)main的使用:
*  1. 一般创建线程会子类化NSThread,重写main方法,把关于线程执行的方法都写在里面,这样可以在任何需要这个线程方法的地方直接使用。
*  2. 把线程执行的方法写在main里,是因为线程的操作应该属于线程的本身,而不是每次使用都通过initWithTarget:selector:object:方法,且再一次实现某个方法。
*  3. 当重写了main方法后,同时使用initWithTarget:selector:object:方法初始化,调用某个方法执行任务,系统默认只执行main方法里面的任务。
*  4. 如果直接使用NSThread创建线程,线程内执行的方法都是在当前的类文件里面的。
*/
- (void)main NS_AVAILABLE(10_5, 2_0);

@end

最后

本文主要是自己的学习与总结。如果文内存在纰漏、万望留言斧正。如果不吝赐教小弟更加感谢。

你可能感兴趣的:(iOS基础深入补完计划--NSThread)