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 |