Re: [Pyobjc-dev] Memory management bug using .new() -- and another one?
Brought to you by:
ronaldoussoren
From: Ronald O. <ron...@ma...> - 2009-06-20 16:40:17
|
Dirk, Do you have an 'outlet = objc.IBOutlet()' in your class definition? If so, could you test if the issue goes away if you do the following: class MyClass (NSObject): outlet = objc.IBOutlet() outlet = objc.ivar() That is, duplicate all IBOutlet definitions with an objc.ivar definition in the same class definition below the IBOutlet definitions. Objc.IBOutlet definitions are treated rather specially by PyObjC and I fairly recently read something about outlet handling on iPhone as compared to regular MacOSX that made me wonder if the way PyObjC handles outlets is correct. If the above hack fixes the memory leak you're seeing I'm going to write a unittest that displays the same behaviour and fix the issue (that last bit should be easy and would remove some code, which is always a good thing). Ronald On 19 Jun, 2009, at 13:53, Dirk Stoop wrote: > For anyone else who might be running into this top-level objects > thing, here's a workaround. > > I don't recommend anyone to put this to use in any production code, > but at least it's right now allowing me to go hunting for leaks > relatively reliably before this bug is fixed, or someone convinces > me it's not a bug. ;) > > ---------- > > # my 'global' to switch this all off when the issue's been fixed > > CH_WORKAROUND_NON_NATIVE_INIT_TLOBJECTS = True > > ---------- > > # in my viewController, identically also in my windowController > > class MyViewController(NSViewController): > def dealloc(self): > if not hasattr(self, > '_chWindowControllerRemoveObservationsCalled'): > raise RuntimeError, 'Call to super(%s, > self).removeObservations() is MISSING in %s\'s removeObservations > implementation' % (self.className(), self.className()) > > if CH_WORKAROUND_NON_NATIVE_INIT_TLOBJECTS: > topLevelObjects = objc.getInstanceVariable(self, > '_topLevelObjects') > objectsToRelease = CHNonNativeInitializedObjectsInList_ > (topLevelObjects) > if len(objectsToRelease): > NSLog(u'\tThe following objects with non-native init > methods will be released:') > for o in objectsToRelease: > NSLog(u'\t\t%s' % o) > o.release() > > super(MyViewController, self).dealloc() > > ---------- > > # the method that filters the set of objects > > def CHNonNativeInitializedObjectsInList_(listOfObjects): > """ > Iterates through the supplied listOfObjects, and returns the > subset of those > that have an 'init*' method that's implemented in Python. > """ > nonNativeInitObjects = [] > > for someObject in listOfObjects: > hasNativeInit = True > allAttributes = (a for a in dir(someObject) if a.startswith > ('init')) > for attributeName in allAttributes: > exec("method = someObject.%s" % attributeName) > if hasattr(method, 'callable'): > hasNativeInit = False > break > if not hasNativeInit: > nonNativeInitObjects.append(someObject) > > return nonNativeInitObjects > > ---------- > > Once more, this is prone to breakage, > 1. since we're accessing the private ivar '_topLevelObjects', which > might be renamed at any time, although I doubt it will; hard to come > up with a better name for that needed private ivar.. > 2. And of course, checking the entire dir(someObject) to look for > any attribute that starts with 'init' is pretty ridiculous too. > > But at least, I'm able to continue my hunt for memory leaks now, > plus I have a boolean that I can throw out the window when this > isn't needed anymore. > > Cheers, > - Dirk > > > > On Jun 19, 2009, at 12:06 PM, Dirk Stoop wrote: > >> Hi Ronald, >> >> Thanks for the clarification. :) >> >> I found another weird memory management quirk that might be related. >> >> Summary: >> Objects that implement the designated initializer are not >> automatically released as top-level objects in nibs owned by view- >> and >> windowcontrollers. >> >> Example: >> I have the following two classes: >> >> ---------------- >> class SFTestViewVanilla(NSView): >> >> def dealloc(self): >> NSLog(u'yay, dealloc in %s' % self) >> super(SFTestViewVanilla, self).dealloc() >> >> class SFTestViewWithInit(NSView): >> >> def initWithFrame_(self, frame): >> NSLog(u'initWithFrame_ called in %s' % self) >> self = super(SFTestViewWithInit, self).initWithFrame_(frame) >> if self: >> pass >> return self >> >> def dealloc(self): >> NSLog(u'yay, dealloc in %s' % self) >> super(SFTestViewWithInit, self).dealloc() >> ---------------- >> >> Instances of each of these have been put as top-level objects in a >> nib >> file that's owned by an NSViewController subclass. Actually, to be >> entire correct, I dragged in 'custom views' and set the class of one >> of them to SFTestViewVanilla, and another one to SFTestViewWithInit. >> So those custom view placeholders are replaced with my custom >> subclasses when the nib is loaded, causing initWithFrame: to be >> called >> on both of them. >> >> When the CHViewController is released, the SFTestViewVanilla instance >> is deallocced and produces a log entry, the SFTestViewWithInit >> instance however, is not being deallocced. The only difference >> between both is the implementation of them pasted above. >> >> More Testing: >> I've also tried not assigning to self in SFTestViewWithInit's >> initWithFrame: implementation but that makes no difference whatsover. >> >> As a next step of figuring stuff out: If I set up outlets to each, >> and >> check their retainCount in awakeFromNib, they both start out with a >> retainCount of 1. >> >> Initially I suspected that there's something weird going on with >> NSViewController's memory management of its top-level objects, if >> those objects are implemented in Python. This weirdness however only >> happens with objects that implement the designated initializer. Next >> I've added these same views as top-level objects in a different nib >> that's owned by an NSWindowController subclass, where they exhibit >> the >> exact same behavior. In both cases there are no outlets to the views >> (just added those at some point to look at retainCounts for >> additional >> clues) and the views aren't part of any view hierarchy. >> >> The same seems to happen with other objects that have their own init, >> like my NSArrayController subclass and other things that don't >> inherit >> from NSView. (I have not isolated that behavior yet, but from my >> app's >> code I suspect this is going on) >> >> Right now, I'm special casing every viewController's and >> windowController's dealloc to manually release these views, but I >> completely fail to understand why something is going haywire with the >> 'automatic' releasing of top-level objects that NSViewController and >> NSWindowController are supposed to do.. This seems to be a bug, that >> I'd rather fix in one spot than work around everywhere. >> >> I'd love to look into this and fix it myself, write a regression >> test, >> and submit a patch. Could you help me out with some pointers on >> where >> to start looking in the PyObjC source? >> >> Thanks! >> - Dirk >> >> PS: I'm using pyobjc_core-2.2b2-py2.5, installed from a >> downloaded .tar from pypi.python.org, because installing from trunk >> didn't work out on my machine (10.5.7) >> >> On Jun 19, 2009, at 10:47 AM, Ronald Oussoren wrote: >> >>> >>> On 18 Jun, 2009, at 22:47, Orestis Markou wrote: >>> >>>> Hi Ronald, >>>> >>>> does that stand for other arbitrary class methods, like >>>> CALayer.layer >>>> () as well? Is there a way to inform PyObjC of constructor class >>>> methods? Or should we change our code to use alloc().init() >>>> instead? >>> >>> No. The only class methods that behave like alloc are alloc itself >>> and anything that starts with 'new'. Regular class methods return >>> autoreleased objects. >>> >>> The behaviour of method names that start with 'new' is newish, the >>> last time I check (which was a couple of years ago), only +alloc >>> returned a retained value. >>> >>> Ronald >>> >>> >>> ------------------------------------------------------------------------------ >>> Crystal Reports - New Free Runtime and 30 Day Trial >>> Check out the new simplified licensing option that enables unlimited >>> royalty-free distribution of the report engine for externally facing >>> server and web deployment. >>> http://p.sf.net/sfu/businessobjects >>> _______________________________________________ >>> Pyobjc-dev mailing list >>> Pyo...@li... >>> https://lists.sourceforge.net/lists/listinfo/pyobjc-dev >> >> >> ------------------------------------------------------------------------------ >> Crystal Reports - New Free Runtime and 30 Day Trial >> Check out the new simplified licensing option that enables unlimited >> royalty-free distribution of the report engine for externally facing >> server and web deployment. >> http://p.sf.net/sfu/businessobjects >> _______________________________________________ >> Pyobjc-dev mailing list >> Pyo...@li... >> https://lists.sourceforge.net/lists/listinfo/pyobjc-dev > |