How to share a block between MRC and ARC code?

99 Views Asked by At

I have a piece of ARC code written in Objective-C that is creating a block and will want to provide it to some Objective-C MRC code as a callback, to be called when an operation finishes. What is the safest way to do that? My hunch is to just provide the MRC code with a copy.

Is there a guide somewhere that explains the memory management issues that arise when mixing ARC and MRC code?

1

There are 1 best solutions below

0
The Dreams Wind On

Provided you handle the request in-place, you don't need to do anything special under MRR, since blocks themselves make const copies of the referenced values. Thus in this example the callback parameter is retained automagically (and doesn't get released until after dispatch_after's block finishes):

- (void)calculateResult:(void (^)(NSInteger))callback {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{
        callback(_value);
    });
}

If you want to retain the callback and use it later on, then you should explicitly copy the block passed OR still copy it via synthesised properties with copy semantic:

NS_ASSUME_NONNULL_BEGIN

@interface TDWMrrObject ()

@property (copy, nonatomic, nullable) void(^callback)(NSInteger);
@property (assign, nonatomic, nullable) NSTimer *timer;
@property (assign, nonatomic) NSInteger value;

- (void)asyncCalculate;

@end

NS_ASSUME_NONNULL_END

@implementation TDWMrrObject

#pragma mark Lifecycle

- (instancetype)init {
    if (self = [super init]) {
        _value = 4;
    }
    return self;
}

- (void)dealloc {
    [_callback release];
    [_timer invalidate];
    [super dealloc];
}

#pragma mark Actions

- (void)calculateResult:(void (^)(NSInteger))callback {
    self.callback = callback;
    [self asyncCalculate];
}

#pragma mark Private

- (void)asyncCalculate {
    if (_timer) {
        [_timer invalidate];
    }

    self.timer = [NSTimer scheduledTimerWithTimeInterval:4 repeats:NO block:^(NSTimer * _Nonnull timer) {
        _callback(_value);
        self.callback = nil;
        self.timer = nil;
    }];
}

@end