在ARC下将对象安全地作为不透明的上下文参数传递

时间:2012-10-09 22:05:16

标签: ios cocoa automatic-ref-counting

在手动内存管理下,我经常使用这种模式:

NSString * myStr = /* some local object */
[UIView beginAnimation:@"foo" context:(void *)[myStr retain]];

然后,后来和异步:

- (void)animationDidStop:(NSString *)anim finished:(NSNumber *)num context:(void *)context
{
    NSString * contextStr = (NSString *)context;
    // ...
    [contextStr release];
}

即。我手动管理了用作不透明上下文的对象的生命周期。 (旧的UIView动画也适用于我使用的其他类型的API。)

在ARC下,我的直觉是我希望__bridge_retained进入处理程序as suggested here __bridge_transfer。但是这会将Cocoa对象视为CFType而不是因为它真的被桥接了,而只是为了将一个保留在其喉咙上。

这是否有效,这在风格上是否可以接受?如果没有,那么什么是更好的*解决方案?

(接受的答案in this question给出了一个不同的答案,说__bridge单独就可以了,但在我看来这是错误的,因为原始字符串有可能很快被解除分配因为它超出了第一个函数的范围。对吗?)

*请不要说“使用基于块的动画”。 (这不是我要问的。)

2 个答案:

答案 0 :(得分:2)

用你的直觉去吧。 __bridge_retained通过ARC将对象的管理转移给您,而__bridge_transfer则反过来,不要担心将对象视为CFType - 您实际上并不是这样做过度管理。

您认为建议的另一种方法是构建您的代码,以便ARC保留管理,但这很容易让人觉得(并且变得混乱)。使用您正在使用的API保持其设计的值是干净的;只需在管理层交给API的地方适当地评论代码并返回ARC。

答案 1 :(得分:1)

即使使用__bridge_retained / __bridge_transfer对我来说似乎没问题(将所有权转移到CoreFoundation或任何C代码或您自己也是如此,您只需告诉ARC您负责对象所有权在某些时候,并在以后将所有权归还给ARC),如果您愿意,可以在某个地方保留一个 strong 引用您的context对象,以便ARC不回收它的记忆。

这可以通过在您的课程中使用@property(strong)来实现,例如,当您之前执行retain并将其分配给nil之前,当您之前执行了release让字符串去。


请注意,如果您需要在同一个类中保留多个上下文,则可以选择使用NSMutableArray来保留上下文字符串,而不是为每个上下文声明属性。

@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