forward invocation, by hand vs magically? - objective-c

I have the following two class:
//file FruitTree.h
#interface FruitTree : NSObject
{
Fruit * f;
Leaf * l;
}
#end
//file FruitTree.m
#implementation FruitTree
//here I get the number of seeds from the object f
#end
//file Fruit
#interface Fruit : NSObject
{
int seeds;
}
-(int) countfruitseeds;
#end
My question is at the point of how I request the number of seeds from f. I have two choices.
Either: Since I know f I can explicitly call it, i.e. I implement the method
-(int) countfruitseeds
{
return [f countfruitseeds];
}
Or: I can just use forwardInvocation:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
// does the delegate respond to this selector?
if ([f respondsToSelector:selector])
return [f methodSignatureForSelector:selector];
else if ([l respondsToSelector:selector])
return [l methodSignatureForSelector:selector];
else
return [super methodSignatureForSelector: selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:f];
}
(Note this is only a toy example to ask my question. My real classes have lots of methods, which is why I am asking.)
Which is the better/faster method?

The direct method implementation is much, much faster. But if you want a real proxy object, the forwardInvocation: route is really the only way to go. Even if you us a macro to make the method declarations very short, you'd still need to write all the method names you wanted and keep the list up to date when any are added or removed.

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.

How to create a subclass of NSOutputStream

I am trying to write a subclass of NSOutputStream to perform a very simple function - keep track of the total number of bytes sent to the stream. However, I am running into an unexpected problem initializing an instance of the function. Here is the code:
#import <Foundation/Foundation.h>
#interface TrackingOutputStream : NSOutputStream {
unsigned long long bytesWritten;
}
#property (readonly) unsigned long long bytesWritten;
#end
---------------------------
#import "TrackingOutputStream.h"
#implementation TrackingOutputStream
#synthesize bytesWritten;
- (NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)length {
NSInteger written = [super write:buffer maxLength:length];
bytesWritten += written;
return written;
}
#end
However, when I try to initialize this class:
TrackingOutputStream *os = [[[TrackingOutputStream alloc] initToFileAtPath:#"/tmp/test" append:NO] autorelease];
I get the following error:
-[TrackingOutputStream initToFileAtPath:append:]: unrecognized selector sent to instance 0x101a187e0
I've tried adding an explicit constructor to the class that calls super, but it doesn't make any difference (as it shouldn't).
If you're working with an API that expects an instance of NSOutputStream, it can be cumbersome to implement all the methods of NSOutputStream in order to forward them to the wrapped (delegate) instance. You can use method-forwarding approach that will allow you to add behavior without writing all the wrapper methods. This involves writing a simple implementation of forwardingTargetForSelector: and respondsToSelector:
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (class_respondsToSelector([self class], aSelector)) { return self; }
if ([self.delegate respondsToSelector:aSelector]) { return self.delegate; }
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if (class_respondsToSelector([self class], aSelector)) { return YES; }
if ([self.delegate respondsToSelector:aSelector]) { return YES; }
return [super respondsToSelector:aSelector];
}
For a longer, detailed description please see the blog post on using Objective-C duck-typing to more easily subclass NSOutputStream. Or check out the sample on https://github.com/jwb/ObjC-DuckType
NSOutputStream has very specific subclassing requirements that are documented in the class's documentation.
Note that the documentation explicitly states that you must implement the appropriate initializers fully. I.e. you can't subclass to change the behavior as you described. At least, not easily.
Instead, create a class whose instances wrap an instance of NSOutputStream and add the behavior you desire.
As an additional note, it should be possible to retrieve info on the written data using the - propertyForKey: of NSStream. Check out the
NSStreamDataWrittenToMemoryStreamKey property key.

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.

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.

How do I compare objects in Objective-C?

How do I compare two objects of a custom class? My idea was to add an additional method to the class in which I can compare the current object with another object of the same kind.
So I can write my own code how each field of the class is compared.
This is how I would do it. Or are there some predefined methods to do that? Like "isEqualTo" of the NSString class?
The pointers to -isEqual: are good, but if you implement -isEqual:, you absolutely must also implement -hash in such a way that if two objects return YES for -isEqual: they will also return the same value for -hash. Implementing isEqual: without also implementing -hash leads to some very surprising bugs when you use Collections like NSArray.
For new developers, I tend to recommend against overloading -isEqual:. I recommend instead using the same technique as NSString, and create a custom -isEqualToFoo: (where Foo is your class) until you understand the impact of -isEqual: on collections and specifically want this behavior. Overloading -isEqual: powerful, but the bugs you can create are subtle. Creating your own custom comparator is safer and clearer in many cases.
The standard way is to override - (BOOL)isEqual:(id)anObject and - (NSUInteger)hash.
You should read the documentation for NSObject protocol and this SO question has some interesting answers on how to write your hash method.
Look at the isEqual: and the compare: method.
I have the following object:
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, SeasonType) {
kWinter,
kSpring,
kSummer,
kFall
};
#interface Season : NSObject
#property (nonatomic) SeasonType season;
#property (nonatomic) NSUInteger year;
+(id) seasonWithYear:(NSInteger)year season:(SeasonType)season;
-(id) initWithYear:(NSInteger)year season:(SeasonType)season;
#end
What I do is overwrite base NSObject comparison methods, there's no need of reinventing the wheel and code keeps cleaner as well:
#import "Season.h"
#interface Season()
#end
#implementation Season
+(id) seasonWithYear:(NSInteger)year season:(SeasonType)season{
return [[self alloc] initWithYear:year season:season];
}
-(id) initWithYear:(NSInteger)year season:(SeasonType)season{
self = [super init];
if (self)
{
_year = year;
_season=season;
_baseDate=nil;
}
return self;
}
#pragma mark - NSObject
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[Season class]]) {
return NO;
}
return [self _isEqualToSeason:(Season *)object];
}
- (NSUInteger)hash {
return self.season ^ self.year;
}
#pragma mark - Private/Internal
- (BOOL)_isEqualToSeason:(Season *)season {
if (!season) {
return NO;
}
return ((!self.season && !season.season) || self.season == season.season) &&
((!self.year && !season.year) || self.year == season.year) ;
}
#end
Usage:
Season *season2 = [Season seasonWithYear:2010 season:kFall];
Season *season3 = [Season seasonWithYear:2009 season:kFall];
[season2 isEqual:season3];