Re: [Pyobjc-dev] Memory problems with PyObjC and outlets in nibs

On Sep 16, 2006, at 10:44 PM, Josh Marshall wrote:
> On 15/09/2006, at 6:12 PM, Ronald Oussoren wrote:
>> On Friday, September 15, 2006, at 08:20AM, Josh Marshall
>> <josh.p.marshall@...> wrote:
>>>
>>> Setting these, I immediately found the source of the error. I have
>>> AIDocument and AIWindowController subclasses (their superclasses
>>> should be obvious) in Python, also defined in my Document nib. The
>>> nib has an NSImageView in the window and the AIWindowController
>>> has a
>>> "plotView" outlet connected to it.
>>>
>>> Now when I close the window, the NSImageView gets released once too
>>> often. I'm guessing that once is when the File's Owner (in this case
>>> AIWindowController) is dealloced, and the other when the nib
>>> structure itself is dealloced, but this is conjecture.
>>>
>>> This same problem has occurred to me elsewhere. It seems that if an
>>> object is assigned to an outlet in a nib, it seems to need a retain
>>> called on the outlet after the nib has finished loading. I couldn't
>>> find anywhere that this was documented, and so my question is if
>>> this
>>> is expected behaviour or a bug?
>>
>> Not really. I usually use a combination of pdb and (mostly) gdb to
>> hunt down issues like this one.
>>
>> Could you create a small project that demonstrates the problem?
>> From your description I'd say this is a bug in PyObjC, but not one
>> I've run into before.
>>
>> Ronald
>
> Of course, I figure it out on my own (I think) a few hours after
> emailing the list, after running into the problem for weeks.
>
> I had an outlet defined in the nib, and *also* in my Python source
> for the class. If I remove the source-defined one, I no longer get
> the additional release sent to the object and subsequent crash. My
> assumption was that the outlets also needed to be defined in the
> source, but this appears to be wrong.
>
> However, I still think maybe PyObjC can take some of the blame (yeah,
> yeah ;). If I define an outlet in the nib and also in the source,
> shouldn't these be assumed to be the same outlet and therefore only
> processed once?
That sounds like a bug to me if you are using
NibClassBuilder.AutoBaseClass, you happen to be the first that ran
into it (or at least the first that reported the issue). If you
aren't using AutoBaseClass PyObjC won't look at the class definition
in the NIB file and you will have to define the outlets yourself :-)
How did you define the outlet in your source code? You must use
objc.IBOutlet to define the outlet in python code, otherwise PyObjC
won't do the reference counting correctly. I should have rembered
this earlier, this is yet another obscure corner where Cocoa plays
loose with reference counting and PyObjC has to jump through hoops to
get the right semantics.
I'm working on a patch that warns about this issue, and adds some
unittests for AutoBaseClass.
Ronald

Thread view

For a while now, I've been perplexed with strange memory crashes
involving several PyObjC applications. They were occurring when
windows were closed and it seemed that objects were getting released
multiple times.
I couldn't find any resources on debugging these crashes, and turned
to see if there was anything similar on the Objective C side of
things. I found this: http://www.cocoadev.com/index.pl?
DebuggingAutorelease which explains the NSDebugEnabled,
NSZombieEnabled, MallocStackLogging environment variables.
Setting these, I immediately found the source of the error. I have
AIDocument and AIWindowController subclasses (their superclasses
should be obvious) in Python, also defined in my Document nib. The
nib has an NSImageView in the window and the AIWindowController has a
"plotView" outlet connected to it.
Now when I close the window, the NSImageView gets released once too
often. I'm guessing that once is when the File's Owner (in this case
AIWindowController) is dealloced, and the other when the nib
structure itself is dealloced, but this is conjecture.
This same problem has occurred to me elsewhere. It seems that if an
object is assigned to an outlet in a nib, it seems to need a retain
called on the outlet after the nib has finished loading. I couldn't
find anywhere that this was documented, and so my question is if this
is expected behaviour or a bug?
My other question is whether there is any way to get a C stack trace
from within the Python code? I eventually tracked this bug down by
overriding retain and release on my Python ObjC objects and
displaying the retain count, but it would be very handy to know where
my Python-defined methods are being called from. I tried running gdb
during a pdb.set_trace(), but this just caused mayhem. Are there any
good resources on debugging PyObjC apps?
Thanks,
Josh

On Friday, September 15, 2006, at 08:20AM, Josh Marshall <josh.p.marshall@...> wrote:
>For a while now, I've been perplexed with strange memory crashes
>involving several PyObjC applications. They were occurring when
>windows were closed and it seemed that objects were getting released
>multiple times.
>
>I couldn't find any resources on debugging these crashes, and turned
>to see if there was anything similar on the Objective C side of
>things. I found this: http://www.cocoadev.com/index.pl?
>DebuggingAutorelease which explains the NSDebugEnabled,
>NSZombieEnabled, MallocStackLogging environment variables.
>
>Setting these, I immediately found the source of the error. I have
>AIDocument and AIWindowController subclasses (their superclasses
>should be obvious) in Python, also defined in my Document nib. The
>nib has an NSImageView in the window and the AIWindowController has a
>"plotView" outlet connected to it.
>
>Now when I close the window, the NSImageView gets released once too
>often. I'm guessing that once is when the File's Owner (in this case
>AIWindowController) is dealloced, and the other when the nib
>structure itself is dealloced, but this is conjecture.
>
>This same problem has occurred to me elsewhere. It seems that if an
>object is assigned to an outlet in a nib, it seems to need a retain
>called on the outlet after the nib has finished loading. I couldn't
>find anywhere that this was documented, and so my question is if this
>is expected behaviour or a bug?
>
>My other question is whether there is any way to get a C stack trace
>from within the Python code? I eventually tracked this bug down by
>overriding retain and release on my Python ObjC objects and
>displaying the retain count, but it would be very handy to know where
>my Python-defined methods are being called from. I tried running gdb
>during a pdb.set_trace(), but this just caused mayhem. Are there any
>good resources on debugging PyObjC apps?
Not really. I usually use a combination of pdb and (mostly) gdb to hunt down issues like this one.
Could you create a small project that demonstrates the problem? From your description I'd say this is a bug in PyObjC, but not one I've run into before.
Ronald

On 15/09/2006, at 6:12 PM, Ronald Oussoren wrote:
> On Friday, September 15, 2006, at 08:20AM, Josh Marshall
> <josh.p.marshall@...> wrote:
>>
>> Setting these, I immediately found the source of the error. I have
>> AIDocument and AIWindowController subclasses (their superclasses
>> should be obvious) in Python, also defined in my Document nib. The
>> nib has an NSImageView in the window and the AIWindowController has a
>> "plotView" outlet connected to it.
>>
>> Now when I close the window, the NSImageView gets released once too
>> often. I'm guessing that once is when the File's Owner (in this case
>> AIWindowController) is dealloced, and the other when the nib
>> structure itself is dealloced, but this is conjecture.
>>
>> This same problem has occurred to me elsewhere. It seems that if an
>> object is assigned to an outlet in a nib, it seems to need a retain
>> called on the outlet after the nib has finished loading. I couldn't
>> find anywhere that this was documented, and so my question is if this
>> is expected behaviour or a bug?
>
> Not really. I usually use a combination of pdb and (mostly) gdb to
> hunt down issues like this one.
>
> Could you create a small project that demonstrates the problem?
> From your description I'd say this is a bug in PyObjC, but not one
> I've run into before.
>
> Ronald
Of course, I figure it out on my own (I think) a few hours after
emailing the list, after running into the problem for weeks.
I had an outlet defined in the nib, and *also* in my Python source
for the class. If I remove the source-defined one, I no longer get
the additional release sent to the object and subsequent crash. My
assumption was that the outlets also needed to be defined in the
source, but this appears to be wrong.
However, I still think maybe PyObjC can take some of the blame (yeah,
yeah ;). If I define an outlet in the nib and also in the source,
shouldn't these be assumed to be the same outlet and therefore only
processed once?
Regards,
Josh

On Sep 16, 2006, at 10:44 PM, Josh Marshall wrote:
> On 15/09/2006, at 6:12 PM, Ronald Oussoren wrote:
>> On Friday, September 15, 2006, at 08:20AM, Josh Marshall
>> <josh.p.marshall@...> wrote:
>>>
>>> Setting these, I immediately found the source of the error. I have
>>> AIDocument and AIWindowController subclasses (their superclasses
>>> should be obvious) in Python, also defined in my Document nib. The
>>> nib has an NSImageView in the window and the AIWindowController
>>> has a
>>> "plotView" outlet connected to it.
>>>
>>> Now when I close the window, the NSImageView gets released once too
>>> often. I'm guessing that once is when the File's Owner (in this case
>>> AIWindowController) is dealloced, and the other when the nib
>>> structure itself is dealloced, but this is conjecture.
>>>
>>> This same problem has occurred to me elsewhere. It seems that if an
>>> object is assigned to an outlet in a nib, it seems to need a retain
>>> called on the outlet after the nib has finished loading. I couldn't
>>> find anywhere that this was documented, and so my question is if
>>> this
>>> is expected behaviour or a bug?
>>
>> Not really. I usually use a combination of pdb and (mostly) gdb to
>> hunt down issues like this one.
>>
>> Could you create a small project that demonstrates the problem?
>> From your description I'd say this is a bug in PyObjC, but not one
>> I've run into before.
>>
>> Ronald
>
> Of course, I figure it out on my own (I think) a few hours after
> emailing the list, after running into the problem for weeks.
>
> I had an outlet defined in the nib, and *also* in my Python source
> for the class. If I remove the source-defined one, I no longer get
> the additional release sent to the object and subsequent crash. My
> assumption was that the outlets also needed to be defined in the
> source, but this appears to be wrong.
>
> However, I still think maybe PyObjC can take some of the blame (yeah,
> yeah ;). If I define an outlet in the nib and also in the source,
> shouldn't these be assumed to be the same outlet and therefore only
> processed once?
That sounds like a bug to me if you are using
NibClassBuilder.AutoBaseClass, you happen to be the first that ran
into it (or at least the first that reported the issue). If you
aren't using AutoBaseClass PyObjC won't look at the class definition
in the NIB file and you will have to define the outlets yourself :-)
How did you define the outlet in your source code? You must use
objc.IBOutlet to define the outlet in python code, otherwise PyObjC
won't do the reference counting correctly. I should have rembered
this earlier, this is yet another obscure corner where Cocoa plays
loose with reference counting and PyObjC has to jump through hoops to
get the right semantics.
I'm working on a patch that warns about this issue, and adds some
unittests for AutoBaseClass.
Ronald

On 17/09/2006, at 7:17 PM, Ronald Oussoren wrote:
>> I had an outlet defined in the nib, and *also* in my Python source
>> for the class. If I remove the source-defined one, I no longer get
>> the additional release sent to the object and subsequent crash. My
>> assumption was that the outlets also needed to be defined in the
>> source, but this appears to be wrong.
>>
>> However, I still think maybe PyObjC can take some of the blame (yeah,
>> yeah ;). If I define an outlet in the nib and also in the source,
>> shouldn't these be assumed to be the same outlet and therefore only
>> processed once?
>
> That sounds like a bug to me if you are using
> NibClassBuilder.AutoBaseClass, you happen to be the first that ran
> into it (or at least the first that reported the issue). If you
> aren't using AutoBaseClass PyObjC won't look at the class
> definition in the NIB file and you will have to define the outlets
> yourself :-)
>
> How did you define the outlet in your source code? You must use
> objc.IBOutlet to define the outlet in python code, otherwise PyObjC
> won't do the reference counting correctly. I should have rembered
> this earlier, this is yet another obscure corner where Cocoa plays
> loose with reference counting and PyObjC has to jump through hoops
> to get the right semantics.
>
> I'm working on a patch that warns about this issue, and adds some
> unittests for AutoBaseClass.
>
> Ronald
So that's the problem. I was defining my outlets using objc.ivar,
rather than objc.IBOutlet. There wasn't any documentation on using
IBOutlet, although I did find a few references to it by grepping
through the examples.
So I'd presume that defining an IBOutlet in my Python class has the
same net result (apart from as documentation) as not defining it at
all, assuming it is defined in the corresponding nib?
Thanks for your invaluable help Ronald.
Regards,
Josh

On Sep 18, 2006, at 1:18 AM, Josh Marshall wrote:
> On 17/09/2006, at 7:17 PM, Ronald Oussoren wrote:
>
>>> I had an outlet defined in the nib, and *also* in my Python source
>>> for the class. If I remove the source-defined one, I no longer get
>>> the additional release sent to the object and subsequent crash. My
>>> assumption was that the outlets also needed to be defined in the
>>> source, but this appears to be wrong.
>>>
>>> However, I still think maybe PyObjC can take some of the blame
>>> (yeah,
>>> yeah ;). If I define an outlet in the nib and also in the source,
>>> shouldn't these be assumed to be the same outlet and therefore only
>>> processed once?
>>
>> That sounds like a bug to me if you are using
>> NibClassBuilder.AutoBaseClass, you happen to be the first that ran
>> into it (or at least the first that reported the issue). If you
>> aren't using AutoBaseClass PyObjC won't look at the class
>> definition in the NIB file and you will have to define the outlets
>> yourself :-)
>>
>> How did you define the outlet in your source code? You must use
>> objc.IBOutlet to define the outlet in python code, otherwise PyObjC
>> won't do the reference counting correctly. I should have rembered
>> this earlier, this is yet another obscure corner where Cocoa plays
>> loose with reference counting and PyObjC has to jump through hoops
>> to get the right semantics.
>>
>> I'm working on a patch that warns about this issue, and adds some
>> unittests for AutoBaseClass.
>>
>> Ronald
>
> So that's the problem. I was defining my outlets using objc.ivar,
> rather than objc.IBOutlet. There wasn't any documentation on using
> IBOutlet, although I did find a few references to it by grepping
> through the examples.
More work :-(. I'll add some documentation for IBOutlet as well. An
IBOutlet is an objc.ivar with an additional flag set that tells
PyObjC that the instance variable is used as an outlet, which
slightly changes the reference count rules.
>
> So I'd presume that defining an IBOutlet in my Python class has the
> same net result (apart from as documentation) as not defining it at
> all, assuming it is defined in the corresponding nib?
That's right, but only if you use NibClassBuilder.AutoBaseClass.
AutoBaseClass is a "magic" superclass, when you inherit from it
PyObjC will look for the actual superclass and defined outlets in the
NIB-files that you have loaded using NibClassBuilder.extractClasses
and merge those into the Python class definition.
If you do not use AutoBaseClass you must define the outlets yourself
using objc.IBOutlet.
Ronald