getArgument of NSInvocation of current method always returns null - objective-c

I want to get the name of the arguments of the current function I am in so that I can prepare loading that object from the filesystem if it's not present on the current instance. (for instance if [foo dictTest] is not available I want to load it's prior saved plist version into exactly that ivar)
I want to find the file by providing the ivar name that I provided as an argument to the current function.
This is the function code:
-(NSDictionary*)getCachedDictionary:(NSDictionary*)dict{
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:_cmd]];
NSString * firstArgument = nil;
[invocation getArgument:&firstArgument atIndex:2];
NSLog(#"Trying to get the objects ivar %#",firstArgument);
// right now during testing return nil
return nil;
}
As soon as the code reaches the NSLog I am getting a null value from firstArgument.
Why is that? Could it be possible that I would have to wait for the complete invocation of that current method I am in or is it actually better to create a proxy function that implicitly calls my class method via an invocation that eats the ivar name provided by setArgument so that I can use that argument string like I want?
Thanks a lot in advance!
P.S.: In this particular example I do not want to use KVC to identify the ivar and return it.

You've misunderstood the NSInvocation API. +[NSInvocation invocationWithMethodSignature:] creates a new NSInvocation that is keyed to accept arguments of the types defined by the method signature. It does not return an NSInvocation that corresponds to the current method invocation. This is pretty easy to see why:
- (void)doBar:(id)bip {
NSLog(#"hi there!")
}
- (void)doFoo {
NSMethodSignature *sig = [self methodSignatureForSelector:#selector(doBar:)];
NSInvocation *i = [NSInvocation invocationWithMethodSignature:sig];
}
When you create the invocation in doFoo for the doBar: method, it's obvious to see that the arguments must be empty, because doBar: hasn't been executed, and thus there is no argument. Changing #selector(doBar:) to _cmd wouldn't magically change anything.
So the next question: is there a way to get an NSInvocation for the current method invocation? Not that I know of. NSInvocation is an extremely complicated class, and constructing one from the current method would be a nightmare.
I strongly suggest finding a different approach to do whatever it is you want to do.

Even though the question is old and answered, here is a link that provides an easy and very elegant way to create an invocation instance for any selector/method that is known at compile time:
http://www.cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

Related

Why NSInvocation return value creates a zombie?

I am trying to build a JavaScript to Native communication. For that purpose I need to execute dynamically a method on some class when JavaScript calls it.
I have a problem with NSInvocation getting the return value. When the getReturnValue is used the app crashes due to zombie. The zombie is indicated to be coming from the invocation called method's return value.
If I comment out the [invocation getReturnValue:&result]; line the app doesn't break.
The test method I am currently calling returns and (NSString *)
If I make the invoked selector method implementation return a literal string like #"firstsecond") the app doesn't break as well.
Why does it need a reference to it any way when the invocation method has already been executed and a string is returned. Isn't the returned string copied to the id result.
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
if ([#"Native_iOS_Handler" isEqualToString: message.name]) {
NSArray *arguments = [message.body valueForKey:#"arguments"];
NSNumber *callbackID = [message.body valueForKey:#"callbackID"];
NSString *APIName = [message.body valueForKey:#"APIName"];
NSString *methodName = [message.body valueForKey:#"methodName"];
id classAPI = [self.exposedAPIs objectForKey:APIName];
SEL methodToRun = [classAPI getSelectorForJSMethod:methodName];
NSMethodSignature *methodSignature = [classAPI methodSignatureForSelector:methodToRun];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setTarget:classAPI];
[invocation setSelector:methodToRun];
id result;
[invocation invoke];
[invocation getReturnValue:&result];
NSLog(#"%#", result);// output: firstsecond
}
}
//the selector in this case is this
-(NSString*)getFoo{
// Why is this a zombie????
return [NSString stringWithFormat:#"%#%#", #"first", #"second"];
// This works:
//return #"fristsecond"
}
Although the selector in Instruments is different the result is the same. From this picture I understand what I have told you. I have no experience whit Instruments.
You fell victim of ARC not being aware of the way NSInvocation works modifying the result indirectly through another pointer. It's a known problem described here.
What happens is the resulting object indirectly becomes equal to result but ARC is not aware of it and will never retain it.
Without going into too much details NSString is a class cluster. What it effectively means is the implementation underneath changes based on how the string is created and used. Details of it are hidden while interacting with it in obj-c and Apple put a lot of effort to make it seamless to iOS developers. Your case is somewhat special.
Typically you will be getting:
__NSCFConstantString (e.g. #"constant") - string constant for app lifetime , for your case it happens to work, but you should never rely on that
NSTaggedPointerString (e.g. [[#"a"] mutableCopy] copy]) - an optimised short string with internal lookup table.
__NSCFString (e.g. [#"longString" mutableCopy] copy]) long string CoreFoundation representation.
At any time NSString may change implementation underneath so you should never make assumptions about it. Case 3 will immediately go out of scope after returning and get deallocated in next run loop, case 1 will never get deallocated, case 2 (?), but for sure will survive the next run loop.
So basially ARC isn't clever enough to associate the potentially deallocated object with the id result and you run into your problems.
How to fix it?
Use one of these:
1.
void *tempResult;
[invocation getReturnValue:&tempResult];
id result = (__bridge id) tempResult;
2.
__unsafe_unretained id tempResult;
[invocation getReturnValue:&tempResult];
result = tempResult;
Edit
As noted by #newacct in his comment getReturnValue: doesn't do registration of weak pointers hence it's inappropriate in this case. The pointer would not get zeroed when the object gets deallocated.
3.
__weak id tempResult;
[invocation getReturnValue:&tempResult];
result = tempResult;

Calling an initialiser using NSInvocation

I have a scenario where the initialiser to use called after allocing 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
Return a different object than the one it was called on, in which case it would release the one it was called on, and retain the new one before returning it, or
Return 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.
Just rename the invokeSelector method to createObjectByInvokingSelector so it fits with the naming scheme and are doesn't free the returnValue
Whoa .... I think I've stumbled on the answer. I've spent some time searching the net and reading the Clang documentation on ARC. Most of which is so full of 'if's, 'but's and 'maybe's, I think I'd have to spend quite some time to understand it. Anyway, here is the modified code:
...
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 __unsafe_unretained returnValue;
[inv getReturnValue:&returnValue];
return returnValue;
}
Somehow the addition of __unsafe_unretained to the variable that receives the response from the initialiser seems to fix the problem. I have not had the time to work through the Clang doc and figure out why this works. I'm just happy it does.
Perhaps someone with some highly technical knowledge of the memory management of ARC could explain further.

Obj-C: Difference between calling a method (with no input) on an object vs calling a method with input

I am an absolute beginner in objective-c and just read an overview of the language on cocoadevcentral.
The first section briefly discusses syntax for calling methods and gives two examples; one for calling a method on an object and the second is a method with input being called on an object,
[object method];
[object methodWithInput: input];
Can anyone explain the difference to me, possibly with a simple example?
There is no huge difference between the two and all depends on what you are doing.
Method 1
[object method];
There are two parts to this method.
object this is either an instance of a class or is a class itself all depends on the type of method you are calling whether it be an instance method or a class method. Each are declared differently.
A Class method is declared like + (void)myClassMethod; and would be called like [NSString myClassMethod];
An Instance method would be declared like - (void)myInstanceMethod; and would be called like [myStr myInstanceMethod]; (Where myStr is an instace of NSString)
method The second part is the actual method that you are calling this all that this will do when you call something like [myStr myInstanceMethod]; it will call the implementation of that method so it would call
- (void)myInstanceMethod
{
NSLog(#"We called our instance method");
}
Method 2
[object methodWithInput: input];
The only difference here is that we are passing in an argument. So here we have three parts the same first two from method 1 and the argument
input All this is, is the value that you are passing into the method to be used within it.
This type of method would be declared something like - (void)myInstanceMethodWithArgument:(NSString *)str;. Here are just saying that we have an argument of type NSString so when we call this like [str myInstanceMethod:#"Some Random String I want to pass in"]; it will run the following implementation code
- (void)myInstanceMethod:(NSString *)str
{
NSLog(#"My str value is : %#", str);
}
Method 3
[object methodWithInput1:input1 andInput2:input2];
Just throwing this in because you my get a little confused later when dealing with multiple arguments. This is exactly the same as method 2 except it has two arguments and not one. This would be declared like - (void)myInstanceMethodWithInput1:(NSString *)str1 andInput2:(NSString *)str2;. Does exactly the same is method 2 except it has multiple arguments that's it nothing to be scared of.
I would recommend that you have a read of the Apple Coding Guidelines for Cocoa. Best of look with learning as it's probably not the easiest language to learn.
Try substituting 'input' for 'argument'..
[object someMethod:(CGFloat )floatArgument];
The type should be there in the brackets, with a dereference operator (*) eg (NSObject *)theArgument if that argument is a pointer.
So basically some methods supply one or more arguments, and some do not, just as with C
When you call method without input data it means that method will work with already existing class's properties.
- (void)someMethod {
self.var_1 = self.var_2 + self.var_3; //or any other implementation
}
You will call this method like this
[self someMethod];
When you call method with some input data it means that this data will be used in method's implementation
- (void)someMethodWithInputData:(NSInteger)inputData {
self.var_1 = self.var_2 * inputData;
}
You will call it like this
[self someMethodWithInputData:10];
It's just the difference between saying "I wait" and "I eat an omelette". In some cases you can say what you mean with just a verb. In some cases a sentence needs an object in order to communicate its meaning.
The same thing applies in programming. Sonetimes you're going to need to specify more than just the action. But not always.

Why is [object.delegate self] allowed in Objective-C?

I have seen several examples of Objective-C code, where a delegate needs to be defined. For example, when using MapKit, I see statements such as:
[self.mapView.delegate self];
I also sometimes see the following:
[self.mapView setDelegate:self];
And still I find some examples that do the following:
self.mapView.delegate = self;
I understand how the second and third are equivalent, however I do not understand how the first is able to run, let alone compile. What I mean is: how is self a valid selector in this context? How does this code translate to an assignment statement for the delegate property?
self in [self.mapView.delegate self]; and [self.mapView setDelegate:self]; are different — yet related things. while the latter self represents the object in its scope it is used, [object self] is a method -(id)self defined in the NSObject protocol.
from the doc:
self
Returns the receiver. (required)
- (id)self
Return Value The receiver.
Availability Available in OS X v10.0 and later.
As the NSObject class implements the NSObject protocol, nearly any object we use in our codes will understand this method.
A clue, what it useful for, gives us the GNUStep documentation:
self
- (id) self;
Availability: OpenStep
Returns the receiver. In a proxy, this may (but is not required to) return the proxied object.
We can use it for proxies.
Also in KVC it can be useful that there is a method called self, as the operator needs a right key path, but actually the object itself is what we need:
NSArray *numbers = #[#1, #1, #2 ,#3, #5]
NSNumber* sum = [numbers valueForKeyPath: #"#sum.self"];
sum will be 12.
[self.mapView setDelegate:self]; and self.mapView.delegate = self; are equivalent and self sends for the object it is used in. Basically each Objective-C message translates to a C function, that takes at least two parameters. -setDelegate: would be translation in runtime to
void setDelegate(id self, SEL _cmd, id delegate)
{
// implementation ....
}
As you can see here, self is just the default name of the object passed in as the first parameter by the runtime and refers to the object of the class the method is defined on.
Although it is often referred as a keyword, self isn't. it is just a convention. As it is possible to construct Objective-C methods by using C functions, the Implementation IMP type and selector SEL type, you could decide to call the first object differently, like this if you would like to have C++ naming.

How do you call a property setter by name?

I've asked a related question, but thought I'd split this out into its own question. See the code below for calling a property getter.
SEL propSelector = NSSelectorFromString(propertyName);
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[target class]instanceMethodSignatureForSelector:propSelector]];
[inv setSelector:propSelector];
[inv setTarget:target];
[inv invoke];
float value;
[inv getReturnValue:&value];
I'd like to do the same thing, but call the property SETTER. I'd also like to avoid manually crafting the setter name by building a #"setPropertyName:" string. Bottom line - is it possible to use the selector created on this line to call the setter?
SEL propSelector = NSSelectorFromString(propertyName);
Use Key-Value Coding.
I.e. [someObject setValue: anObjectValue forKey: #"foo"];
Having read your other question earlier today, this is what I can come up with:
It's not possible to use it directly as the setter. The getter does not accept any arguments, so your selector will never have the colon, so it's never gonna work.
Easiest thing to do is define your setter methods as foo: instead of setFoo:. This way you'll only have to append the colon (which is quite cheap, requires only two extra strings)