From: Benjamin J. <bhj...@gm...> - 2008-07-03 15:25:52
|
An update for anyone following along: The dropped notifications have re-appeared, though with less frequency. I'm not sure if this lambda thing was a real or imagined problem. Perhaps something else is causing it independently. On Wed, Jul 2, 2008 at 8:35 PM, Benjamin Jackson <bhj...@gm...> wrote: > Hi all, > > I wanted to share my experience and see if anyone else has had the > same issues as we have had with Blogo and memory management. > > In short: we make liberal use of Cocoa's asynchronous classes for IO, > almost all of it using NSURLConnection. In all cases we hook our > controllers into methods which accept a proc as a callback and call it > when their delegate methods or notification listeners get called. > > Anyone who has followed Blogo's launch knows we've had more than our > share of crashing bugs. Most of these incidents amounted to us > receiving crash reports with segfaults and cryptic libruby- and > AppKit-related stack traces. Some of them pointed us in the direction > of the asynchronous callbacks, which led us to examine why GC was > ripping up our objects before they were called. > > This in turn led us to the realization that Ruby GC really doesn't > give a damn about Objective C's reference counter, which led to the > realization that we couldn't just throw an object into a local > variable and expect it to hang around waiting for the delegate methods > to fire. Changing to use NSNotifications solved the crashes but was > the equivalent of putting a band-aid on a broken leg: now we had to > run timers to check for dropped notifications when GC acts up. Clearly > not ideal. > > So there we thought we had it worked out. Until the crashes started > reappearing in other areas of the app which used no local variables. > > While researching ruby GC today I had an idea. What if the objects are > still there, but the procs being used for callbacks were getting > swallowed by GC? > > Normally we call our helper classes as such: > > @helper = MyAwesomeAsyncHelper.alloc.init > @helper.getSomething do |result| > # do something interesting with the result... > end > > MyAwesomeAsyncHelper then stores the lambda in an instance var to be > called later: > > class MyAwesomeAsyncHelper < OSX::NSObject > def getSomething &callback > @myCallback = callback > # do something asynchronous... > end > > def connectionDidFinishLoading > @myCallback.call(@resultData) > end > end > > By our logic, this should be sound. The reference is there. It's > assigned to an instance var. > > For kicks I tried changing the helper classes to use Procs instead of lambdas: > > @helper = MyAwesomeAsyncHelper.alloc.init > @helperCallback = Proc.new { |result| # do something interesting with > the result... } > @helper.getSomething(@helperCallback) > > And the helper (removed the & from the args list): > > class MyAwesomeAsyncHelper < OSX::NSObject > def getSomething callback > @myCallback = callback > # do something asynchronous... > end > > def connectionDidFinishLoading > @myCallback.call(@resultData) > end > end > > Voila. No more dropped notifications. > > So here's what I think is happening: > > 1. Client sends lambda to helper. > 2. Helper calls to_proc on lambda > 3. Helper stores reference to the new Proc > 4. Lambda has no references to it and is swept at the first opportunity > > Can anyone confirm or deny that this is the case? > |