Re: NSUndoManager retain/release of arguments - ad infinitum
Re: NSUndoManager retain/release of arguments - ad infinitum
- Subject: Re: NSUndoManager retain/release of arguments - ad infinitum
- From: Graham Cox <email@hidden>
- Date: Thu, 13 Jan 2011 22:32:26 +1100
On 13/01/2011, at 7:52 PM, John Bartleson wrote:
> The real point of my original plaintive cry, and I'm sorry if it's become obfuscated, is that I
> had to allocate memory as part of setting up for undo, but I couldn't find any *simple* way to
> keep track of whether that memory is still in use or not, thus threatening a memory leak.
You are not required to keep track of it. You allocate it, pass it on, then release it. It's no longer of any interest to you, you're done with it. Standard MM rules.
> This
> is not a sorting problem - it appears that it will occur generically whenever we write reference-
> counted code that allocates memory and then passes that memory, usually via -prepareWithInvocationTarget:,
> to an undo method.
Let's make something clear.
-prepareWithInvocationTarget: takes just one argument - the target to call when undo or redo is invoked. This method then returns an object - in fact a NSProxy in the current implementation, but that's not especially important to this discussion.
While you typically write something like this:
[[undoManager prepareWithInvocationTarget:self] performSortWithDescriptors:descriptors];
what you're really doing is this:
id proxy = [undoManager prepareWithInvocationTarget:self];
[proxy performSortWithDescriptors:descriptors];
So at no point are you passing objects such as arrays or sort descriptors to -prepareWithInvocationTarget:, you are passing the TARGET. By and large this will be 'self', the object that is setting up the undo. It is not retained.
On the returned proxy, you call a method, which is a method you know to exist in the target (self). The proxy is set up so that, because it knows about the target already, it can 'fake' the existence of that method within itself. When you call that method, it freezes it and all its arguments into an NSInvocation which is stored by the NSUndoManager. The arguments are retained.
> This gave me a glimmer of hope to explain what's going on. If in my method I allocate an
> object (see the Part 1 code in my 2nd post), save a pointer to the object in the local variable
> 'unsortedArray', pass the pointer to -prepareWithInvocationTarget:, then exit the method, my pointer
> is gone. If I haven't kept a copy of the pointer (say in an NSDocument instance variable)
> -prepareWithInvocationTarget: (or the instance of NSUndoManager or whatever) is now sole
> owner of the pointer.
No, you don't pass the pointer to -prepareWithInvocationTarget:, see above. You pass the pointer as an argument to a method of the target which is actually invoked on the NSProxy.
This isn't as complicated as it sounds. But the distinction is important, because the proxy converts the method call to an invocation, and the invocation retains the arguments. So yes, that means that the undo manager does take ownership of the arguments, because it owns the invocations.
By the way, while none of this is especially transparent in NSUndoManager, you can follow the code in my GCUndoManager, which uses different structures but works similarly.
> Recall that, after I fixed my @property, my code appears to work properly. Ignoring the fact that
> I don't know any way to verify that the object is actually being deallocated at NSDocument dealloc time,
> I conclude that NSUndoManager and -prepareWithInvocationTarget: must be designed to retain and
> release objects passed to them as needed to provide undo functionality. As Graham says: "it
> follows the rules for ownership of objects, so by and large does the 'right thing'."
You are not required to verify it. (Though you may do so using the memory tracing tools). You only need to follow the rules, and understand that the undo manager also follows the rules. When everyone follows the rules, the need to verify and check ownership disappears - you can take it as read that correct memory management is performed. In other words you only need to concentrate on ensuring YOUR code follows the rules - the frameworks can be assumed to.
> There's appears to be only one problem remaining: this behavior is not documented anywhere I can
> find. In fact, Apple's 'Undo Architecture' implies just the opposite: "An NSUndoManager object does
> not retain the targets of undo operations."
That's because you are confusing the target of the undo operation (not retained) with the arguments to the undo invocation (retained).
The documentation is not required to state that any particular class follows the MM rules - all classes can be assumed to. Only exceptions to the standard rules need to be documented.
--Graham
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden