objective-c 编程总结(第八篇)运行时操作 - 消息转发
第七篇中讲动态属性时,提到了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
#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
void dynamicMethod(id self, SEL _cmd, float height);
@interface Human : NSObject{
float _height;
}
@property(retain, nonatomic) NSString * name;
@property( readonly) float weight;
@property float height;
-(id) initWithWeight:( float)weight;
-(NSString*)description;
@end
#import
#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
#import
#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;
#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
void dynamicMethod(id self, SEL _cmd, float height);
@interface Human : NSObject{
float _height;
}
@property(retain, nonatomic) NSString * name;
@property( readonly) float weight;
@property float height;
-(id) initWithWeight:( float)weight;
-(NSString*)description;
@end
#import
#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
#import
#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 一十一王 阅读(
...) 评论(
...) 编辑 收藏