Reputation: 1366
I wanna implement defer
in objective-c. And here's my code:
/**
RAII : ABC->~ABC
*/
@interface DeferImpl_ : NSObject
/**
* init with a callback
*
* @return
*/
-(instancetype) initWithCallback:(void(^)())callback;
/**
* a callback
*/
@property(nonatomic, copy) void(^callback)();
@end
/**
* Defer
*
* @param X { statement; statement; ... }
*
* @return
*/
#define DEFER(X) [[DeferImpl_ alloc] initWithCallback:^X]
#define SAFE_INVOKE(x) do{if(x){(x)();}}while(0)
@implementation DeferImpl_
/**
* invoke callback
*/
-(void) dealloc {
SAFE_INVOKE(self.callback);
}
-(instancetype) initWithCallback:(void(^)())callback {
self = [super init];
self.callback = callback;
return self;
}
@end
The implementation is simple and seems easy to use. But it's buggy!.
Here comes what I feel frustrated.
int main(void)
{
NSInteger count = 0;
DEFER({
NSLog(@"Defer: %@", @(count));
});
count = 123;
NSLog(@"Before defer block!");
return 0;
}
Log is:
2017-01-12 17:31:32.401 test[73724:18571479] Defer: 0
2017-01-12 17:31:32.402 test[73724:18571479] Before defer block!
So is there anyone can tell me why count
still 0 in the block?
Upvotes: 0
Views: 49
Reputation: 130102
You have hidden most of your work into macros (not a good programming style!) and your are missing the basic point - DEFER
calls the block immediately, before even reaching count = 123
.
If you don't assign the result of [DeferImpl_ alloc]
to any variable, it will get released immediately, also immediately calling the block.
Your current code is the same as writing directly:
NSInteger count = 0;
NSLog(@"Defer: %@", @(count));
count = 123;
NSLog(@"Before defer block!");
If you change your code to:
NSInteger count = 0;
id x = DEFER({
NSLog(@"Defer: %@", @(count));
});
count = 123;
NSLog(@"Before defer block!");
Your result will be:
Before defer block!
Defer: 0
Now, why the value is still zero? Because blocks capture variables by value. To capture the reference, you will have to add __block
:
__block NSInteger count = 0;
id x = DEFER({
NSLog(@"Defer: %@", @(count));
});
count = 123;
NSLog(@"Before defer block!");
Before defer block!
Defer: 123
Upvotes: 1