Search code examples
objective-cthread-safetydispatch

How do I pass a var from within a dispatch_sync block?


I need to extract a NSData item from within a block of a dispatch_sync().

Specifically, I can't do a 'return data' in mid block due to a compiler error:

Incompatible block pointer types passing 'NSData *(^)(void)' to parameter of type 'dispatch_block_t' (aka 'void (^)(void)')

The error occurs here:

if (data)return data; // data is present in memory cache

- (NSData  *)dataForFile:(NSString *)fileName {
    __block NSData *data;
    // ... having problem with 'data' var within dispatch_sync
    dispatch_sync(serialQueue, ^{
        data = [_memoryCache objectForKey:fileName];
        if (data)return data; // data is present in memory cache

        NSString *archivePath = [_cacheDirectory stringByAppendingPathComponent:fileName];
        data = [NSData dataWithContentsOfFile:archivePath];

        if (data) {
            [self cacheData:data toFile:fileName]; // put the recently accessed data to memory cache
        }
    });
    return data;
}

...should I put a return type vs a (void) on the block? Or need it be re-engineered? One solution is to replace the dispatch with a @synchronized().

Question: is it possible to break out of a block & return during a dispatch_sync()?


Solution

  • Ask yourself where the return value would go. You've got no <var> = dispatch_sync(...), so you don't expect a result, which is good as dispatch_sync doesn't return one!

    dispatch_sync takes a block with return type of void - your block cannot return a value with return. However a block can assign to captured local variables which have the __block attribute - just as your data variable does, and you do assign to that within your block. You don't need to assign to data and return it, which seems to be what you are trying to do.

    At a guess what you want is:

    dispatch_sync(serialQueue, ^{
        data = [_memoryCache objectForKey:fileName];
        if (!data) // data is NOT present in memory cache
        {
            NSString *archivePath = [_cacheDirectory stringByAppendingPathComponent:fileName];
            data = [NSData dataWithContentsOfFile:archivePath];
    
            if (data) {
                [self cacheData:data toFile:fileName]; // put the recently accessed data to memory cache
            }
        }
    });
    

    HTH