How does NSProxy "transform itself into another object"? - objective-c

The NSProxy Class Reference says this:
Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object.
How exactly would the "transform itself into the real object" work?
To make things a little more specific, suppose class Foo has a method newFooWithString: that takes a string and returns a new instance of Foo. Would it be possible to setup an NSProxy that sits around, and if a pleaseBecomeAFooUsingString: #"bar" message is received, transforms itself into [Foo newFooWithString: #"bar"], occupying the same memory, without messing with other references to itself that may exist?

If you have a pointer to the same NSProxy instance all over the code and will "transform" it, it will change all over the code. There is no way to differentiate a caller of a method for an object, so you will not be able to alternate targets for forwarding of methods invocation in your code automatically. Common transformable proxy will looks in following way:
MyTrickyProxy.h
#import <Foundation/Foundation.h>
#interface MyTrickyProxy : NSProxy {
NSObject *object;
}
- (id)transformToObject:(NSObject *)anObject;
#end
MyTrickyProxy.m
#import "MyTrickyProxy.h"
#implementation MyTrickyProxy
- (void)dealloc
{
[object release];
object = nil;
[super dealloc];
}
- (NSString *)description
{
return [object description];
}
//Stupid transform implementation just by assigning a passed in object as transformation target. You can write your factory here and use passed in object as id for object that need ot be created.
- (id)transformToObject:(NSObject *)anObject
{
if(object != anObject) {
[object release];
}
object = [anObject retain];
return object;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
if (object != nil) {
[invocation setTarget:object];
[invocation invoke];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
NSMethodSignature *result;
if (object != nil) {
result = [object methodSignatureForSelector:sel];
} else {
//Will throw an exception as default implementation
result = [super methodSignatureForSelector:sel];
}
return result;
}
#end
So what you requested is some sort of code-magic, but NSProxy is a simple forwarder of a messages, there is no any magic at all, so your goal is not achievable in a way as you described.

You could create a subclass of of NSProxy that changes which object it forwards it methods to based of what every criteria you want. So you object will always point to NSProxy but you pleaseBecomeAFooUsingString: will change the the object it forwards to as a Foo.

Related

Programmatically creating new instance method to track message sending

I want to design a class (TrackingClass) that would be in charge of tracking the calls to some methods of an other class (TrackedClass), i.e. of setting up the method swizzling from what I understood.
So let's say I load up an array with #selectors of the instance methods of TrackedClass i'm interested in.
Here is the pseudo-code I would like to run :
#implementation BCTrackedClass
-(void)doA
{
}
#end
and
#implementation BCTrackingClass
#import "BCTrackingClass.h"
#import "BCTrackedClass.h"
#include <objc/runtime.h>
#include <objc/objc-runtime.h>
#implementation BCTrackingClass
void myMethodIMP(id self, SEL _cmd);
void myMethodIMP(id self, SEL _cmd)
{
//NSLog(#"_cmd : %#",NSStringFromSelector(_cmd));
[BCTrackingClass logCallForMethod:NSStringFromSelector(_cmd)];
objc_msgSend(self,
NSSelectorFromString([NSString stringWithFormat:#"tracked%#",NSStringFromSelector(_cmd)]));
}
+(void)setUpTrackingForClass:(Class)aClass andMethodArray:(NSArray*)anArray //Array of selectorsStrings of methods to track
{
for (NSString* selectorString in anArray)
{
SEL selector = NSSelectorFromString(selectorString);
SEL trackedSelector = NSSelectorFromString([NSString stringWithFormat:#"tracked%#",selectorString]);
class_addMethod(aClass,
trackedSelector,
(IMP) myMethodIMP, "v#:");
//Swizzle the original method with the tracked one
Method original = class_getInstanceMethod(aClass,
selector);
Method swizzled = class_getInstanceMethod(aClass,
trackedSelector);
method_exchangeImplementations(original, swizzled);
}
}
+(void)logCallForMethod:(NSString*)aSelectorString
{
NSLog(#"%#",aSelectorString);
}
#end
Theoretically, I'm just missing the bit of code where I could effectively create this new instance method trackedSelector. Can I achieve that ?
Edit
I updated the code with some new piece of information, am I getting closer ?
Edit 2
I set up a Github repository with a Demo application if people want to dynamically try out their ideas.
Source : BCTrackingClass on Github
Edit 3
I finally come up with a working version of the code (cf Github repo, or just above). My next problem is : I want my class to be instance based (currently, all my methods are class methods), so that I can assign a property #property NSMutableDictionnary* to instances of the class for call logging.
I'm not sure how to achieve that. Any ides ?
Do you want to do it for all instances of all objects of that class?
for some selectors or all of them?
...
If what you want is to track specific instances, then the simplest route is to use isa swizzling, doing that, more or less (the code is absolutely untested)
#interface ClassTracker
+ (void)trackObject:(id)object;
#end
static const char key;
#implementation ClassTracker
+ (void)trackObject:(id)object
{
objc_setAssociatedObject(object, &key, [object class], OBJC_ASSOCIATION_ASSIGN);
object_setClass(object, [ClassTracker class]);
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
Class aClass = objc_getAssociatedObject(self, &key);
return [aClass instanceMethodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
Class aClass = objc_getAssociatedObject(self, &key);
// do your tracing here
object_setClass(self, aClass);
[invocation invoke];
object_setClass(self, [ClassTracker class]);
}
// dealloc is magical in the sense that you really want to undo your hooking
// and not resume it ever!
- (void)dealloc
{
Class aClass = objc_getAssociatedObject(self, &key);
object_setClass(self, aClass);
[self dealloc];
}
#end
If it's used for reverse engineering or debug purposes, that should (with minor adaptations) do the trick.
If you intend that to be fast, then you have to do instance method swizzling, knowing their type and so forth.
My "solution" has the drawback that it will only trace entering calls, IOW if a selector calls other ones, since the isa swizzling is paused to recurse the call, then you don't see the new ones until you restore the isa swizzling.
There may be a way to forward the invocation to the original class, without undoing isa swizzling, but I reckon I was too lazy to search for it.

Know when an object is created

Is there any way to use the ObjC runtime library, or Cocoa, to be notified when an object is created, for example, after it returns from the init method?
I want to achieve this without modifying the object, or subclassing it (no subclass on NSObject, for example) and without method swizzling (I already know how to do that).
There is no sanctioned way to be notified when a method executes, unless it specifically notes that it returns a notification, or a pointer to some kind of callback, a block, etc. While swizzling may be one way of going about it, proxying is probably your best bet. Instead of messing with the selector for an entire class, you interpose yourself "as" the class by implementing all its properties and/or forwarding selectors to the target object. In this way, NSProxy and subclasses can be used as wrappers around normal objects, meaning you can respond to any kind of method that happens to be sent through your proxy before forwarding it on to the target. A simple proxy can be modeled after the sample below:
FOUNDATION_EXPORT NSString *const CFIProxyDidInitializeTargetNotification;
#interface CFIObjectProxy : NSProxy {
__strong Foo *_target;
}
- (id)init;
#property(nonatomic, readonly, retain) NSArray* bars;
#end
//...
#import "CFIObjectProxy.h"
NSString *const CFIProxyDidInitializeTargetNotification = #"CFIProxyDidInitializeTargetNotification";
#implementation CFIObjectProxy
- (id)init {
_target = [[Foo alloc]init];
[NSNotificationCenter.defaultCenter postNotificationName:CFIProxyDidInitializeTargetNotification object:nil];
return self;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:_target];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [_target methodSignatureForSelector:sel];
}
- (NSString *)description {
return [_target description];
}
- (NSString *)debugDescription {
return [NSString stringWithFormat:#"<%#:%p> Proxy for Object: %#", NSStringFromClass(self.class), self, _target];
}
- (NSArray*)bars {
return [_target bars];
}
#end
Per default, the runtime doesn't record this. I think I'd use swizzling BUT as you don't want this... I think that CodaFi's idea of wrapping the object in a proxy is best ALTHOUGH this is only an option for allocations you manually do AFAICS
so if you want it to be truly transparent, swizzle after all I'd say

Is there a way to pass the entire argument list to another method in Objective C?

I'd like to be able to pass all the arguments received in my method to a different method, as generically as possible.
Ideally, this would be done by passing a dictionary or some system variable (similar to _cmd).
In other words, I'm looking for something like the arguments array in javascript, or anything giving me access to the currently called method's list of arguments.
I think what you are looking for is NSObject's forwardInvocation: It gets passed an NSInvocation object that contains the information you want. NSInvocation also has a nice method called invokeWithTarget: that pretty much forwards the method call just like if you've called it directly.
The runtime will call fowardInvocation: if you're object is sent a message that it doesn't have a method for, provided you also override methodSignatureForSelector: so the runtime can create the NSInvocation object.
If all your arguments are objects the method forwardInvocation method will look something like this:
#implementation Forwarder
#synthesize friendObject;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [self.friendObject methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog("Forwarding method: %#", [anInvocation selector]);
NSMethodSignature *sig = [anInvocation methodSignature];
// Get the juicy argument list info from [anInvocation methodSignature]
// NOTE: Arguments 0 and 1 are for self and _cmd So we'll skip those.
int numberOfArgs = [[anInvocation methodSignature] numberOfArguments];
// Assuming all arguments are objects.
id objPointer;
NSMutableArray *argArray = [NSMutableArray array];
for (int i = 2; i < numberOfArgs; i++) {
[anInvocation getArgument:&objPointer atIndex:i];
[argArray addObject:objPointer];
}
// Now argArray contains the array of all the arguments.
}
#end
The hard part is that you need to make buffers to hold the argument values. If all the arguments are objects or the same type you can use the above code but It's much more complicated to make a generic function if you use C types. You can use NSMethodSignature's getArgumentTypeAtIndex: but it returns a string encoding of the type and sizeof wont help you there. You would need to make a map of type names to size_ts for malloc/calloc.
Edit: I added a concrete example of what I glossed over as // Get the juicy info in methodSignature As you can see what you want to do is possible but it's pretty tough.
(Check out Apple's documentation on Type Encodings and NSMethodSignature's signatureWithObjCTypes:.)
Edit2: This might be better as a separate answer but Here's a complete (and tested) listing of how you can make use of the listing above to make a method that gets called with an arguments array like in JavaScript.
First make a delegate protocol that the Forwarder object will call when a method is called.
#protocol ForwarderDelegate <NSObject>
- (void)selectorCalled:(SEL)selector withArguments:(NSArray *)args;
#end
Then make the actual Forwarder:
#interface Forwarder : NSObject {
#private
NSObject *interfaceObject;
id<ForwarderDelegate> delegate;
}
// Some object whose methods we want to respond to.
#property (nonatomic, retain) NSObject *interfaceObject;
#property (nonatomic, retain) id<ForwarderDelegate> delegate;
#end
#implementation Forwarder
#synthesize interfaceObject;
#synthesize delegate;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
return [interfaceObject methodSignatureForSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
int numberOfArgs = [[anInvocation methodSignature] numberOfArguments];
NSMutableArray *args = [NSMutableArray array];
id ref;
for (int i = 2; i < numberOfArgs; i++) {
[anInvocation getArgument:&ref atIndex:i];
[args addObject:ref];
}
// Call the method on the interface (original) object.
if ([self.interfaceObject respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:self.interfaceObject];
}
[self.delegate selectorCalled:[anInvocation selector] withArguments:args];
}
#end
Now you can instantiate the forwarder that takes some object and forwards any calls to the delegate. If both the target and the delegate are the same object it would work like this:
#interface testreflectAppDelegate : NSObject <UIApplicationDelegate, ForwarderDelegate> {
UIWindow *window;
}
#end
#implementation testreflectAppDelegate
#synthesize window;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window makeKeyAndVisible];
Forwarder *forwarder = [[[Forwarder alloc] init] autorelease];
forwarder.delegate = self;
forwarder.interfaceObject = self;
[((id)forwarder) doFoo:[NSNumber numberWithInt:1]
withBar:[NSNumber numberWithInt:2]];
return YES;
}
- (void)doFoo:(NSNumber *)foo withBar:(NSNumber *)bar {
NSLog(#"doFoo:withBar: called. Args: %d %d", [foo intValue], [bar intValue]);
}
- (void)doFoo:(NSNumber *)foo {
NSLog(#"doFoo called. Args: %d", [foo intValue]);
}
- (void)selectorCalled:(SEL)selector withArguments:(NSArray *)args {
NSLog(#"selectorCalled: %s with %d arguments", selector, [args count]);
[self doFoo:[args objectAtIndex:0]];
}
#end
Running this should output something like:
testreflect[3098:207] doFoo:withBar: called. Args: 1 2
testreflect[3098:207] selectorCalled: doFoo:withBar: with 2 arguments
testreflect[3098:207] doFoo called. Args: 1
Again this version will only work with id typed arguments. But can work with other types if you use the above mentioned TypeEncodings.
You may want to take a look at the NSMethodSignature class documentation. This class is used to record information on arguments and return values from methods.

How to simplify callback logic with a Block?

Let's say I need to communicate with a class that provides a protocol and calls delegate methods when an operation is complete, as so:
#protocol SomeObjectDelegate
#required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
#end
#interface SomeObject : NSObject
{
}
#end
Now, I've decided that while I could make another class implement the stuffDone: delegate method, I've decided that I'd rather encapsulate the process into a block which is written somewhere close to where SomeObject is instantiated, called, etc. How might I do this? Or in other words, if you look at this famous article on blocks (in the Replace Callbacks section); how might I write a method in SomeObject that accepts a completionHandler: of sorts?
It sounds like you wish to communicate with an existing class which is designed to take a delegate object. There are a number of approaches, including:
using a category to add block-based variants of the appropriate methods;
use a derived class to add the block-based variants; and
write a class which implements the protocol and calls your blocks.
Here is one way to do (3). First let's assume your SomeObject is:
#protocol SomeObjectDelegate
#required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
#end
#interface SomeObject : NSObject
{
}
+ (void) testCallback:(id<SomeObjectDelegate>)delegate;
#end
#implementation SomeObject
+ (void) testCallback:(id<SomeObjectDelegate>)delegate
{
[delegate stuffDone:[NSNumber numberWithInt:42]];
[delegate stuffFailed];
}
#end
so we have some way to test - you will have a real SomeObject.
Now define a class which implements the protocol and calls your supplied blocks:
#import "SomeObject.h"
typedef void (^StuffDoneBlock)(id anObject);
typedef void (^StuffFailedBlock)();
#interface SomeObjectBlockDelegate : NSObject<SomeObjectDelegate>
{
StuffDoneBlock stuffDoneCallback;
StuffFailedBlock stuffFailedCallback;
}
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
- (void)dealloc;
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
// protocol
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
#end
This class saves the blocks you pass in and calls them in response to the protocol callbacks. The implementation is straightforward:
#implementation SomeObjectBlockDelegate
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
if (self = [super init])
{
// copy blocks onto heap
stuffDoneCallback = Block_copy(done);
stuffFailedCallback = Block_copy(fail);
}
return self;
}
- (void)dealloc
{
Block_release(stuffDoneCallback);
Block_release(stuffFailedCallback);
[super dealloc];
}
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
return (SomeObjectBlockDelegate *)[[[SomeObjectBlockDelegate alloc] initWithOnDone:done andOnFail:fail] autorelease];
}
// protocol
- (void)stuffDone:(id)anObject
{
stuffDoneCallback(anObject);
}
- (void)stuffFailed
{
stuffFailedCallback();
}
#end
The only thing you need to remember is to Block_copy() the blocks when initializing and to Block_release() them later - this is because blocks are stack allocated and your object may outlive its creating stack frame; Block_copy() creates a copy in the heap.
Now you can all a delegate-based method passing it blocks:
[SomeObject testCallback:[SomeObjectBlockDelegate
someObjectBlockDelegateWithOnDone:^(id anObject) { NSLog(#"Done: %#", anObject); }
andOnFail:^{ NSLog(#"Failed"); }
]
];
You can use this technique to wrap blocks for any protocol.
ARC Addendum
In response to the comment: to make this ARC compatible just remove the calls to Block_copy() leaving direct assignments:
stuffDoneCallback = done;
stuffFailedCallback = fail;
and remove the dealloc method. You can also change Blockcopy to copy, i.e. stuffDoneCallback = [done copy];, and this is what you might assume is needed from reading the ARC documentation. However it is not as the assignment is to a strong variable which causes ARC to retain the assigned value - and retaining a stack block copies it to the heap. Therefore the ARC code generated produces the same results with or without the copy.
You could do something like this:
typedef void (^AZCallback)(NSError *);
AZCallback callback = ^(NSError *error) {
if (error == nil) {
NSLog(#"succeeded!");
} else {
NSLog(#"failed: %#", error);
}
};
SomeObject *o = [[SomeObject alloc] init];
[o setCallback:callback]; // you *MUST* -copy the block
[o doStuff];
...etc;
Then inside SomeObject, you could do:
if ([self hadError]) {
callback([self error]);
} else {
callback(nil);
}
The below link explains how the call backs using delegates could be easily replaced with blocks.
The examples includes UITableview,UIAlertview and ModalViewController.
click me
Hope this helps.

Method signature for a Selector

I'm new to the Objective C business (Java developer most of the time) and am woking on my first killer app now. :-)
At the moment I am somehow confused about the usage of selectors as method arguments. They seem to be a little bit different than delegates in C# for example.
Given the following method signature
-(void)execute:(SEL)callback;
is there a way to enforce the signature for the selector passed to such a method?
The method is expecting a selector of a method with the following signature
-(void)foo:(NSData*)data;
But the SEL (type) is generic, so there is a good chance to pass a wrong selector to the
execute method. OK at least at runtime one would see a funny behavior... but I would like to see a compiler warning/error when this happens.
The quick answer is: no, there is no way to have the compiler enforce the method signature of a method selector that is provided via a SEL argument.
One of the strengths of Objective-C is that it is weakly-typed language, which allows for a lot more dynamic behaviour. Of course, this comes at the cost of compile-time type safety.
In order to do what (I think) you want, the best approach is to use delegates. Cocoa uses delegates to allow another class to implement "callback"-type methods. Here is how it might look:
FooController.h
#protocol FooControllerDelegate
#required:
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#interface FooController : NSObject
{
id <FooControllerDelegate> * delegate;
}
#property (assign) id <FooControllerDelegate> * delegate;
- (void)doStuff;
#end
FooController.m
#interface FooController (delegateCalls)
- (void)handleData:(NSData *)data;
#end
#implementation FooController
#synthesize delegate;
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
delegate = nil;
...
return self;
}
- (void)doStuff
{
...
[self handleData:data];
}
- (void)handleData:(NSData *)data
{
if (delegate != nil)
{
[delegate handleData:data forFoo:self];
}
else
{
return;
// or throw an error
// or handle it yourself
}
}
#end
Using the #required keyword in your delegate protocol will prevent you from assigning a delegate to a FooController that does not implement the method exactly as described in the protocol. Attempting to provide a delegate that does not match the #required protocol method will result in a compiler error.
Here is how you would create a delegate class to work with the above code:
#interface MyFooHandler <FooControllerDelegate> : NSObject
{
}
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#implementation MyFooHandler
- (void)handleData:(NSData *)data forFoo:(FooController *)foo
{
// do something here
}
#end
And here is how you would use everything:
FooController * foo = [[FooController alloc] init];
MyFooHandler * fooHandler = [[MyFooHandler alloc] init];
...
[foo setDelegate:fooHandler]; // this would cause a compiler error if fooHandler
// did not implement the protocol properly
...
[foo doStuff]; // this will call the delegate method on fooHandler
...
[fooHandler release];
[foo release];
To directly answer your question, no, the SEL type allows any type of selector, not just ones with a specific signature.
You may want to consider passing an object instead of a SEL, and document that the passed object should respond to a particular message. For example:
- (void)execute:(id)object
{
// Do the execute stuff, then...
if ([object respondsToSelector:#selector(notifyOnExecute:)]) {
[object notifyOnExecute:self];
}
// You could handle the "else" case here, if desired
}
If you want to enforce the data handling, use isKindOfClass inside your selector. This works a lot like instanceof which you are familiar with in Java.