Delphi XE6 ARC on OSX releasing variables
当我没想到时,我遇到了 NSObject 实例被释放的问题。我有一个 NSNumber 类型的表单变量,在 button1 中创建一个实例并设置一个值,在 button2 中读取该值。如果我不在按钮 1 中调用保留,那么当我单击按钮 2 时,变量被释放并且应用程序挂起,添加对保留的调用使一切正常。
这是在 OSX 上使用 Delphi XE6 和 firemonkey。
这是一些代码
定义一个 NSNumber 类型的表单变量
1 | Fv : NSNumber; |
现在添加几个按钮
对于 Button1Click
1 2 3 4 5 6 | 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
1 2 3 4 | begin ShowMessage(IntToStr(Fv.retainCount)); // value is 1 or crashes without the retain ShowMessage(FloatToStr(Fv.doubleValue)); end; |
现在似乎正在发生的事情是在 Button1 单击结束时,delphi 通过减少引用计数来释放 Fv - 即它的行为就像它超出了范围。所以要让 Fv 闲逛,我必须添加 Fv.retain。如果我在没有保留的情况下单击 button2,那么它会崩溃。
我应该添加一个保留 - 我认为没有必要,还是我错过了其他东西?
tia
感谢@RudyVelthius 和@RemyLebeau 让我走上了正确的道路。
问题不是delphi问题,而是客观C问题(至少我对objective C的理解是问题)。
1 | TNSNumber.OCClass.numberWithFloat(4.0) |
是一个方便的构造函数 - 这意味着它被添加到自动释放池中,并在下次主运行循环执行时释放。
所以我的 delphi 界面很好,但不幸的是它指向的东西不再存在。在调用保留周围保留一个自动释放变量。只是为了证明这是调用 alloc/init 应该解决的问题。所以
替换
1 | Fv := TNSNumber.Wrap(TNSNumber.OCClass.numberWithFloat(4.0)); |
与
1 | Fv := TNSNumber.Wrap(TNSNumber.Alloc.initWithDouble(4.0)); |
并删除保留,一切正常。
从这里 https://stackoverflow.com/a/801000/416047 规则是
If the selector returning an object has the word"new","alloc",
"retain" or"copy" in it, then you own the returned object and are
responsible for releasing it when you are finished.Otherwise you do not own it and should not release it. If you want to
keep a reference to a non-owned object, you should call -[NSObject
retain] on that instance. You now"own" that instance an must
therefore call -[NSObject release] on the instance when you are done
with it. Thus you do not own the instance returned by -[NSNumber
numberWithInt:] and should not call -release on it when you are done.
If you want to keep the returned instance beyond the current scope
(really beyond the lifetime of the current NSAutoreleasePool
instance), you should -retain it.