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);
View Code

实例运用及理解说明

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:^{
        //执行耗时操作
    }];
View Code

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);
    }
    
}
View Code

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];
    }
}
View Code

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);
View Code

 

以上博文如有理解有误的地方,欢迎指正!

 

 

posted @ 2017-03-12 22:00  Funky、  阅读(222)  评论(0)    收藏  举报