Objective-C语言的许多决策可以在编译和运行时执行。只要有可能,它是动态的。这意味着Objective-C语言不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。Runtime系统是一种用于Objective-C语言的操作系统,它使OC语言工作起来。
Runtime的核心是在运行时动态操作类和消息分发给其他对象,本文档主要介绍在运行时的消息分发机制。
你可以从中学到消息分发。多数情况下,我们在动态绑定方法时就需要使用到它。
消息分发机制的核心是objc_msgSend
一、消息分发简介
当编译器遇到一个方法调用时,编译器会根据接受端、返回值和参数调用一个或多个函数来实现。此时,在Runtime中就运用到了消息分发机制,它动态绑定一切函数。
在Objective-C中,方法直到运行的时候才会调用。编译器会接受到[receiver message]
的方法调用,此时运用objc_msgSend完成此方法的调用。objc_msgSend会根据其接收器和选择器将其转为
objc_msgSend(receiver, selector)
在消息传递过程中,任何的参数也会一起携带:
objc_msgSend(receiver, selector, arg1, arg2, ...)
- 在objc_msgSend消息分发过程中,它首先查找方法体的实现,由于同样的方法可以由不同的类来实现,它的执行取决于接收器的类别。
- 然后调用这个函数,通过它的接受对象和携带的参数执行。
- 最后它将程序执行的返回值转为它自己的返回值。
二、消息分发运行图
从消息分发的运行图,objc_msgSend找到接收器,接收器不接受此方法的执行时,传递给父类,当父类接受到这个消息并执行时,消息分发机制就结束了。
消息分发的设计模式主要是责任链模式 (COR),在IOS开发过程中随处可见这种模式的运用,如点击屏幕的手势响应。
三、消息分发的代码实现
1.开启消息分发模式
2.模型类
User.h
//
// User.h
// Runtime
//
// Created by yangjun on 15/9/21.
// Copyright © 2015年 六月. All rights reserved.
//
#import <Foundation/Foundation.h>
/// 用户
@interface User : NSObject
@property (nonatomic, copy) NSString *userName;///< 用户名
/**
* 初始化
*
* @param userName 用户名
*
* @return id
*/
- (id)initWithUserName:(NSString *)userName;
/**
* 初始化
*
* @param userName 用户名
*
* @return id
*/
+ (id)userWithUserName:(NSString *)userName;
@end
User.m
//
// User.m
// Runtime
//
// Created by yangjun on 15/9/21.
// Copyright © 2015年 六月. All rights reserved.
//
#import "User.h"
@implementation User
+ (id)userWithUserName:(NSString *)userName
{
User *user = [[User alloc] init];
user.userName = userName;
return userName;
}
#pragma mark 初始化
- (id)initWithUserName:(NSString *)userName
{
self = [super init];
if (self) {
self.userName = userName;
}
return self;
}
@end
子类SuperUser.h
//
// SuperUser.h
// Runtime
//
// Created by yangjun on 15/9/23.
// Copyright © 2015年 六月. All rights reserved.
//
#import "User.h"
/** 超级用户*/
@interface SuperUser : User
@property (nonatomic, readonly) NSInteger ID;///< 超级用户的ID
@end
SuperUser.m
//
// SuperUser.m
// Runtime
//
// Created by yangjun on 15/9/23.
// Copyright © 2015年 六月. All rights reserved.
//
#import "SuperUser.h"
@implementation SuperUser
- (id)initWithUserName:(NSString *)userName
{
self = [super initWithUserName:userName];
if (self) {
_ID = 8888;
}
return self;
}
@end
3.测试代码
MsgSendTest.h
//
// MsgSendTest.h
// Runtime
//
// Created by yangjun on 15/9/23.
// Copyright © 2015年 六月. All rights reserved.
//
#import <Foundation/Foundation.h>
/** 消息分发测试*/
@interface MsgSendTest : NSObject
- (void)sendTest;
@end
MsgSendTest.m
//
// MsgSendTest.m
// Runtime
//
// Created by yangjun on 15/9/23.
// Copyright © 2015年 六月. All rights reserved.
//
#import "MsgSendTest.h"
#import "SuperUser.h"
#import <objc/message.h>
@implementation MsgSendTest
#pragma mark 消息发送
- (void)sendTest
{
User *user = [[User alloc] init];
user.userName = @"IOS";
NSLog(@"%@", user.userName);
// 等价
[user performSelector:@selector(setUserName:) withObject:@"performSelector"];
NSLog(@"%@", user.userName);
#if OBJC_OLD_DISPATCH_PROTOTYPES // Runtime时
// objc_msgSend发送一个消息并返回一个id类型的数据
fprintf(stdout, "\nobjc_msgSend\n");
objc_msgSend(user, @selector(setUserName:), @"objc_msgSend");
NSLog(@"%@", objc_msgSend(user, @selector(userName)));
// objc_msgSend_fpret只能在i386处理器上运行,否则返回NaN
fprintf(stdout, "\nobjc_msgSend_fpret\n");
double sendFpret = objc_msgSend_fpret(self, @selector(getDouble));
NSLog(@"%f", sendFpret);
// objc_msgSendSuper和objc_msgSend一样,主要是调用父类的方法的。
SuperUser *sUser = [[SuperUser alloc] init];
fprintf(stdout, "\nobjc_msgSend_stret\n");
struct objc_super objcSuper = {sUser, objc_getClass("User")};
objc_msgSendSuper(&objcSuper, sel_registerName("setUserName:"), @"objc_msgSendSuper");
NSLog(@"%@", objc_msgSendSuper(&objcSuper, sel_registerName("userName")));
#endif
}
#pragma mark 返回doule类型数据
- (double)getDouble
{
return 24;
}
@end
4. 运行输出
2015-09-23 15:17:25.978 Runtime[16665:800014] IOS
2015-09-23 15:17:25.979 Runtime[16665:800014] performSelector
objc_msgSend
2015-09-23 15:17:25.979 Runtime[16665:800014] objc_msgSend
objc_msgSend_fpret
2015-09-23 15:17:25.980 Runtime[16665:800014] nan
objc_msgSend_stret
2015-09-23 15:17:25.980 Runtime[16665:800014] objc_msgSendSuper
Program ended with exit code: 0
源代码下载:http://download.csdn.net/detail/y550918116j/9133687
参考文献
Objective-C Runtime Reference
Objective-C Runtime Programming Guide