在 iOS中可以直接调用 某个对象的消息 方式有3种
1.performSelector:withObject:
2.NSInvocation
3.objc_msgSend
performSelector:withObject:
@interface MyClass : NSObject
@end
@implementation MyClass
-(NSString *)methodNoParameters{
NSLog(@"methodNoParameters");
return @"methodNoParameters";
}
-(NSString *)methodOneParameters:(NSString *)first{
NSLog(@"methodOneParameters:%@",first);
return @"methodOneParameters";
}
-(NSString *)methodTwoParameters:(NSString *)first two:(NSString *)two{
NSLog(@"methodTwoParameters:%@:%@",first,two);
return @"methodTwoParameters";
}
-(NSString *)methodThreeParameters:(NSString *)first two:(NSString *)two three:(NSString *)three{
NSLog(@"firstMethod:%@:%@:%@",first,two,three);
return @"methodThreeParameters";
}
- (void)viewDidLoad {
[super viewDidLoad];
[self callMethod];
// Do any additional setup after loading the view, typically from a nib.
}
<pre name="code" class="objc">-(void)callMethod{
MyClass *object = [[MyClass alloc] init];
NSString *returnValue = [object performSelector:@selector(methodNoParameters)];
NSLog(@"return:%@",returnValue);
returnValue = [object performSelector:@selector(methodOneParameters:) withObject:@"firstParermater"];
NSLog(@"return:%@",returnValue);
returnValue = [object performSelector:@selector(methodTwoParameters:two:) withObject:@"firstParermater" withObject:@"SecondParameter"];
NSLog(@"return:%@",returnValue);
}
2015-06-16 12:05:03.396 Demo[7877:138875] methodNoParameters
2015-06-16 12:05:03.396 Demo[7877:138875] return:methodNoParameters
2015-06-16 12:05:03.397 Demo[7877:138875] methodOneParameters:firstParermater
2015-06-16 12:05:03.397 Demo[7877:138875] return:methodOneParameters
2015-06-16 12:05:03.397 Demo[7877:138875] methodTwoParameters:firstParermater:SecondParameter
2015-06-16 12:05:03.397 Demo[7877:138875] return:methodTwoParameters
NSInvocation
NSInvocation可以处理参数、返回值。其实NSInvocation就相当于反射操作<pre name="code" class="objc">- (void)viewDidLoad {
[super viewDidLoad];
[self callInvocation];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)callInvocation{
MyClass *object = [[MyClass alloc] init];
NSMethodSignature *methodSig = [object methodSignatureForSelector:@selector(methodNoParameters)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setTarget:object];
[invocation setSelector:@selector(methodNoParameters)];
[invocation invoke];
methodSig = [object methodSignatureForSelector:@selector(methodThreeParameters:two:three:)];
invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setTarget:object];
[invocation setSelector:@selector(methodThreeParameters:two:three:)];
NSString *firstArgu = @"first";
[invocation setArgument:&firstArgu atIndex:2];
NSString *secondArgu = @"second";
[invocation setArgument:&secondArgu atIndex:3];
NSString *thirdArgu = @"third";
[invocation setArgument:&thirdArgu atIndex:4];
[invocation invoke];
[invocation retainArguments];
//get return value
const char *returnType = methodSig.methodReturnType;
id returnValue;
if (!strcmp(returnType,@encode(void))) {
returnValue = nil;
}else if (!strcmp(returnType, @encode(id))){
[invocation getReturnValue:&returnValue];
}else{
//如果返回值为普通类型NSInteger BOOL
//返回值长度
NSUInteger length = [methodSig methodReturnLength];
//根据长度申请内存
void *buffer = (void *)malloc(length);
//为变量赋值
[invocation getReturnValue:buffer];
if( !strcmp(returnType, @encode(BOOL))) {
returnValue = [NSNumber numberWithBool:*((BOOL*)buffer)];
}else if( !strcmp(returnType, @encode(NSInteger)) ){
returnValue = [NSNumber numberWithInteger:*((NSInteger*)buffer)];
}else{
returnValue = [NSValue valueWithBytes:buffer objCType:returnType];
}
free(buffer);
}
NSLog(@"return:%@",returnValue);
}
2015-06-16 12:05:28.936 Demo[7877:138875] methodNoParameters
2015-06-16 12:05:28.936 Demo[7877:138875] firstMethod:first:second:third
2015-06-16 12:05:28.937 Demo[7877:138875] return:methodThreeParameters
objc_msgSend
在Objective-C中,message与方法的真正实现是在执行阶段绑定的,而非编译阶段。编译器会将消息发送转换成对objc_msgSend方法的调用。
objc_msgSend方法含两个必要参数:receiver、方法名(即:selector),如:
[receiver message]; 将被转换为:objc_msgSend(receiver, selector);
objc_msgSend方法也能hold住message的参数,如:
objc_msgSend(receiver, selector, arg1, arg2, …);
objc_msgSend方法会做按照顺序进行以下操作,以完成动态绑定:
- 查找selector所指代的程序(方法的真正实现)。因为不同类对同一方法有不同的实现,所以对方法的真正实现的查找依赖于receiver的类
- 调用该实现,并将一系列参数传递过去
- 将该实现的返回值作为自己的返回值,返回之
消息传递的关键是,编译器构建每个类和对象时所采用的数据结构。每个类都包含以下两个必要元素:
- 一个指向父类的指针
- 一个调度表(dispatch table)。该调度表将类的selector与方法的实际内存地址关联起来
每个对象都有一个指向所属类的指针isa。通过该指针,对象可以找到它所属的类,也就找到了其全部父类,如下图所示:
当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表(dispatch table)中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针找到父类,并在父类的调度表(dispatch table)中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的内存地址调用该实现。
通过这种方式,message与方法的真正实现在执行阶段才绑定。
为了保证消息发送与执行的效率,系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的缓存,缓存包含有当前类自己的 selector以及继承自父类的selector。查找调度表(dispatch table)前,消息发送系统首先检查receiver对象的缓存。
缓存命中的情况下,消息发送(messaging)比直接调用方法(function call)只慢一点点点点。
关于Selector,什么 是Selector,Selector就是一个字符串,用来表示一个方法。
在Objective-C中,消息是直到运行的时候才和方法实现绑定的。编译器会把一个消息表达式,
[receiver message]
转换成一个对消息函数objc_msgSend的调用。该函数有两个主要参数:消息接收者和消息对应的方法名字——也就是方法选标:
objc_msgSend(receiver, selector)
objc_msgSend(receiver, selector, arg1, arg2, ...)
该消息函数做了动态绑定所需要的一切:
-
-
然后将消息接收者对象(指向消息接收者对象的指针)以及方法中指定的参数传给找到的方法实现。
-
最后,将方法实现的返回值作为该函数的返回值返回。
<span style="font-family:SimSun;font-size:14px;">- (void)viewDidLoad {
[super viewDidLoad];
[self callMsgSend];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)callMsgSend{
MyClass *object = [[MyClass alloc] init];
NSString *returnValue = objc_msgSend(object,@selector(methodThreeParameters:two:three:),@"first",@"second",@"third");
NSLog(@"return:%@",returnValue);
}</span>
<pre name="code" class="objc"><span style="font-family:SimSun;font-size:14px;">2015-06-16 12:06:54.885 Demo[7877:138875] firstMethod:first:second:third
2015-06-16 12:06:54.885 Demo[7877:138875] return:methodThreeParameters</span>
- 查找selector所指代的程序(方法的真正实现)。因为不同类对同一方法有不同的实现,所以对方法的真正实现的查找依赖于receiver的类
- 调用该实现,并将一系列参数传递过去
- 将该实现的返回值作为自己的返回值,返回之
消息传递的关键是,编译器构建每个类和对象时所采用的数据结构。每个类都包含以下两个必要元素:
- 一个指向父类的指针
- 一个调度表(dispatch table)。该调度表将类的selector与方法的实际内存地址关联起来
每个对象都有一个指向所属类的指针isa。通过该指针,对象可以找到它所属的类,也就找到了其全部父类,如下图所示:
当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表(dispatch table)中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针找到父类,并在父类的调度表(dispatch table)中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的内存地址调用该实现。
通过这种方式,message与方法的真正实现在执行阶段才绑定。
为了保证消息发送与执行的效率,系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的缓存,缓存包含有当前类自己的 selector以及继承自父类的selector。查找调度表(dispatch table)前,消息发送系统首先检查receiver对象的缓存。
缓存命中的情况下,消息发送(messaging)比直接调用方法(function call)只慢一点点点点。
关于Selector,什么 是Selector,Selector就是一个字符串,用来表示一个方法。
在Objective-C中,消息是直到运行的时候才和方法实现绑定的。编译器会把一个消息表达式,
[receiver message] |
转换成一个对消息函数objc_msgSend的调用。该函数有两个主要参数:消息接收者和消息对应的方法名字——也就是方法选标:
objc_msgSend(receiver, selector) |
objc_msgSend(receiver, selector, arg1, arg2, ...) |
该消息函数做了动态绑定所需要的一切:
-
然后将消息接收者对象(指向消息接收者对象的指针)以及方法中指定的参数传给找到的方法实现。
-
最后,将方法实现的返回值作为该函数的返回值返回。
<span style="font-family:SimSun;font-size:14px;">- (void)viewDidLoad {
[super viewDidLoad];
[self callMsgSend];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)callMsgSend{
MyClass *object = [[MyClass alloc] init];
NSString *returnValue = objc_msgSend(object,@selector(methodThreeParameters:two:three:),@"first",@"second",@"third");
NSLog(@"return:%@",returnValue);
}</span>
<pre name="code" class="objc"><span style="font-family:SimSun;font-size:14px;">2015-06-16 12:06:54.885 Demo[7877:138875] firstMethod:first:second:third
2015-06-16 12:06:54.885 Demo[7877:138875] return:methodThreeParameters</span>