Edit AllPages

Late note on recent page edit: This seems largely to be a misunderstanding of how the autorelease pool works

In my document-based app, my document class has a single instance variable that points to a model object (that is itself composed of several instances). The model object is a subclass of General/NSObject; this model object and all of its instance variables conform to General/NSCoding to archive the data.

The document class creates an instance of this model class in init, and destroys it in dealloc. There is a setter for this variable in the document class, but I do not use it so far. (Logging a message in the setter is never reached when opening and closing documents.)

There is a window controller that obtains a reference to the model object in its awakeFromNib, but does not retain it.

I am testing the opening and closing of documents with log statements in the init and dealloc methods of the model class. When I open a default blank document, the alloc message is logged; when I close that window the dealloc message is logged. In both cases the id for the model object is the same. But here is the weirdness:

When I open a document from a file on disk, I see an additional dealloc log message, listing a pointer to an object that I never allocated in the init method of the model object. The usual init and dealloc log messages appear also, as with the default blank document.

I have no idea what this other object is. It seems to come from nowhere. But I believe this is related to an additional observation that the app leaks a dozen blocks or so when I open a document from archived data and does not leak when the default blank document is opened and closed, and this is what causes me the most immediate concern.

Warning: There is an array controller (Bindings) involved, but (I think) it only copies in the objects from this model array using its addObjects: method. Still, somehow I am inclined to blame this thing - bindings get blamed for pestilence and death, too.

I have done everything I can to search for anything that suggests a retain cycle or a circular reference, or whatever you call it, but I come up short. I cannot find anything that should invoke this dealloc message when opening a document from a file.

Here is what the logging looks like (note the double-dealloc when opening and closing a document from disk)

2005-02-13 11:52:33.149 App[1498] alloc <General/MyModel: 0x341f10>

2005-02-13 11:52:35.070 App[1498] dealloc <General/MyModel: 0x341f10>

2005-02-13 11:52:38.688 App[1498] alloc <General/MyModel: 0x3d9fd0>

2005-02-13 11:52:39.724 App[1498] dealloc <General/MyModel: 0x3f63d0>

2005-02-13 11:52:40.737 App[1498] dealloc <General/MyModel: 0x3d9fd0>

2005-02-13 11:53:01.812 App[1498] alloc <General/MyModel: 0x3649a0>

2005-02-13 11:53:02.541 App[1498] dealloc <General/MyModel: 0x3a7240>

2005-02-13 11:53:03.640 App[1498] dealloc <General/MyModel: 0x3649a0>

2005-02-13 11:53:47.977 App[1498] alloc <General/MyModel: 0x3b55f0>

2005-02-13 11:53:49.780 App[1498] dealloc <General/MyModel: 0x3b55f0>

2005-02-13 11:54:05.149 App[1498] alloc <General/MyModel: 0x392960>

2005-02-13 11:54:05.585 App[1498] dealloc <General/MyModel: 0x3ba2e0>

2005-02-13 11:54:06.674 App[1498] dealloc <General/MyModel: 0x392960>

I should preface this by saying I don’t really know what might be going on. Here’s something to look at though: when decoding archives, it’s possible for substitutions to occur after decoding the object graph. This could explain an extra object created and destroyed (maybe an instance of the General/NSNotifying subclass is substituted or something). As for the missing alloc, well, maybe the alloc method is not necessarily called when you decode an object from a nib. It’s possible that it’s done more directly.

Possibility: alloc just calls allocWithZone: with the default zone. Maybe allocWithZone: is being called instead [edit: directly, not instead, probably from unarchiveObjectWithData:, now that you’ve posted your code] here. Also, is there any real problem with this, or are you just curious? –General/JediKnil

Here are the relevant methods from the document, model, and window controller classes (This is largely boilerplate code - so far, the app just undoably adds and deletes records from its table view, loads and saves documents, or imports and exports data from and to text files. All of it is taken from published sample code.) I have changed the identifiers below to preserve generality, and might have integrated it all in some idiosyncratic way:

Document Class

Here are the relevant methods for the General/MyModel class referred to in the above code (again, nothing fancy); the style I use for my setter accessors is to release the current instance in first line and retain the argument passed in in the second.

Model Class

Note especially that the model class does not instantiate a mutable array, but a non-mutable copy, and only for archiving. The user is never directly modifying the model class. The array is copied in or out when archiving and unarchiving.

Window Controller Class

(By the way, if we can fix this, I believe it provides a nice template for a simple table app; all you need do is implement some indexed accessors for the instance array that belongs to the window controller, where in you forward invocations for the use of the undo manager and implement your Key Value Observing for appropriate key paths):

// The manual binding and unbinding of the array controller // are crucial to avoid a retain cycle associated with the binding when done through File’s Owner in the nib

myModel is an instance of the General/MyModel class, and is alloc’d in the init method of the document class. The initializer of the General/MyModel class is presented in the description above.

So the dealloc that I am seeing prior to the “real” one is courtesy of the autorelease pool. I notice on the “anomalous” one that at the time the dealloc happens, none of the instance variables point to anything. Makes sense now. Well - at least I feel confident that this business has nothing to do with my memory leaks.

I personally don’t have the kind of luck that allows my objects to survive a double-dealloc without causing a crash!

I’d recommend that you override -dealloc in your General/MyModel class if you haven’t already, and put a breakpoint on it. That way you can see exactly where it’s being called and what its contents are at the time.

Having that breakpoint in dealloc in the model class was how I first “discovered” that it was being called twice. I had not thought about logging from dealloc since I did my first experiments with autoreleased objects as a beginner. I am convinced that the first dealloc comes when the temporary object is taken out by the autorelease pool. At that point the document ivar points to nothing, but the rest of the model ivars contain the stuff from the General/NSUnarchiver. This is as it should be; my mistake. The second dealloc shows the document ivar points to the current document.

Here’s the loadDataRepresentation: ofType: method from the document class again:

In general, my setter accessors follow this pattern