Under manual memory management, I use this pattern fairly often:
NSString * myStr = /* some local object */
[UIView beginAnimation:@"foo" context:(void *)[myStr retain]];
And then, later and asynchronously:
- (void)animationDidStop:(NSString *)anim finished:(NSNumber *)num context:(void *)context
{
NSString * contextStr = (NSString *)context;
// ...
[contextStr release];
}
i.e. I manually managed the lifetime of an object used as an opaque context. (This is true for the old UIView animations but also for other kinds of API that I use.)
Under ARC, my instinct is that I want to __bridge_retained
going in and __bridge_transfer
in the handler, as suggested here. But this treats a Cocoa object as a CFType
not because it's really bridged, but just for the purpose of shoving a retain down its throat.
Is this valid, and is this stylistically acceptable? If not, what's the better* solution?
(The accepted answer in this question gives a different answer, saying that __bridge
alone is OK, but that seems to me to be wrong, since the original string would be at risk of being deallocated as soon as it goes out of scope in the first function. Right?)
*Please don't say "use block-based animations instead". (That's not what I'm asking about.)
Go with your instinct. __bridge_retained
transfers management of an object to you from ARC, while __bridge_transfer
does the reverse, don't worry about treating the object as a CFType
- you're not really doing that just taking over management.
The other approach you see recommended is to construct your code so that ARC retains management, but this can easily come across as contrived (and get messy). Having the API you're using maintain the value as it is designed to do is clean; just comment the code appropriately where management is handed to the API and returned back to ARC.
Even if using __bridge_retained
/__bridge_transfer
seems fine to me (transferring ownership to CoreFoundation or to any C code or to yourself is quite the same you just tell ARC that you are responsible for the object ownership at some point and give the ownership back to ARC later), you can instead keep a strong
reference on your object you use as your context
somewhere if you prefer, so that ARC does not reclaim its memory.
This can be achieved by using a @property(strong)
in your class for example, affecting it to the value when you previously did your retain
and assigning it to nil
when you previously did your release
to let the string go.
Note that if you need to keep around multiple contexts in the same class, you may opt for the option to use an NSMutableArray
that keeps your context strings around instead of declaring a property for each context.
@interface YourClass ()
@property(strong) NSMutableArray* runningAnimationContexts;
@end
@implementation YourClass
-(id)init {
self = [super init];
if (self) {
self.runningAnimationContexts = [NSMutableArray array];
}
return self;
}
-(void)someMethod
{
// Example with two different parallel animations using old API
NSString * myStr = /* some local object */
[self.runningAnimationContexts addObject:myStr]; // ~ retain
[UIView beginAnimation:@"foo" context:(__bridge)myStr];
...
[UIView commitAnimations];
NSString * myStr2 = /* some other local object */
[self.runningAnimationContexts addObject:myStr2]; // ~ retain
[UIView beginAnimation:@"foo2" context:(__bridge)myStr2];
...
[UIView commitAnimations];
}
- (void)animationDidStop:(NSString *)anim finished:(NSNumber *)num context:(void *)context
{
NSString * contextStr = (__bridge NSString *)context;
// ...
[self.runningAnimationContexts removeObject:contextStr]; // ~ release
}
@end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With