Delphi XE6在OSX上释放变量时使用ARC

3
我在使用NSObject实例时遇到了一些问题,不符合我的预期实例被释放了。我有一个NSNumber类型的表单变量,在button1中我创建了一个实例并设置了一个值,在button2中我读取该值。如果我不在button1中调用retain,那么该变量将被释放,并且当我点击button2时应用程序将挂起,添加一个retain调用可以使一切正常。
这是在OSX上使用Delphi XE6和FireMonkey。
下面是一些代码:
定义一个NSNumber类型的表单变量:
Fv : NSNumber;

现在添加几个按钮
用于 Button1Click
begin
  Fv := TNSNumber.Wrap(TNSNumber.OCClass.numberWithFloat(4.0));
  ShowMessage(IntToStr(Fv.retainCount)); // value is 1
  Fv.retain; // comment out this to make it crash on button2 click
  ShowMessage(IntToStr(Fv.retainCount)); // value is 2, or 1 without the retain
end;

对于Button2click

begin
   ShowMessage(IntToStr(Fv.retainCount)); // value is 1 or crashes without the retain
 ShowMessage(FloatToStr(Fv.doubleValue));
end;

现在发生的情况是,在 Button1 点击结束时,Delphi 通过减少引用计数释放 Fv,即它的作用相当于超出范围。因此,为了让 Fv 延续存在,我必须添加 Fv.retain。如果我没有使用 retain 单击 Button2,则会导致崩溃。

我应该添加一个 retain 吗?我原本认为这不是必要的,还是我遗漏了其他方面?

tia


1
在针对iOS进行开发时也会出现类似的情况。当我包装Objective-C对象时,有时需要调用retain方法,而有时不需要。我还没有找出其中的区别,但是很容易检测到何时需要使用retain :-) - Hans
我不知道XE 6的OS X编译器实现了ARC。我以为只有iOS和Android? - Rudy Velthuis
2
就此而言,numberWithFloat() 可能会生成一个自动释放变量(大多数“便利构造函数”都是如此)。这些变量确实需要保留,以使它们的生命周期超过下一个自动释放周期。 - Rudy Velthuis
1
@RudyVelthuis:Delphi仅在iOS和Android上使用对象ARC。然而,Delphi的桥接框架与OSX/iOS/Android上的本地平台API进行交互时确实使用接口。在这种情况下,Macapi.Foundation.pas中的type NSNumber = interface(NSValue) - Remy Lebeau
@RudyVelthuis 关于OSX上的ARC,是的,我也认为是这样,但是各种文档都说了矛盾的事情,所以不太确定。就像Remy Lebeau所说的那样。虽然我不确定为什么在变量仍在作用域内时接口被释放。 - daven11
1个回答

2

感谢 @RudyVelthius 和 @RemyLebeau 指导我正确的方向。

问题不在于 Delphi,而在于 Objective C (至少我对 Objective C 的理解是问题所在)。

TNSNumber.OCClass.numberWithFloat(4.0)

这是一个方便的构造函数 - 这意味着它被添加到自动释放池中,并在下次主运行循环执行时释放。

所以我的Delphi接口没问题,但不幸的是它指向的东西已经不存在了。为了保留autorelease变量,请调用retain。只是为了证明这是问题,调用alloc/init应该可以解决它。所以

替换

Fv := TNSNumber.Wrap(TNSNumber.OCClass.numberWithFloat(4.0));

使用

Fv := TNSNumber.Wrap(TNSNumber.Alloc.initWithDouble(4.0));

去掉保留并一切正常。

从这里 https://dev59.com/T0fRa4cB1Zd3GeqP8mJd#801000 规则如下:

如果选择器返回的对象中包含单词 "new"、"alloc"、"retain" 或 "copy",则您拥有该返回对象,并且在完成后需要释放它。

否则,您不拥有它,不应该释放它。如果您想保留对非拥有对象的引用,应调用 -[NSObject retain] 方法。现在,您“拥有”该实例,因此在完成使用后必须调用 -[NSObject release] 方法释放该实例。因此,您不拥有由 -[NSNumber numberWithInt:] 返回的实例,因此在完成时不应调用 -release。如果要将返回的实例保留在当前范围之外(真正超过当前 NSAutoreleasePool 实例的生命周期),应该使用 -retain 方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接