8

我已经覆盖了一个请求在线服务以获得结果的吸气剂。如何强制 getter 仅从同步块返回结果?

@interface MyClass ()

@property (nonatomic, strong) NSMutableDictionary* myDictionary;
@end 

@implementation MyClass

-(NSMutableDictionary*) myDictionary {

    dispatch_async(queue, ^{
         /* perform online request */
         dispatch_sync(dispatch_get_main_queue(), ^{
             // I need to obtain lock until this line gets executed and only then return
         });
    });
}
@end

通过至少 3 个小时的相当不错的谷歌搜索,我遇到了dispatch_group_asyncdispatch_semaphore__block。我不知道我是否用错了,但没有达到目的。

更新 1:

myDictionary是一个异步属性。我想看看这是否可以通过 getter 本身来实现。

4

3 回答 3

4

@Kishor 当请求真正异步时,UI 不会阻塞。您的“异步”请求阻止 UI 的原因是,实际上它们不是异步的。这是伪代码中的原因:

- (double) notTruelyAsyncValue 
{
    __block double someExpensiveDoubleToCompute = 0.0;

    // One of the many ways that we can coordinate concurrent threads.
    dispatch_semaphore_t sema_done = dispatch_semaphore_create(0);

    dispatch_async(not_the_main_queue, ^(void) {
       // Simulate long processing time.
       sleep(5); 
       someExpensiveDoubleToCompute = 3.1415926535;
       dispatch_semaphore_signal(sema_done);
    });

    // We can't return until the async block has returned.
    // So we wait until it's done. If we wait on the main queue
    // then our UI will be "frozen". 
    dispatch_semaphore_wait(sema_done, DISPATCH_TIME_FOREVER);

    // Now we have our result we free the resources and return
    dispatch_release(sema_done);

    return someExpensiveDoubleToCompute;
}

如果您从异步线程调用此方法,它不会阻塞 UI,但如果您从主队列/线程调用它,那么它将阻塞 UI,因为您正在等待主线程上的信号量。不管你如何实现你的等待,它总是会阻塞 UI,因为主线程是一个串行队列。这意味着在您的异步块完成之前,主队列上的任何其他块或事件都不会运行。

如果您不想阻止您的 UI,则不要调用任何可能从主线程阻塞的内容。一个很好的模式是使用@Collin 建议的完成块。模式如下:

- (void) computeAnyncValueWithCompletionBlock:((void)^(double value))completionBlock 
{
     dispatch_async(not_the_main_queue, ^(void) {
          // do some expensive computation.
          double value = 3.1415926535;
          completionBlock(value);
     });
}

这可以从任何地方调用并且永远不会阻塞。

于 2013-04-19T19:04:30.817 回答
3

如果我不清楚,请纠正我 - 听起来你想要做的是在后台下载,同时还以非异步方式从方法返回?如果您考虑一下,您有点试图同时做两件相互矛盾的事情:该方法要么必须阻塞直到它返回,要么异步返回。

我认为你想要的是一个完成块。您可以创建第二个方法,而不是覆盖myDictionary,而是创建执行以下操作的第二个方法:

- (void)downloadWithCompletion:(void(^)(NSDictionary *dictionary))completion
{
    dispatch_async(queue, ^{
         /* perform online request */
         // Create an NSDictionary from what was downloaded.
         NSDictionary *dictionary = <parsed request data>
         dispatch_sync(dispatch_get_main_queue(), ^{
             // Call the completion block using the info that was downloaded
             // where self.myDictionary could be set.
             completion(dictionary);
         });
    });
}
于 2013-04-04T07:43:14.553 回答
3

事实证明,一点也不难。我试图用我的发现来更新这个,我会继续更新这个。

异步属性的 getter 应该如何表现?

  1. Perform asynchronous request如果属性不可用并且set the property. (用于延迟加载)
  2. 可用时归还该财产。

挑战:

  • 用户界面冻结
  • 无效退货

虽然,它可能会令人困惑,因为为什么不在这里使用同步方法?, 答案是它会冻结 UI。

没有人知道异步请求何时会完成,但这并不意味着对可用性状态的全部需求应该是未知的。从硬件、内核到更高级别的 API,所有系统中都有相应的机制。您可以将Protocols 和 Delegates作为传达此信息的一种方式。


为什么不将协议和委托用于异步属性?

  1. 我将不得不强制在所有引用类中实现委托 ->不是 getter
  2. 我不希望其他类知道它是一个异步属性,如果他们想要数据,他们会在数据可用时得到它,而他们不知道如何检索它的性质。(obv 没有冻结 UI)。

我们如何在不使用协议和委托或将其转换为同步调用的情况下实现这一点?

答案是使用条件变量。请注意,此条件变量与我们用于分支的条件变量不同。它必须是线程安全的,并在编译器和内核级别得到支持。

  1. NS条件

从官方文档,

The NSCondition class implements a condition variable whose semantics follow 
those used for POSIX-style conditions. A condition object acts as both a lock 
and a checkpoint in a given thread. The lock protects your code while it tests 
the condition and performs the task triggered by the condition. The checkpoint 
behavior requires that the condition be true before the thread proceeds with its 
task. While the condition is not true, the thread blocks. It remains blocked until 
another thread signals the condition object.

我所要做的就是让这个 getter 方法在不使用委托的情况下知道异步请求的完成。

-(NSMutableDictionary*) myDictionary {
    if(!_myDictionary) {
        _myDicitonary = [self someOtherMethod];
    }
    return _myDictionary;
}

虽然锁和异步请求可以在 getter 本身中实现,但我拒绝这样做以方便操作锁。此外,这是一个很好的逻辑分离:)

- (NSMutableDictionary *)someOtherMethod
{
    NSCondition *lockForCompletion = [[NSCondition alloc] init];
    __block BOOL available = NO;
    __block NSMutableDictionary* tempDict = [[NSMutableDictionary alloc] init];

    [lockForCompletion lock]; // acquire the lock

        dispatch_async(queue, ^{
             /* perform online request */
             dispatch_sync(dispatch_get_main_queue(), ^{
                 [tempDict setObject:myResponse forKey:@"mykey" count:1];
                 available = YES;
                 [lockForCompletion signal];
             });
        });
    while(!available) {
        [lockForCompletion wait];
    }

    [lockForCompletion unlock];
    return tempDict;
}

我还想指出,最初布尔谓词available似乎根本没有必要,因为wait它将禁止控制超出它。但实际上布尔谓词在保持锁定方面起着非常重要的作用,如文档中所述。

A boolean predicate is an important part of the semantics of using conditions 
because of the way signaling works. Signaling a condition does not guarantee 
that the condition itself is true. There are timing issues involved in signaling 
that may cause false signals to appear. Using a predicate ensures that these 
spurious signals do not cause you to perform work before it is safe to do so. 
The predicate itself is simply a flag or other variable in your code that you test 
in order to acquire a Boolean result.
于 2013-04-05T04:00:27.860 回答