NSOperation那些事
博客专区 > dashL 的博客 > 博客详情
NSOperation那些事
dashL 发表于4年前
NSOperation那些事
  • 发表于 4年前
  • 阅读 378
  • 收藏 1
  • 点赞 0
  • 评论 0

新睿云服务器60天免费使用,快来体验!>>>   

摘要: NSOperation,NSOperationQueue,iOS

iOS中多线程编程主要有三种方式,1)Thread,2)NSOperation与NSOperationQueue,3)GCD,本文简要的介绍一下我理解中的Thread和NSOperation与NSOperationQueue,重点在于介绍NSOperation和NSOperationQueue~

1)NSThread

NSThread,主要有三种创建方式,

- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
- (void)performSelectorInBackground:(SEL)selector withObject:nil];

前面两种会显示的创建一个Thread,后一种方法则是NSObject (NSThreadPerformAdditions)的实例方法,当调用这种方法的时候,系统会隐式的创建一个Thread。

Thread的缺点是使用者必须手动去管理,并通过加锁等方式确保线程同步,iOS中有两种加锁方式,即NSLock与NSCondition,之后又通过@synchronized替代了NSLock复杂的书写方式,确保线程同步。

NSThread通过以下四种方式进行线程之间的通信。

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);

2)NSOperation

NSOperation是一个基类,你可以通过继承的方式,独立创建子Operation,或者通过实例化NSBlockOperation或者NSInvocationOperation使用operation。

查看NSOperation的官方文档,对下面property及method进行解释。

1)- (id)init;//NSOperation实例初始化方法,用于创建一个NSOperation实例。
2)- (void)start;//该方法是operation的起点,如果需要创建并发operation必须,覆盖start方法。同时调用start方法,会默认做各种验证。
3)- (BOOL)isCancelled;//该property,是用于标示某个operation是否cancel。对于多线程来说需要不断检测这个值。
4)- (void)cancel;//调用cancel方法会取消一个operation,但是如果operation加入到Queue中或者operation已经start了,则无法取消成功,调用cancel也不一定立即执行cancel操作,需要等待时间周期。
5)- (BOOL)isExecuting;//判定operation是否正在执行。
6)- (BOOL)isFinished;//判定operation是否完成,cancel掉某个operation,也会将该operation的该字段设置成为YES。
7)- (BOOL)isConcurrent;//判定该线程是否是并发线程,即调用该operation的start方法的线程是否与operation所在线程相同。
8)- (BOOL)isReady;//在start方法开始之前,需要确定operation是否ready,默认为YES,如果该operation没有ready,则不会start。
9)- (void)addDependency:(NSOperation *)op;//该方法用于配置operation之间的依赖关系,涉及执行顺序稍后会介绍。如果不是手动调用start去执行operation,一定要在将其加入到Queue之前做好依赖,因为一旦加入到Queue中,其也许很快会执行,依赖关系将不会起作用。
10)- (void)removeDependency:(NSOperation *)op;//相对应add,其为移除两个operation之间的依赖关系。
11)- (NSArray *)dependencies;//获取operation的依赖关系的数组。
12)- (NSOperationQueuePriority)queuePriority;//如果将operation加入到Queue中,设定其在Queue中的优先级,优先级高的先执行的概率大,但并不代表一定会先执行,执行顺序稍后介绍。
13)- (void)setQueuePriority:(NSOperationQueuePriority)p;//setter方法。
14)- (void (^)(void))completionBlock NS_AVAILABLE(10_6, 4_0);//在operation完成之后会调用completionBlock,你可以自定义执行行为。
15)- (void)setCompletionBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);//setter方法
16)- (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0);//设定是否等待operation执行结束,如果为YES,该线程会一直等待operation执行结束,才会执行接下来的代码。
17)- (double)threadPriority NS_AVAILABLE(10_6, 4_0);//设定operation的线程优先级,涉及执行顺序稍后介绍。线程优先级默认为0.5,最低为0,最大为1.即使设定了线程优先级,也只能保证其在main方法范围内有效,operation的其他代码仍然执行在默认线程。
18)- (void)setThreadPriority:(double)p NS_AVAILABLE(10_6, 4_0);setter方法。

手动创建Operation

1)非并发Operation

如果该operation为非并发operation,则只需要重写其main方法,并正确的执行cancel操作即可,需要operation定时检测isCanceled值。

2)并发Operation

如果需要创建一个并发的Operation,并手动执行,则需要重写main,start,isCancelled,isReady,isConcurrent,isFinished,isExcluting,dependency,queuepripority方法,并维护每个property的KVO,同时确保每个property的原子性。

利用NSInvocationOperation或者NSBlockOperation

大多数开发都不需要自己独立创建operation,而只需要创建一个invocationOperation实例或者创建一个BlockOperation实例,之后将其加入到OperationQueue中,网上很多例子,不再赘述。需要注意的一点是,你应该尽量持有operation的引用,而不要让operation的block做数据存储操作,因为operation的线程被Queue持有,而不是自己独立管理线程。

3)NSOperationQueue

你可以假想成NSOperationQueue是一个任务管理队列,你只需要封装好每个任务(operation),然后将其加入到OperationQueue中,其就会在某个时间执行。

- (void)addOperation:(NSOperation *)op;
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

上述三种方式,就是将operation加入到queue中,你可以不断的将operation加入到queue中,但是如果operation加入的过多,则调度operation所耗费的时间会大大增加,导致系统性能下降。

- (NSInteger)maxConcurrentOperationCount;//设定queue的最大并发数
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;//setter方法
- (void)setSuspended:(BOOL)b;//设置queue是否挂起,YES为将该Queue挂起,NO则取消挂起。如果一个queue处于挂起状态,则其不会相应启动任何operation,如果碰巧operation的waituntilfinished设置成为YES,则陷入死锁。
- (BOOL)isSuspended;//判定某个queue是否挂起
- (void)cancelAllOperations;//取消所有operations
- (void)waitUntilAllOperationsAreFinished;//等待所有的operations执行结束
+ (id)currentQueue NS_AVAILABLE(10_6, 4_0);//获取当前的queue
+ (id)mainQueue NS_AVAILABLE(10_6, 4_0);//获取主线程的queue

4)执行顺序

主要介绍所有operation放在同一个Queue中的执行顺序。先上一段测试代码。

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        self.count += 2;
        NSLog(@"o1:%d",self.count);
    }];
    [operation setCompletionBlock:^{
        NSLog(@"o1,finished");
    }];
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        self.count += 1;
        NSLog(@"o2:%d",self.count);
    }];
    [operation2 setCompletionBlock:^{
        NSLog(@"o2,finished");
    }];
    [operation2 addDependency:operation];
    NSOperationQueue *operationQ = [[NSOperationQueue alloc] init];
    [operation2 setQueuePriority:NSOperationQueuePriorityHigh];
    [operation setThreadPriority:0];
    operationQ.maxConcurrentOperationCount = 1;
    NSArray *array = @[operation,operation2];
    [operationQ addOperations:array waitUntilFinished:YES];

在实例代码中,首先设定Queue为单队列,即最大并发数为1。此时主要有两个考虑因素,首先考虑Operation的依赖关系,

[operation2 addDependency:operation];

Operation2依赖于Operation,所以一定是Operation先执行,之后继续执行Operation2,如果将这行代码注释掉,则开始考虑Operation之间的QueuePriority,在本例子中

 [operation2 setQueuePriority:NSOperationQueuePriorityHigh];

Operation2的queuePriority高于Operation,故先执行Operation2,之后再执行Operation,如果再将这行代码注释掉,则考虑加入的先后顺序

 NSArray *array = @[operation,operation2];

operation先于operation2,则会先执行operation,之后再执行operation2。threadPriority,只会影响其在具体线程资源的priority,并不会影响queue的调度顺序。

假设在本例中的OperationQueue的最大并发数为2,则仍然是按照此顺序调度,不过由于最大并发数为2,假设去除依赖关系,所以operation与operation2需要看谁先轮到时间片,谁先谁后则不确定,如果仍旧存在依赖关系,仍旧是operation先执行,operation2后执行,但是哪个先执行完成并不确定。

ps:在本例中采用了blockOperation方式,同时在block中引用了self.count,并将其值打印出来,可以看到其值是乱的,多线程,你还能要求神马呢。。欢迎交流~~

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 0
博文 5
码字总数 3765
×
dashL
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: