【问题标题】:Passing objects safely as opaque context params under ARC在 ARC 下将对象作为不透明的上下文参数安全地传递
【发布时间】:2012-10-09 22:05:16
【问题描述】:

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

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 进入,__bridge_transfer 在处理程序中,as suggested here。但这将 Cocoa 对象视为 CFType 并不是因为它真的被桥接了,而只是为了将保留物塞进它的喉咙。

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

(接受的答案in this question 给出了不同的答案,说单独__bridge 是可以的,但在我看来这是错误的,因为原始字符串一旦发出就会有被释放的风险第一个函数的作用域。对吧?)

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

【问题讨论】:

  • void* 上下文对象的形式传递保留对象一开始就非常难看。如果你真的需要这样做,我不会担心它是否“在风格上可以接受”。

标签: ios cocoa automatic-ref-counting


【解决方案1】:

跟随你的直觉。 __bridge_retained 将对象的管理权从 ARC 转移给您,而 __bridge_transfer 则相反,不要担心将对象视为 CFType - 您并没有真正这样做只是接管管理。

您看到推荐的另一种方法是构建您的代码,以便 ARC 保留管理,但这很容易让人觉得做作(并且变得混乱)。让您使用的 API 保持其设计的价值是干净的;只需在将管理交给 API 并返回给 ARC 的地方适当地注释代码即可。

【讨论】:

  • 这是有道理的。看来看待它的方式并不是你实际上保留了CFType,你只是告诉ARC不要释放它。当您桥接转回时,您是在告诉 ARC 恢复所有权。这是一种更简洁的思考方式。谢谢。
  • 实际上,查看 llvm 文档,我认为这确实确实保留并传输了对象。但我更高兴不在概念上那样考虑它。
  • @BenZotto - 更多的是对象没有被释放而不是被保留;该对象可能(但不总是)已经保留,因此不需要额外的保留。
【解决方案2】:

即使使用__bridge_retained/__bridge_transfer 对我来说似乎很好(将所有权转移给 CoreFoundation 或任何 C 代码或你自己都是一样的,你只需告诉 ARC 你在某个时候对对象所有权负责,并且稍后将所有权归还给 ARC),您可以改为在您用作 context 的对象上保留 strong 引用(如果您愿意),这样 ARC 就不会回收其内存。

这可以通过在你的类中使用 @property(strong) 来实现,例如,在你之前执行 retain 时将其影响到值,并在之前执行 release 时将其分配给 nil 以让字符串去。


请注意,如果您需要在同一个类中保留多个上下文,您可以选择使用 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

【讨论】:

  • 在这种情况下,由于 ARC 强制执行某种类型的演员,您可能希望使用简单的__bridge,对吗?
  • 谢谢。这是一个深思熟虑的替代方案,在某些情况下可能很有意义。
猜你喜欢
  • 2014-03-12
  • 2012-12-21
  • 2017-12-07
  • 1970-01-01
  • 2020-09-13
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多