Objective-C Runtime Messaging

  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, ...)
  1. 在objc_msgSend消息分发过程中,它首先查找方法体的实现,由于同样的方法可以由不同的类来实现,它的执行取决于接收器的类别。
  2. 然后调用这个函数,通过它的接受对象和携带的参数执行。
  3. 最后它将程序执行的返回值转为它自己的返回值。

 

二、消息分发运行图

消息分发运行图
 
    从消息分发的运行图,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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值