木先生

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

第七篇中讲动态属性时,提到了resolveInstanceMethod,这个方法不仅在这里用,还用来实现消息的转发。

消息的转发就是向对象发送一个它本身并没有实现的消息,在运行时确定它实际产生的行为。

举个例子来说,一个Person对象,在运行时根据实际情况,决定是否响应fly这样的方法。如果条件具备,则fly被响应。否则,则不具备这样的方法。类似于AoP的做法。

要实现消息转发,需要覆盖三个方法:

1, resolveInstanceMethod(可选),这个方法为你提供了一个机会,在消息被发现本身没有在类中定义时你可以通过class_addMethod将它添加进去。如果你不这样做,不管你最终返回YES还是NO,程序都会jmp到下一个方法。

2, –(NSMethodSignature*)methodSignatureForSelector: (SEL) sel;通过覆盖这个方法,可以将你要转发的消息的签名创建并返回。如果在这里你返回nil,那么就会直接抛出异常。如果返回一个签名,则程序会jmp到第三个方法。这里返回的方法签名,必须满足两个条件之一(方法名相同||输入参数相同).

3, –(void)forwardInvocation:(NSInvocation * )anInvocation;在这里进行实际的方法调用。参考以下代码:

#import <Foundation/Foundation.h>
#import "Human.h"
@interface Plane : NSObject
-(void)fly:(Human*)p;
-(int)fly1;
-(void)fly2:(Human*)p withBird:(NSString*)birdName;
@end

#import "Plane.h"

@implementation Plane
-(void)fly:(Human*)p{
    NSLog(@"Fly with a guy whose information is \"%@\"", [p description]);
    NSLog(@"fly......");
}
-(int)fly1{
    NSLog(@"I can fly in Plane for fly1");
    return 0;
}
-(void)fly2:(Human*)p withBird:(NSString*)birdName{
    NSLog(@"Fly with a guy whose information is \"%@\"", [p description]);
    NSLog(@"fly......bird:'%@'.", birdName);
}

@end

#import <Foundation/Foundation.h>
void dynamicMethod(id self, SEL _cmd, float height);

@interface Human : NSObject{
    float _height;
}
@property(retain, nonatomic) NSString * name;
@property(readonlyfloat weight;
@property float height;
-(id) initWithWeight:(float)weight;
-(NSString*)description;
@end


#import <objc/runtime.h>
#import "Human.h"
#import "Plane.h"
void dynamicMethod(id self, SEL _cmd, float height){
    NSLog(@"dynamicMethod:%@", NSStringFromSelector(_cmd));
    // here is a question. how to implement the statements to assign the property of id(human.height);
    
// looks like the dynamic property is just for linkage.
    
// 当赋值发生时,由于动态函数的全局性,可以访问其他全局变量,对象。所以更像一种链接机制
}
@implementation Human
@synthesize name;
@synthesize weight;
@dynamic height;
-(id)initWithWeight:(float)w{
    if(self=[super init]){
        weight = w;
    }
    return self;
}
-(NSString*)description{
    return [NSString stringWithFormat:@"The human whose name is \"%@\". weight is [%f]. height is [%f]",
            self.name, self.weight, self.height];
}
-(float)height{
    return _height;
}
-(void)fly2{
    NSLog(@"yes I can fly in 2!");
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
    NSString* method = NSStringFromSelector(sel);
    if([method isEqualToString:@"setHeight:"]){
        class_addMethod([self class], sel, (IMP)dynamicMethod, "v@:f"); 
    }
    return [super resolveInstanceMethod:sel];
}
-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector{
    NSMethodSignature * signature = [super methodSignatureForSelector:selector];
    if(!signature&&[NSStringFromSelector(selector) isEqualToString:@"fly"]){
        //signature = [[self class] instanceMethodSignatureForSelector:@selector(fly2)];
        SEL newSel = NSSelectorFromString(@"fly1");  //will ok.
        
// SEL newSel = NSSelectorFromString(@"fly:");//will ok.
        
// SEL newSel = NSSelectorFromString(@"fly2:withBird");//will wrong.
        
//这里返回的消息,必须符合以下条件之一:
        
//方法名相同
        
//输入参数相同
        if([Plane instancesRespondToSelector:newSel]){
            signature = [Plane instanceMethodSignatureForSelector:newSel];
        }
    }
    return signature;    
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
    NSString * selName = NSStringFromSelector([anInvocation selector]);
    NSMethodSignature * sig = [anInvocation methodSignature];
    NSLog(@"the signature %@", [NSString stringWithCString:[sig methodReturnType] encoding:NSUTF8StringEncoding]);
    if([selName isEqualToString:@"fly"]){
        Plane * p = [[Plane alloc] init];
        SEL newSel = NSSelectorFromString(@"fly:");
        IMP imp = [p methodForSelector:newSel];
        imp(p, newSel, self);
        [p release];
    }
}
-(void)dealloc{
    [self setName:nil];
    [super dealloc];
}
@end

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "Human.h"
#import "Plane.h"
int main (int argc, const char * argv[])
{

    @autoreleasepool {
        
        // insert code here...
        
        Human * h = [[Human alloc] initWithWeight:17.3];
        h.name=@"Chris";
        h.height = 18.0;
        NSLog(@"%@", [h description]);
        [h fly];
        [h release];
        h=nil;

}} 

 

 

posted on 2012-03-13 20:38  一十一王  阅读(1725)  评论(0编辑  收藏  举报