I have a scenario where the initialiser to use called after alloc
ing an object is not known until runtime and I have no control over it. It also may have various arguments. So currently I'm doing this:
...
id obj = [MyClass alloc];
return [self invokeSelectorOn:obj];
}
-(id) invokeSelectorOn:(id) obj {
SEL initSelector = ...;
NSMethodSignature *sig = [[MyClass class] instanceMethodSignatureForSelector:initSelector];
NSinvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
inv.selector = initSelector;
[inv retainArguments];
// ... setting arguments ...
[inv invokeWithTarget:obj];
id returnValue;
[inv getReturnValue:&returnValue];
return returnValue;
}
The problem (I think !) I have is that because initSelector
is being called by an NSInvocation, it's not returning a retain+1 object. So the result of the above is a crash when the auto release pool attempts to dealloc the object.
I've tried adding a CFBridgingRetain(...)
which fixes the memory issue, however I'm not sure this is the correct solution and the static analyser tags it as a memory leak.
So my question is how can I can I call an initialiser via a NSInvocation and get back a correctly retain+1 object?
getReturnValue:
simply copies the return value into the location pointed to by your pointer as plain old dumb binary data. It doesn't care what the type is, it just copies it binarywise, and does nothing else with it like memory management if it's a managed object type.
Therefore, passing it a id __strong *
is not appropriate, since that type requires that when something is assigned to the thing pointed to, the previous value is released and the new value is retained (getReturnValue:
doesn't do that.)
Passing it a id __unsafe_unretained *
is appropriate, since that type exactly matches the behavior where assigning something to the thing pointed to doesn't do any memory management. That's why declaring returnValue
to be id __unsafe_unretained
(or MyClass * __unsafe_unretained
) and then passing &returnValue
works.
After you fix this, what you have is fine for calling normal methods. But in this case you are calling initialisers, and initialisers have different memory management rules than normal methods. Initializers consume a reference count on the reference they are called on, and return a retained reference. If the initializer returns the object it was called on (which is what most initializers do), then those cancel out and it works just like normal methods. However, initializers are also allowed to
nil
, in which case it would release the object it was called on, and return nil
. So more complex handling is needed for it to work with initializers in general. I will not go into that. If you know your initialisers always return the object it was called on, then you don't need to worry about this.