Programmatically access method arguments in Objective-C - objective-c

Let's say I have the following method:
-(void)methodWithVar1:(NSString*)var1 var2:(NSString*)var2;
Now of course I can access var1 and var2 within methodWithVar1:var2: by using the variable names directly. But what I'd like to do is something like methodArgs[0] and methodArgs[1] to access the variables.
Is there a way to do this? I've looked at runtime.h but can't see anything that helps.
Why do I want to do this?
In certain circumstances, when a method is called, I want to prevent the method executing, but allow it to execute at another moment in time. I'm doing this by creating an NSInvocation object that allows me to 're-call' the method when I would prefer it. However, NSInvocation requires that I call setArgument:atIndex:, which I have to do manually. If the method every changes, then the population of NSInvocation needs to be updated. I want to avoid updating it manually and have a generic way of doing it.
Example
-(void)methodWithVar1:(NSString*)var1 var2:(NSString*)var2{
if (someCheckToSeeIfICannotRun) {
NSMethodSignature * methodSignature =
[self.class instanceMethodSignatureForSelector:_cmd];
KWInvocation * invocation =
[KWInvocation invocationWithMethodSignature:methodSignature];
[invocation.invocation setTarget:self];
[invocation.invocation setSelector:_cmd];
[invocation.invocation setArgument:&var1 atIndex:2];// This and
[invocation.invocation setArgument:&var2 atIndex:3];// this, I would prefer to make generic
[invocation.invocation retainArguments];
//
// Store invocation somewhere so I can call it later...
//
}
else {
// Let the method run as normal
}
}

I think this can help you:
#import <objc/runtime.h>
void logArguments(const id* selfPtr)
{
id obj = *selfPtr;
SEL sel = *(SEL*)(void*)(--selfPtr);
Method m = class_getInstanceMethod([obj class], sel);
for (unsigned int cnt = method_getNumberOfArguments(m) - 2; 0 != cnt; --cnt)
{
NSLog(#"arg: %#", *(--selfPtr));
}
}
You can call logArguments(&self); in any method, but there is one restriction: all arguments should be objective-c objects.

Related

Calling original function from swizzled function

I am messing around with method swizzling and would like to call the original function after performing a method_exchangeImplementations. I have two projects I have setup for this.
The first project is the main project for the application. This project includes all of the logic for the application. Notice that originalMethodName is called when the view loads.
#implementation ViewController
- (void)originalMethodName
{
NSLog(#"REAL %s", __func__);
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"REAL %s", __func__);
[self originalMethodName];
}
#end
The second project includes only the code for swizzling. I have a method swizzle_originalMethodName which includes the code I want to inject into the main application with the originalMethodName function is called.
#implementation swizzle_ViewController
- (void)swizzle_originalMethodName
{
NSLog(#"FAKE %s", __func__);
}
__attribute__((constructor)) static void initializer(void)
{
NSLog(#"FAKE %s", __func__);
Class c1 = objc_getClass("ViewController");
Class c2 = [swizzle_ViewController class];
Method m1 = class_getInstanceMethod(c1, #selector(originalMethodName));
Method m2 = class_getInstanceMethod(c2, #selector(swizzle_originalMethodName));
method_exchangeImplementations(m1, m2);
}
#end
The swizzle is working just fine (as seen in the output below), but now I want to be able to call originalMethodName from the swizzle_originalMethodName
2016-08-17 14:18:51.765 testMacOS[7295:1297055] FAKE initializer
2016-08-17 14:18:51.822 testMacOS[7295:1297055] REAL -[ViewController viewDidLoad]
2016-08-17 14:18:51.822 testMacOS[7295:1297055] FAKE -[swizzle_ViewController swizzle_originalMethodName]
I have tried to use NSInvocation but am not having any luck. Any ideas what I am doing wrong?
Class c1 = objc_getClass("ViewController");
Method m1 = class_getInstanceMethod(c1, #selector(originalMethodName));
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding( m1)];
NSInvocation *originalInvocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[originalInvocation invoke];
If you are swizzling within a class hierarchy, e.g. you have a subclass which swizzles one of its ancestors methods with one of its own, then you simply have the swizzled-in method apparently call itself – that call will actually call the swizzled-out method as the methods have been swapped. In your case you would have:
- (void)swizzle_originalMethodName
{
NSLog(#"FAKE %s", __func__);
[self swizzle_originalMethodName]; // call original
}
This does not work in your case as you are cross-class swizzling, so self doesn't reference the class with the swizzled-out method. And you don't have an instance of the swizzling class you can call the swizzled-out method on...
Here is one easy way to fix this, what your swizzle-in method needs to be able to do is call the original implementation, and you can get that when you setup the swizzling.
In Objective-C a method is implemented by a function whose first two arguments are the object reference the method is being called on and the selector and the remaining arguments are those of the method. For example the NSString method:
- (NSRange)rangeOfString:(NSString *)aString
is implemented by a function something like:
NSRange rangeOfStringImp(NSString *self, SEL cmd, NSString *aString)
You can obtain a function pointer to this implementation function using method_getImplementation.
To your code, first in your swizzle_ViewController declare a type for the implementation function of the method you are swizzling, and a global to store the function pointer:
typedef void (*OriginalImpType)(id self, SEL selector);
static OriginalImpType originalImp;
Now in your initializer method you need to save the method implementation, you can do this by adding the line shown:
Method m1 = class_getInstanceMethod(c1, #selector(originalMethodName));
originalImp = (OriginalImpType)method_getImplementation(m1); // save the IMP of originalMethodName
Finally have your swizzled-in method call the saved implementation:
- (void)swizzle_originalMethodName
{
NSLog(#"FAKE %s", __func__);
originalImp(self, #selector(originalMethodName)); // call the original IMP with the correct self & selector
}
Optional: The above works correctly, however it does a little more than is required – the method implementations are both exchanged and one is stored in a global variable, all you really need to do is save the original implementation of m1 and then set its implementation to that of m2. You can address this by replacing the call to method_exchangeImplementations with:
method_setImplementation(m1, method_getImplementation(m2));
It is a little more typing, but somewhat clearer as to what actually needs to be done.
HTH
There is a slightly easier option to call the original implementation that doesn't require you to store the method implementation directly. When you exchange implementations of the methods, the original implementation will be stored in the swizzler class. You can fetch the swizzled out implementation using the class_getMethodImplementation function. Here is a playground sample:
import Cocoa
let fooSelector = Selector("fooWithArg:")
let swizzledFooSelector = Selector("swizzled_fooWithArg:")
class A: NSObject {
#objc dynamic func foo(arg: String) {
print("Foo \(arg) in A")
}
}
class B: NSObject {
private typealias FooFunc = #convention(c) (AnyObject, Selector, String) -> Void
#objc func swizzled_foo(arg: String) {
print("Swizzled_foo \(arg) in B")
unsafeBitCast(
class_getMethodImplementation(B.self, swizzledFooSelector),
to: FooFunc.self
)(self, fooSelector, arg)
}
}
method_exchangeImplementations(
class_getInstanceMethod(A.self, fooSelector)!,
class_getInstanceMethod(B.self, swizzledFooSelector)!
)
A().foo(arg: "bar")

How to invoke an Objective-C block obtained at runtime?

I'm trying to write a mock of HKHealthStore. In the stubbed executeQuery: I need to call the result handler block of a HKSampleQuery instance passed to it. The block is private so I need to get it at runtime. This is what I have so far:
- (void)executeQuery:(HKQuery *)query {
NSAssert([query isKindOfClass:HKSampleQuery.class], #"Mock executeQuery: not implemented yet for other query types than HKSampleQuery");
HKSampleQuery *sampleQuery = (HKSampleQuery *)query;
NSMutableArray<HKObject *> *queryResults = [NSMutableArray new];
for (HKObject *o in self.storedObjects) {
if ([sampleQuery.predicate evaluateWithObject:o]) {
[queryResults addObject:o];
}
}
SEL selector = NSSelectorFromString(#"resultHandler");
Method m = class_getInstanceMethod(HKSampleQuery.class, selector);
IMP imp = method_getImplementation(m);
typedef void(*resultHandler_t)(id, SEL, void(^)(HKQuery*, NSArray*, NSError*));
resultHandler_t f = (resultHandler_t)imp;
// here, I need to invoke the result handler block with sampleQuery, queryResults and nil as arguments
}
Note the selector name is "resultHandler" even though the parameter of the initializer of HKSampleQuery is called "resultsHandler".
Is there any way to invoke the block with appropriate arguments?
You're not doing what you think you are doing. You are getting the implementation of the method resultHandler (the getter method of the property resultHandler). The block you want is the value of the property resultHandler, which is the return value of running the getter method. In other words, you need to run the getter and get the result, not get the getter itself.
Simplest way to call the method and get the return value (since in this case the return value is a regular object pointer type) would be
typedef void (^resultHandler_t)(HKSampleQuery *query, NSArray *results, NSError *error);
resultHandler_t f = [sampleQuery performSelector:#selector(resultHandler)];
f(sampleQuery, queryResults, nil);
Alternately, if you declare (but not implement) the instance method or property resultHandler in a dummy category of HKSampleQuery, you can then access the property directly like resultHandler_t f = sampleQuery.resultHandler;

Dynamic method creation in Objective-C

In the book The Pragmatic Programmer, the authors suggest that all method inputs should be validated. This allows problems with a method to be caught early and their sources traced easily.
In my Mac application, I accomplished this by creating an Assert class. This class has several class methods. These methods determine if some precondition is met, and if it is not, then an exception is thrown. A typical assertion might looks something like this:
-(void) setWidth: (int) theWidth {
[Assert integer: width isGreaterThanInteger: 0];
width = theWidth;
}
This works really well, and significantly reduced the amount of time I've spend bug hunting. However, I've noticed lately some of the assertion methods are very useful as predicates. For example, my integer:isGreaterThanInteger:andLessThanInteger: and my stringIsNotEmpty: methods are equally useful. To this end, I created a second class Predicate, which I filled with several of my more useful predicate methods. So I took the logic from the assert methods, and moved it into Predicate, and then rewrote my Assert methods like the following:
if ![Predicate predicateMethod]
throw exception
This has turned into a maintenance nightmare. If I change the name of a method name in Predicate, I must also change it in Assert to stay consistent. If I update the documentation of an Assert method, then I must do the same to a Predicate method.
Ideally, I would like the reconstruct the Assert class so that when any method is called on it, it intercepts the selector. The Predicate class can then be checked to see if it responds to the selector, and if it does, the method is called on Predicatewith the same arguments that were passed into the Assert method. If the Predicate method returns false, then an exception is thrown.
Is there a way to do this in Objective-C?
Thanks.
You could use -forwardingTargetForSelector: to simply forward the method to another object, but if you want advanced behavior (like checking the return value to see if it's false), you may need to use -forwardInvocation:. (However, note that the documentation says this is "much more expensive" than the former option.)
If you're using pure Objective-C, you should see the "Forwarding" discussion here. It basically describes how to do exactly what you want, including example code.
If you're using Cocoa then you might have to use forwardInvocation: instead.
I ended up overriding resolveClassMethod:. While overriding forwardInvocation might have worked (I would have had to figure out some way to override it for the class object), resolveClassMethod: seems like it's the easier and more efficient method. Here's what my final implementation ended up looking like:
#import "Assert.h"
#import "Predicate.h"
#include <objc/objc-runtime.h>
void handlePredicateSelector(id self, SEL _cmd, ...);
#implementation Assert
+(void) failWithMessage: (NSString *) message
{
NSLog(#"%#", message);
[NSException raise:#"ASSERTION FAILURE" format:message];
}
+(void) fail
{
[Assert failWithMessage:#"An unconditional failure has been detected."];
}
+(BOOL) resolveClassMethod: (SEL) selector
{
if ([(id) [Predicate class] respondsToSelector:selector])
{
/*
The meta class fix was taken from here: http://iphonedevelopment.blogspot.com/2008/08/dynamically-adding-class-objects.html
*/
//get the method properties from the Predicate class
Class predicateMetaClass = objc_getMetaClass([[Predicate className] UTF8String]);
Method predicateMethod = class_getClassMethod(predicateMetaClass, selector);
const char *encoding = method_getTypeEncoding(predicateMethod);
Class selfMetaClass = objc_getMetaClass([[self className] UTF8String]);
class_addMethod(selfMetaClass, selector, (IMP) handlePredicateSelector, "B#:?");
return YES;
}
return [super resolveClassMethod:selector];
}
#end
void handlePredicateSelector(id self, SEL _cmd, ...)
{
//get the number of arguments minus the self and _cmd arguments
NSMethodSignature *predicateMethodSignature = [(id) [Predicate class] methodSignatureForSelector:_cmd];
NSUInteger numberOfArguments = [predicateMethodSignature numberOfArguments] - 2;
NSInvocation *predicateInvocation = [NSInvocation invocationWithMethodSignature:predicateMethodSignature];
[predicateInvocation setTarget:[Predicate class]];
[predicateInvocation setSelector:_cmd];
va_list ap;
va_start(ap, _cmd);
for (int i = 0; i < numberOfArguments; i++)
{
void *arg = va_arg(ap, void *);
[predicateInvocation setArgument:&arg atIndex:i+2];
}
va_end(ap);
BOOL returnValue;
[predicateInvocation invoke];
[predicateInvocation getReturnValue:&returnValue];
//determine if the assertion is true
if (!returnValue)
{
[Assert failWithMessage:[NSString stringWithFormat: #"The following assertion failed: %#", NSStringFromSelector(_cmd)]];
}
}
The only thing I couldn't really figure out was how to get the type encoding from the method signature. It didn't seem to affect the output of the methods, but I would like to fix it if I can.

Objective-C, how can i hook up a method in another class

Objective-C keeps all its methods in a huge hashtable - so shouldn't it possible to patch this table and replace an existing method with my own patched method (which then calls the original)?
I need a way to hook up the NSWindow KeyUp method in a window which i can't subclass cause it's already created.
I need some code or at least some keywords i can use for further searching.
You should NOT swizzle methods for this. This is deprecated behavior. This will affect ALL windows in your app not just the one you wanted to change. However, what you should do instead is to subclass NSWindow already and then change the class of that window at runtime. This can be done using this runtime function:
Class object_setClass(id object, Class cls)
Reference is here: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/doc/uid/TP40001418-CH1g-SW12
Your code should then look like this:
object_setClass(theWindow, [MyWindowSubclass class]);
On problem you might experience is that window already being a subclass of NSWindow. If that's the case there are more complicated ways to achieve this. You can construct a class dynamically at runtime. Here's some more code. Given that window is the target window:
Class newWindowClass = objc_allocateClassPair([window class], "MyHackyWindowSubclass", 0);
Method upMethod = class_getInstanceMethod(newWindowClass, #selector(keyUp:));
method_setImplementation(upMethod, new_NSWindow_keyUp_);
object_setClass(window, newWindowClass);
I'm not totally sure this does not change the implementation of the superclass. The documentation is a bit unspecific about it. However, you should still try it. If it does not work, replace the second and third line by this one:
class_replaceMethod(newWindowClass, #selector(keyUp:), new_NSWindow_keyUp_, "v#:#");
In any case you need to define the new Method implementation. It could look like that (partially by KennyTM):
void new_NSWindow_keyUp_(NSWindow* self, SEL _cmd, NSEvent* evt) {
[super keyUp: evt];
... // do your changes
}
Of course it is possible. In fact, you don't even need to look into the hash table — there's standard API for this.
For example:
typedef void (*NSWindow_keyUp__IMP)(NSWindow* self, SEL _cmd, NSEvent* evt);
static NSWindow_keyUp__IMP original_NSWindow_keyUp_;
void replaced_NSWindow_keyUp_(NSWindow* self, SEL _cmd, NSEvent* evt) {
NSLog(#"Entering keyUp:. self = %#, event = %#", self, evt);
original_NSWindow_keyUp_(self, _cmd, evt);
NSLog(#"Leaving keyUp:. self = %#, event = %#", self, evt);
}
...
Method m = class_getInstanceMethod([NSWindow class], #selector(keyUp:));
original_NSWindow_keyUp_ = method_setImplementation(m, replaced_NSWindow_keyUp_);

Creating a selector from a method name with parameters

I have a code sample that gets a SEL from the current object,
SEL callback = #selector(mymethod:parameter2);
And I have a method like
-(void)mymethod:(id)v1 parameter2;(NSString*)v2 {
}
Now I need to move mymethod to another object, say myDelegate.
I have tried:
SEL callback = #selector(myDelegate, mymethod:parameter2);
but it won't compile.
SEL is a type that represents a selector in Objective-C. The #selector() keyword returns a SEL that you describe. It's not a function pointer and you can't pass it any objects or references of any kind. For each variable in the selector (method), you have to represent that in the call to #selector. For example:
-(void)methodWithNoParameters;
SEL noParameterSelector = #selector(methodWithNoParameters);
-(void)methodWithOneParameter:(id)parameter;
SEL oneParameterSelector = #selector(methodWithOneParameter:); // notice the colon here
-(void)methodWIthTwoParameters:(id)parameterOne and:(id)parameterTwo;
SEL twoParameterSelector = #selector(methodWithTwoParameters:and:); // notice the parameter names are omitted
Selectors are generally passed to delegate methods and to callbacks to specify which method should be called on a specific object during a callback. For instance, when you create a timer, the callback method is specifically defined as:
-(void)someMethod:(NSTimer*)timer;
So when you schedule the timer you would use #selector to specify which method on your object will actually be responsible for the callback:
#implementation MyObject
-(void)myTimerCallback:(NSTimer*)timer
{
// do some computations
if( timerShouldEnd ) {
[timer invalidate];
}
}
#end
// ...
int main(int argc, const char **argv)
{
// do setup stuff
MyObject* obj = [[MyObject alloc] init];
SEL mySelector = #selector(myTimerCallback:);
[NSTimer scheduledTimerWithTimeInterval:30.0 target:obj selector:mySelector userInfo:nil repeats:YES];
// do some tear-down
return 0;
}
In this case you are specifying that the object obj be messaged with myTimerCallback every 30 seconds.
You can't pass a parameter in a #selector().
It looks like you're trying to implement a callback. The best way to do that would be something like this:
[object setCallbackObject:self withSelector:#selector(myMethod:)];
Then in your object's setCallbackObject:withSelector: method: you can call your callback method.
-(void)setCallbackObject:(id)anObject withSelector:(SEL)selector {
[anObject performSelector:selector];
}
Beyond what's been said already about selectors, you may want to look at the NSInvocation class.
An NSInvocation is an Objective-C message rendered static, that is, it is an action turned into an object. NSInvocation objects are used to store and forward messages between objects and between applications, primarily by NSTimer objects and the distributed objects system.
An NSInvocation object contains all the elements of an Objective-C message: a target, a selector, arguments, and the return value. Each of these elements can be set directly, and the return value is set automatically when the NSInvocation object is dispatched.
Keep in mind that while it's useful in certain situations, you don't use NSInvocation in a normal day of coding. If you're just trying to get two objects to talk to each other, consider defining an informal or formal delegate protocol, or passing a selector and target object as has already been mentioned.