Re: [Pyobjc-dev] Retain / Release semantics
Brought to you by:
ronaldoussoren
|
From: Ronald O. <ous...@ci...> - 2002-10-25 09:22:53
|
On Thursday, Oct 24, 2002, at 23:40 Europe/Amsterdam, Bill Bumgarner
wrote:
> I noticed that Web Services Tool stopped working recently. In
> delving into it, I had to comment out the -release here...
>
> if (res && ObjCObject_Check(res) && self->sel_allocator) {
> /* Ownership transfered to us, but 'execute' method has
> * increased retainCount, the retainCount is now one to high
> */
> NSLog(@"punting a release... %@", ObjCObject_GetObject(res));
> // [ObjCObject_GetObject(res) release];
> }
> return res;
>
[... Output that explains what code above does]
>
> ... which is exactly a 1 for 1 correspondance to calls to alloc() from
> within Python. I had been writing my code like...
>
> toolbarItem =
> NSToolbarItem.alloc().initWithItemIdentifier_(anIdentifier)
> toolbarItem.autorelease()
>
> ... but under the code implied by the -release above, I should not be
> calling autorelease(). Makes sense.
That was my idea: Conversion from Objective-C to Python implies
removing all calls to 'retain', 'release' and 'autorelease'.
>
> However, I'm not 100% sure that it is the correct pattern. I.e. do we
> really want to make the bridge responsible for keeping track of every
> single method that causes object allocation and do the -release as the
> current code does?
Yes, this makes the bridge as transparent as possible. Asking the
programmer to *sometimes* worry about reference counts is confusing. I
added the code above to PyObjC to make sure I never have to think about
reference counts when programming in Python.
Due to a 'feature' of NSOutlineView I still have to worry about this,
but that is a different story. In short: NSOutlineView doesn't 'retain'
the results of outlineView:child:ofItem:, but does hang on to this
value. This means you cannot use python objects as return values,
because their proxies are autoreleased and NSOutlineView tries to
access these proxies after they are released... IMNSHO this is a bug
and I'm thinking of fileing a bug report on this.
>
> I don't think we do because it introduces a situation where Obj-C
> written in Python behaves artificially different than Obj-C in Obj-C.
>
> Consider the following hunk of code under the conditions where the
> -release in the above code snippet doesn't happen:
>
> if aMenu:
> menuItem = NSMenuItem.alloc().init().autorelease()
> menuItem.setSubmenu_(aMenu)
> menuItem.setTitle_( aMenu.title() )
>
> The developer is explicitly indicating that menuItem should live for
> at least as long as the current thread's current autorelease pool.
>
> Now, consider the same code without the autorelease, but with the
> implied -release to balance the +alloc as the code from objc_selcall()
> currently exists:
>
> if aMenu:
> menuItem = NSMenuItem.alloc().init()
> menuItem.setSubmenu_(aMenu)
> menuItem.setTitle_( aMenu.title() )
>
> In this case, the NSMenuItem instance will only live as long as
> menuItem remains in scope. As soon as the scope of the if statement
> is torn down, menuItem will be -released and destroyed.
>
> This subtly, but significantly, changes the object lifespan patterns
> pervasive throughout the foundation. While this particular example is
> somewhat bogus, there are other situations where such a lifespan
> change could cause a failure. Example: the developer creates a class
> whose instances listen for a particular notification and remove
> themselves as observers when dealloc'd. A common idiom for creation
> might be MyChangeListener.alloc().init().autorelease(), but that isn't
> possible with the current implementation of the bridge.
'a common idiom for creation MIGHT be': Is this really a common idiom?
I'd rather not worry about theoretical changes. As far as I can judge
right now the current policy of fully automaticly managing reference
counts is far more usefull than manually updating them.
BTW. If you really want to use autorelease you could always do
'obj.retain();obj.autorelease()'. This is different from the
Objective-C way of doing this, but at least it is clear you're doing
something fishy. And this is pretty fishy: In most programs 'at least
as long as the current threads current autorelease pool' is 'at least
until the start of the next round throught the event loop', which is
probably a very short time.
BTW2. I really don't like 'invisible' objects like the MyChangeListener
example. How am I as a code maintainer/reviewer to know that this is
the intended behaviour and not an attempt to work around another bug
(at least this doesn't leak memory).
>
> As well, there is no way we can ever quantify all possible methods
> that a developer might use to produce a freshly allocated object that
> the current code would need to -release to preserve the pattern as it
> exists now.
Yes we can. Apple clearly documents in the Objective-C reference manual
that only a small number of methods should return objects that you
don't have to 'retain' (basicly alloc and copy). If you get an object
in any other way you should call 'retain' yourself. As much as a
dislike the idea of 'autorelease' it does solve the problem of object
ownership (why couldn't Objective-C use a real garbage collector and do
away with manual memory managment?).
If a developer writes classes that don't follow this convention that is
a bug, plain and simple. Such code is even a problem for plain
Objective-C users: Given the text in the Objective-C manual users will
expect they have to retain the result of method calls, a method that
does not follow this convention is confusing and is bound to introduce
memory leaks.
As should be obvious by now I am not convinced at all that the current
policy of PyObjC is wrong. I'd like to see a real-live example of why
the current policy is bad before I change my position.
Ronald
|