转自: http://my.oschina.net/amoyai/blog/92191
iOS的消息机制和动态绑定
Class SQSYAPIProtalClass = NSClassFromString(@"SQSYAPIProtal");
SEL initFunc = NSSelectorFromString(@"initSQSYSDKWithGameID:gameKey:");
if (dict)
{
NSString* configGameID = [dict objectForKey:@"GameID"];
NSString* configGameKey = [dict objectForKey:@"GameKey"];
((void(*)(id, SEL,NSString*, NSString*))objc_msgSend)(SQSYAPIProtalClass, initFunc, configGameID, configGameKey);
}
else
{
((void(*)(id, SEL,NSString*, NSString*))objc_msgSend)(SQSYAPIProtalClass, initFunc, gameID, gamekey);
}
// 回调
if ([self.beginRefreshingTaget respondsToSelector:self.beginRefreshingAction]) {
((void(*)(id, SEL, id))objc_msgSend)(self.beginRefreshingTaget, self.beginRefreshingAction, self);
}
最近见到这两段代码,对这个objc_msgSend这个C类型的方法相当陌生,但是望文生义, objc msg send, 就知道是和消息机制有关的方法。
在:
/**
* Sends a message with a simple return value to an instance of a class.
*
* @param self A pointer to the instance of the class that is to receive the message.
* @param op The selector of the method that handles the message.
* @param ...
* A variable argument list containing the arguments to the method.
*
* @return The return value of the method.
*
* @note When it encounters a method call, the compiler generates a call to one of the
* functions \c objc_msgSend, \c objc_msgSend_stret, \c objc_msgSendSuper, or \c objc_msgSendSuper_stret.
* Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper;
* other messages are sent using \c objc_msgSend. Methods that have data structures as return values
* are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.
*/
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
看头文件的文档注释,函数作用看不懂,但是参数的类型可以大体看得懂:
分别是:1.一个类的实例的指针来接收这个消息 2.一个操作方法来处理这个消息 3.该方法的一个参数列表
其中前两项是必选的: 接收者 和 方法名
[self justTestLog];
- (void)justTestLog
{
<span style="white-space:pre"> </span>NSLog(@"just Test Log");
}
例如上面非常简单的一个接受者 和 方法名 [self justTestLog]
在objc_msgSend方法中,就会按照参数格式转换为:objc_msgSend(self, NSSelectorFromString(@"justTestLog") );
如果是有参数的情况下,例如:
[self justTestLog:@"lxt" and:@"man"];
- (void)justTestLog:(NSString* )name andSex:(NSString* )sex
{
NSLog(@"just Test Log and :%@ and :%@", name, sex);
}
这个时候就会加上参数列表:
objc_msgSend( self, NSSelectorFromString(@"justTestLog: andSex:") , name, sex )
该方法会按照这样的操作顺序来完成动态绑定:
1.查找selector 即方法的实现来完成对receiver的操作 (不同的receiver对相同的selector可能会有着不同的实现效果,这个就是多态性)
2.如果没有参数,直接进行第三步,如果方法存在参数,则传递参数
3.使用selector在对应的receiver下的返回值作为自己的返回值,并返回它
消息传递的关键是,编译器构建每个类和对象时所采用的数据结构。每个类都包含以下两个必要元素:
1.一个指向父类的指针 2.一个调度表(dispatch table)。该调度表将类的selector与方法的实际内存地址关联起来
每个对象都有一个指向所属类的指针isa ( instance selector address )。通过该指针,对象可以找到它所属的类,也就找到了其全部父类:
当向一个对象发送消息时,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)只慢一点点点点。
这里还没完全理解,等日后再慢慢理解吧!