首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >学习NSBlockOperation

学习NSBlockOperation
EN

Stack Overflow用户
提问于 2012-06-13 06:01:01
回答 3查看 16.9K关注 0票数 20

我非常喜欢块,但没有将它们用于并发性。在谷歌搜索之后,我把这个想法拼凑在一起,把我学到的一切都藏在一个地方。目标是在后台执行一个块,当它完成后,执行另一个块(如UIView动画).

代码语言:javascript
复制
- (NSOperation *)executeBlock:(void (^)(void))block completion:(void (^)(BOOL finished))completion {

    NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:block];

    NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
        completion(blockOperation.isFinished);
    }];

    [completionOperation addDependency:blockOperation];
    [[NSOperationQueue mainQueue] addOperation:completionOperation];    

    NSOperationQueue *backgroundOperationQueue = [[NSOperationQueue alloc] init];
    [backgroundOperationQueue addOperation:blockOperation];

    return blockOperation;
}

- (void)testIt {

    NSMutableString *string = [NSMutableString stringWithString:@"tea"];
    NSString *otherString = @"for";

    NSOperation *operation = [self executeBlock:^{
        NSString *yetAnother = @"two";
        [string appendFormat:@" %@ %@", otherString, yetAnother];
    } completion:^(BOOL finished) {
        // this logs "tea for two"
        NSLog(@"%@", string);
    }];

    NSLog(@"keep this operation so we can cancel it: %@", operation);
}

我的问题是:

  1. 当我运行它的时候它能工作,但是我有没有遗漏任何东西..。隐藏的地雷?我还没有测试取消测试(因为我还没有发明一个长时间的操作),但是这看起来会起作用吗?
  2. 我担心我需要限定我的backgroundOperation声明,以便我可以在完成块中引用它。编译器没有抱怨,但是是否有一个隐藏在那里的保留周期?
  3. 如果“字符串”是一个ivar,那么如果我在块运行时观察它的键值会发生什么呢?或者在主线程上设置一个计时器并定期记录它?我能看到进展吗?我能宣布它是原子的吗?
  4. 如果这如我所期望的那样工作,那么它似乎是隐藏所有细节并获得并发性的好方法。为什么苹果不给我写这个?我错过了什么重要的事情吗?

谢谢。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-07-06 05:17:00

我不是NSOperation或NSOperationQueues方面的专家,但我认为下面的代码更好一些,尽管我认为它还有一些警告。可能对某些目的来说已经足够了,但并不是并发的通用解决方案:

代码语言:javascript
复制
- (NSOperation *)executeBlock:(void (^)(void))block
                      inQueue:(NSOperationQueue *)queue
                   completion:(void (^)(BOOL finished))completion
{
    NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:block];
    NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
        completion(blockOperation.isFinished);
    }];
    [completionOperation addDependency:blockOperation];

    [[NSOperationQueue currentQueue] addOperation:completionOperation];
    [queue addOperation:blockOperation];
    return blockOperation;
}

现在让我们使用它:

代码语言:javascript
复制
- (void)tryIt
{
    // Create and configure the queue to enqueue your operations
    backgroundOperationQueue = [[NSOperationQueue alloc] init];

    // Prepare needed data to use in the operation
    NSMutableString *string = [NSMutableString stringWithString:@"tea"];
    NSString *otherString = @"for";

    // Create and enqueue an operation using the previous method
    NSOperation *operation = [self executeBlock:^{
        NSString *yetAnother = @"two";
        [string appendFormat:@" %@ %@", otherString, yetAnother];
    }
    inQueue:backgroundOperationQueue 
    completion:^(BOOL finished) {
        // this logs "tea for two"
        NSLog(@"%@", string);
    }];

    // Keep the operation for later uses
    // Later uses include cancellation ...
    [operation cancel]; 
}

对你的问题的一些回答:

  1. 取消.通常是子类NSOperation,这样就可以检查self.isCancelled并提前返回。参见这条线,这是一个很好的例子。在当前的示例中,您无法从您提供的生成NSBlockOperation的块中检查操作是否已被取消,因为当时还没有这样的操作。在调用块时取消NSBlockOperation显然是可能的,但累赘是可能的。NSBlockOperation是针对特定的简单情况的。如果需要取消,则最好将NSOperation :)子类化。)
  2. 我看不出有什么问题。不过要注意两件事。(A)我更改了在当前队列b中运行完成块的方法do )需要一个队列作为参数。正如@Mike所说,您应该提供更好的background queue,这样您就不需要为每个操作创建一个,并且可以选择使用哪个队列来运行您的内容:)
  3. 我想是的,你应该做string atomic。您不应该忘记的一件事是,如果您向队列提供了几个操作,它们可能不会按该顺序运行(必然),因此您可能会在string中得到一条非常奇怪的消息。如果需要一次运行一个操作,则可以在开始对操作进行排队之前执行:[backgroundOperation setMaxConcurrentOperationCount:1];。然而,在文档中有一个值得阅读的注释: 附加操作队列行为操作队列根据其优先级和就绪性执行其排队操作对象。如果所有排队操作对象都具有相同的优先级,并准备好在将它们放入队列时执行--也就是说,它们的isReady方法返回是的--它们将按照提交到队列的顺序执行。对于最大并发操作数设置为1的队列,这等于串行队列。但是,您不应该依赖于操作对象的串行执行。操作准备状态的更改可以更改结果的执行顺序。
  4. 我想在读了这几行之后,你知道:)
票数 21
EN

Stack Overflow用户

发布于 2012-06-13 07:22:17

您不应该为每个NSOperationQueue调用创建一个新的executeBlock:completion:。这是昂贵的,而且这个API的用户无法控制一次执行多少次操作。

如果要返回NSOperation实例,则应该由调用方决定将它们添加到哪个队列。但是在这一点上,您的方法并没有做任何有用的事情,调用方也可以自己创建NSBlockOperation

如果您只想要一种简单而简单的方法在后台拆分一个块并在它完成时执行一些代码,那么使用dispatch_*函数进行一些简单的GCD调用可能会更好。例如:

代码语言:javascript
复制
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // do your background work
    // ...

    // now execute the 'completion' code.
    // you often want to dispatch back to the main thread to update the UI
    // For example:

    dispatch_async(dispatch_get_main_queue(), ^{
        // update UI, etc.
        myLabel.text = @"Finished";
    });

});
票数 10
EN

Stack Overflow用户

发布于 2018-07-27 08:59:23

没有必要设置一个块来在完成时运行并添加类似的依赖项。与所有NSBlockOperation子类一样,NSOperation子类已经具有一个completionBlock属性,该属性将在块完成其工作后自动运行:

代码语言:javascript
复制
@property(copy) void (^completionBlock)(void);

当完成块移动到finished状态时,将运行完成块。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11009027

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档