mikeash.com pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html commentshttp://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsmikeash.com Recent CommentsFri, 29 Mar 2024 01:37:20 GMTPyRSS2Gen-1.0.0http://blogs.law.harvard.edu/tech/rssPeter Carpenter - 2011-11-10 03:02:42http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsThanks very much for your response Mike. I hadn't been able to find any information on what the change was under the covers and knowing that they're no longer storing everything in a Dictionary makes me very happy!! We overrode the autorelease function on a few of our base class objects to put themselves directly on our autorelease pools and as you said found it to be slower than using IOS5 autorelease pools. Looks like the NSAutoreleasePools are definitely the way to go now.faf88bd668785b724c8208994e1ae573Thu, 10 Nov 2011 03:02:42 GMTmikeash - 2011-11-09 16:10:37http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsConceptually, as far as "autorelease pools are arrays of stuff that get released later", everything is the same. But the details have changed a great deal. In particular, there's not really an NSAutoreleasePool class anymore. The class still exists, but is now a thin wrapper around deeper runtime functionality. That functionality is, I think, directly accessed by the -autorelease method. In short, there's just enough left that you can still use NSAutoreleasePool objects in your code (when not using ARC), but you can't subclass or override things anymore and expect it to work. <br /> <br />I'd suggest ditching the custom class, at least on iOS 5. The new autorelease pool implementation is extremely fast, probably very difficult to improve upon, and I'd wager faster than what you have. <br /> <br />In particular, it uses a single array for the entire thread. Individual pools are just pointers into that array, and when popping a pool, that pointer is just used as a fence to know when to stop. Page-granular allocation and clever recycling means that no copying is needed when expanding the array (which doesn't need to be contiguous). For details, check out AutoreleasePoolPage and related code here: <a href="http://opensource.apple.com/source/objc4/objc4-493.9/runtime/objc-arr.mm">http://opensource.apple.com/source/objc4/objc4-493.9/runtime/objc-arr.mm</a>5b710860c548962744da5c705942e7afWed, 09 Nov 2011 16:10:37 GMTPeter Carpenter - 2011-11-08 23:31:59http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsYou state that nothing is changed with APC, however can you confirm if this is also true under ios5? We are subclassing NSAutoreleasePool and overriding the functions to use c arrays as the collection class. This has a MASSIVE improvement in performance, and worked well under ios4.2. With ios5 the overridden addObject is no longer being called so our 'enhanced' memory management is now broken. A change in the method signature perhaps?5a4b386a77ce267625292b0d6c1b65f2Tue, 08 Nov 2011 23:31:59 GMTmikeash - 2011-09-18 20:21:11http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsI shy away from the Leopard collection classes in most cases because of their general unavailability on iOS. It doesn't really matter in this case, of course, but it's a habit.c6fb3253c43139d96119dbd63a1e225fSun, 18 Sep 2011 20:21:11 GMTJeremy W. Sherman - 2011-09-18 19:23:13http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsWhat motivated the use of a <code>CFMutableArray</code> over an <code>NSPointerArray</code>?26e748071d0a7eb6b999adfe80bb5e15Sun, 18 Sep 2011 19:23:13 GMTmikeash - 2011-09-05 21:41:13http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#comments<b>Jason:</b> That's an interesting point. I think that, strictly speaking, ordering of releases is not guaranteed to have any relation to ordering of autoreleases, so this code is correct. Mostly I would expect people not to rely on the ordering, however across pool boundaries is probably a likely spot for people to do so, whether accidentally or on purpose. If one were doing it for real, it probably would be a good idea to make your change. <br /> <br /><b>Terrence:</b> Hey, at least you'll still know if they like to broaden their horizons and are capable of remembering stuff. Some advanced warning: if you liked to quiz people on how they'd implement reference counting, you may want to get that out of your system soon.b23fcbdd018570a6ad2ea07290287cc5Mon, 05 Sep 2011 21:41:13 GMTTerrence - 2011-09-05 18:17:54http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsWell... thanks Mike, my favourite interview question, out the window. <br /> <br />This has always been a great "I never actually thought about that" / "separate the men from the boys" problem, which requires even the most experienced devs to think a little. <br />9932611cfb8ab94c96a429b1d706cf1bMon, 05 Sep 2011 18:17:54 GMTJason - 2011-09-03 16:05:20http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsThis is great! In this implementation, I think it might be more appropriate to release inner pools before releasing the objects in _objects. <br /> <br />It would be legal for an object 'owned' by an inner pool to send a message (or retain via perform... side effects) to a parent object 'owned' by an outer pool (that it might only have a weak reference to). <br />19618a59a961c4f6fbd26e68e718cc33Sat, 03 Sep 2011 16:05:20 GMTmatthew - 2011-09-02 19:10:07http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsThanks Mike! I know most of that, but wasn't sure that the threadDictionary would be valid for threads not started by NSThread. This will be super handy to know when I need thread local storage in the future!05939bcddb50a32b1bb16e2ff4db83afFri, 02 Sep 2011 19:10:07 GMTmikeash - 2011-09-02 18:55:50http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsYes, NSThread's threadDictionary works fine on GCD queues. Remember that, from a plain multithreading standpoint, GCD queues aren't special in any way. They're just regular pthreads running regular code. Anything that works on a pthread works on a GCD queue, because you <i>are</i> on a pthread. The only trick is that you need to leave the thread as you found it, because code you don't own will run on it after you're done. <br /> <br />All pthreads are implicitly NSThreads as well. NSThread will create an instance on demand the first time you ask for one on a thread it doesn't know about.eb1f776606073557bdaf1a3b65fe3eafFri, 02 Sep 2011 18:55:50 GMTmatthew - 2011-09-02 18:46:51http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsDoes this work under GCD queues: [[NSThread currentThread] threadDictionary]? <br /> <br />I know it's not the point of your post, but if that won't work on GCD queues then the semantics of it are quite different from NSAutoreleasePool.17258c385c6eee53e5a0bf6fa74f81ffFri, 02 Sep 2011 18:46:51 GMTJens Ayton - 2011-09-02 18:37:23http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsLion details – not important for conceptual understanding, but fun: <br /> <br />In Lion, autorelease pools are implemented in the ObjC runtime (in objc-arr.mm). A single stack is maintained per thread, and manipulated through “pool tokens”. A pool token is simply a pointer to the top of an autorelease pool. (A NULL entry is kept a a boundary sentinel for verification.) <br /> <br />The <code>@autorelease</code> keyword generates calls to <code>objc_autoreleasePoolPush()</code> and <code>objc_autoreleasePoolPop()</code>, which use pool tokens directly. I presume an <code>NSAutoreleasePool</code> is now a simple wrapper for a pool token. <br /> <br />Another interesting optimization for ARC is runtime elision of autoreleases. An ARC function which semantically returns an autoreleased value can do so using <code>objc_autoreleaseReturnValue()</code>. If the calling function immediately calls <code>objc_retainAutoreleasedReturnValue(id obj)</code> – checked by looking for a specific instruction sequence – the object isn’t autoreleased, but instead stashed in a per-thread global, which causes <code>objc_retainAutoreleasedReturnValue(id obj)</code> to not retain.403e7a99cba2d09e3214cf4614c424caFri, 02 Sep 2011 18:37:23 GMTUli Kusterer - 2011-09-02 16:49:13http://www.mikeash.com/?page=pyblog/friday-qa-2011-09-02-lets-build-nsautoreleasepool.html#commentsThank you for this great explanation. As a programmer trying to understand Cocoa, this explanation is really all you need to know. It is how NSAutoreleasePool conceptually works. <br /> <br />Of course, in reality, Apple has optimized the hell out of this class. AFAIR, it doesn't really create a new instance of the array. There is one shared CFMutableArrayRef per thread, and it just puts a marker on that when a new autorelease pool starts, and when you release a pool, releases down to that marker. <br /> <br />I think it even plays tricks to avoid creating a new object for every pool, but I'm not sure anymore. After all, when you release an outer pool, all the inner pools get released as well, and it couldn't do that by returning the same singleton per thread from alloc and init each time.0f296fc82bafa584607074e17763a3c0Fri, 02 Sep 2011 16:49:13 GMT