问题
I'm having a problem with NSObject instances being freed when I don't expect. I have a form variable of type NSNumber, in button1 I create an instance and set a value, in button2 I read the value. If I don't call retain in button 1 then the variable is freed and the app hangs when I click button2, adding a call to retain makes everything work.
This is on OSX using Delphi XE6 with firemonkey.
Here's some code
Define a form variable of type NSNumber
Fv : NSNumber;
Now add a couple of buttons
for 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;
for Button2click
begin
ShowMessage(IntToStr(Fv.retainCount)); // value is 1 or crashes without the retain
ShowMessage(FloatToStr(Fv.doubleValue));
end;
Now what seems to be happening is at the end of Button1 click, delphi is releasing Fv by decrementing the reference count - i.e. it acts like its going out of scope. So to make Fv hang around I have to add the Fv.retain. If I click button2 without the retain, then it crashes.
Should I put a retain in - I didn't think it was necessary, or am I missing something else?
tia
回答1:
Thanks to @RudyVelthius and @RemyLebeau for putting me on the right path.
The problem isn't a delphi problem but an objective C problem (at least my understanding of objective C is the problem).
TNSNumber.OCClass.numberWithFloat(4.0)
is a convenience constructor - this means its added to the auto release pool, and freed next time the main run loop executes.
So my delphi interface is fine, but unfortunately it's pointing to something thats no longer there. To keep an autorelease variable around call retain. Just to prove this is the problem calling alloc/init should fix it. so
replace
Fv := TNSNumber.Wrap(TNSNumber.OCClass.numberWithFloat(4.0));
with
Fv := TNSNumber.Wrap(TNSNumber.Alloc.initWithDouble(4.0));
and remove the retain and it all works.
from here https://stackoverflow.com/a/801000/416047 the rule is
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.
来源:https://stackoverflow.com/questions/36622910/delphi-xe6-arc-on-osx-releasing-variables