学习NSBlockOperation(Learning NSBlockOperation)

2019-07-29 21:34发布

我是块的大风扇,但没有使用他们的并发。 一些google搜索后,我拼凑出这个想法隐藏一切,我在一个地方的经验教训。 我们的目标是在后台执行块,当它完成,执行另一个块(如UIView的动画)...

- (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. 如果“串”是伊娃,如果我键值观察到它,而该块正在运行会发生什么? 或设置在主线程一个定时器和定期记录呢? 我能看到进展? 我将宣布它的原子?
  4. 如果这个工程,我期望的那样,那么它似乎想隐藏的所有细节,并得到并发性的好方法。 为什么不写苹果这给我吗? 我失去了一些重要的东西?

谢谢。

Answer 1:

我不是的NSOperation或NSOperationQueues专家,但我认为下面的代码是一个好一点,虽然我认为它有一些注意事项仍在。 也许足以使一些目的,但不是并发一个通用的解决方案:

- (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;
}

现在,让我们使用它:

- (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 s,而上调用此块是显然可行的,但很麻烦 。 NSBlockOperation s为特定容易的情况。 如果你需要取消你最好的子类NSOperation :)

  2. 我没有在这里看到的一个问题。 虽然注意到两两件事。 一)我改变了方法做以运行在当前队列b中的完成的块)的队列被要求作为一个参数。 正如@Mike韦勒说,你应该更好的货源background queue ,所以你不需要每次创建每个操作之一,可以选择什么样的排队用来运行你的东西:)

  3. 我想是的,你应该让string atomic 。 有一件事你不应该忘记的是,如果你提供几个操作到队列他们可能没有按照这个顺序运行(必须的),所以你可能在你一个很奇怪的消息结束string 。 如果你需要同时运行一个操作顺序,你可以这样做: [backgroundOperation setMaxConcurrentOperationCount:1]; 前开始排队您的操作。 有一个在读,值得一提的文档 ,但:

    附加操作队列行为的操作队列基于它们的优先级和准备执行它的排队操作对象。 如果所有排队的操作对象具有相同的优先级,并准备当它们被放入队列,那就是执行,他们的isReady方法返回YES,他们在它们被提交给队列的顺序执行。 对于一个队列,其并发操作的最大数量设置为1,这相当于一个串行队列。 但是,你不应该依赖于操作对象的串行执行。 在操作的准备就绪的变化可改变所得执行顺序。

  4. 我想读你知道这些行后:)



Answer 2:

你不应该创建一个新的NSOperationQueue每个executeBlock:completion:调用。 这是昂贵的,这个API的用户拥有多少操作可以同时执行的控制。

如果要返回NSOperation情况下,那么你应该把它留给调用者来决定哪个队列将它们添加到。 但在这一点上,你的方法真的是什么都不做有益的和呼叫者不妨创建NSBlockOperation自己。

如果你只是想要一个简单易用的方式分拆的背景块,并执行一些代码,当它结束时,你可能会更好做一些简单的GCD与调用dispatch_*功能。 例如:

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";
    });

});


Answer 3:

有没有必要设置要在完成运行块,并添加依赖这个样子。 NSBlockOperation像所有NSOperation子类已经有一个completionBlock当块已完成其工作,它会自动运行特性:

@property(copy) void (^completionBlock)(void);

当它的块移动到完成块运行finished状态。



文章来源: Learning NSBlockOperation