[Pyobjc-dev] Retain / Release semantics
Brought to you by:
ronaldoussoren
From: Bill B. <bb...@co...> - 2002-10-24 21:40:26
|
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; (at the end of objc_selcall()) In running the app, I see... 2002-10-24 17:03:20.535 Web Services Tool[16103] punting a release... <WSTConnectionWindowController: 0xbd5de0> 2002-10-24 17:03:20.922 Web Services Tool[16103] punting a release... <NSToolbar: 0xbb3940> 2002-10-24 17:03:20.974 Web Services Tool[16103] punting a release... <NSToolbarItem: 0xbd7db0> 2002-10-24 17:03:21.008 Web Services Tool[16103] punting a release... <NSToolbarItem: 0xc05f50> 2002-10-24 17:03:21.029 Web Services Tool[16103] punting a release... <NSToolbarItem: 0xc01480> 2002-10-24 17:03:21.050 Web Services Tool[16103] punting a release... <NSToolbarItem: 0xc08dd0> 2002-10-24 17:03:21.077 Web Services Tool[16103] punting a release... <NSToolbarItem: 0xc04fd0> ... 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. 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? 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. 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. The retain/release on Python assignment [or addition to a collection, whatever] is definitely the correct thing to do. But that is about the limit of what we can do automatically without opening ourselves up for some serious inconsistency in the long run. |