Re: [Pyobjc-dev] Reference Counting
Brought to you by:
ronaldoussoren
From: b.bum <bb...@ma...> - 2004-04-08 20:07:57
|
On Apr 8, 2004, at 11:59 AM, Michael Tsai wrote: >> I'm not sure why Cocoa behaves like this, but this might be the >> intended behaviour. The documentation for NSAutoreleasePool explictly >> mentions that pools behave like a stack, I suppose the implementation >> doesn't like it when you pop the second highest entry of that stack. > > I think popping the second highest element on the stack should be > fine. That can happen in Objective-C if control leaves a pool's > "scope" because of an exception. Popping the outer (lower on the > stack) pool should cause it to empty the inner (higher on the stack) > pool. Autorelease pools are implemented as a stack. In the context of pure Obj-C, there is no requirement that you have a top level stack. All [almost?] of the classes in the Foundation and AppKit have allocation and initialization paths that can be followed to instantiate an object without that object ever sitting in an autorelease pool. This is useful in threaded or tool environments where you don't want to use an autorelease pool, for whatever reason. One of the most common reasons to avoid an autorelease pool is for efficiency's sake. Creating 10,000 objects that are intended to stick around beyond the current release pool is much less expensive if you can avoid having those 10,000 objects stuck in the release pool. Now, in the context of PyObjC, the move to automatically maintaining retain counts on the Python side of the bridge incurred the cost of effectively requiring an autorelease pool be present per thread (at least with the current design/implementation). Removing retain/release/autorelease from the Python side of the bridge is such a boon to the developer that paying that cost is pretty minor in comparison. Besides, if you really need to optimize memory usage and performance to that point, you can always write the performance sensitive code in ObjC and call to it from Python. > So I think what's happening is that the [oldpool release] is popping > both pools, causing the NSArray to be put into the top-level pool > created by the objc module. The top-level pool is never emptied inside > the loop, so it *looks* like the NSArrays are being leaked. Releasing a pool only ever releases a single pool; it will never cause two pools to be released (because a pool can never be autoreleased). There is a bug in this code: > while (1) { > oldpool = pool; > pool = [[NSAutoreleasePool alloc] init]; > [oldpool release]; > > [NSArray array]; > } > > If you run this code you'll notice lots of messages on stderr: It's > complaining that an NSArray instance is leaked due to missing an > autorelease pool. The first pass through, pool is uninitialized.... so, [oldpool release] does nothing. The reason it spews a message over and over is because the loops are not nested correctly. Unrolling the loop yields the following: oldpool = pool; pool = [[NSAutoreleasePool alloc] init]; [oldpool release]; [NSArray array]; oldpool = pool; pool = [[NSAutoreleasePool alloc] init]; [oldpool release]; [NSArray array]; oldpool = pool; pool = [[NSAutoreleasePool alloc] init]; [oldpool release]; [NSArray array]; Like thread looking, pulls are a stack. You shouldn't pull a pool after pushing a new pool on top. That is why you see such loops written as: while(1) { NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; [NSArray array]; [p release]; } |