iOS 开发技巧大全

深入剖析OC Runtime(三) Message Forwa

2017-07-25  本文已影响25人  ShawnDu

深入剖析OC Runtime(三) Message Forward Demo
原文地址

objc_msgSend

objc_msgSend的格式如下:

void objc_msgSend(id self, SEL cmd, parameter...)

OC中所有的调用方法,属性赋值,都会转化为上面的C函数发送消息。找不到方法时,会走消息转发机制。

消息转发

先上图, 消息转发全流程

消息转发分为两大阶段。

示例

由开发者来添加属性的定义,并声明为@dynamic,此类会自动处理属性值的存放。
header中:

#import <Foundation/Foundation.h>

@interface SSAutoDictionary : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) NSNumber *number;
@end

上面定义了几种数据类型。
在实现文件中:

#import "SSAutoDictionary.h"
#import <objc/runtime.h>

@interface SSAutoDictionary()
@property (nonatomic, strong) NSMutableDictionary *backStore;
@end

@implementation SSAutoDictionary

@dynamic name, date, number;

- (id)init {
    if (self = [super init]) {
        _backStore = [NSMutableDictionary new];
    }
    return self;
}

假如只这样写,从外部访问属性的set和get方法时,都会找不到方法,所以引入了resolveInstanceMethod:

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSString *selectorString = NSStringFromSelector(sel);
    if ([selectorString hasPrefix:@"set"]) {
        class_addMethod(self, sel, (IMP)autoDictionarySetter, "v@:@");
    } else {
        class_addMethod(self, sel, (IMP)autoDictionaryGetter, "@@:");
    }
    return YES;
}

用前缀断定是否为set,两种情况下,都向类中新增一个处理该selector的子方法,这两个方法以函数指针形式出现autoDictionarySetter和autoDictionaryGetter。Getter方法:

id autoDictionaryGetter(id self, SEL _cmd) {
    SSAutoDictionary *mSelf = (SSAutoDictionary *)self;
    NSString *key = NSStringFromSelector(_cmd);
    return [mSelf.backStore objectForKey:key];
}

setter方法:

void autoDictionarySetter(id self, SEL _cmd, id value) {
    SSAutoDictionary *mSelf = (SSAutoDictionary *)self;
    NSString *selectorString = NSStringFromSelector(_cmd);
    NSMutableString *key = [selectorString mutableCopy];
    [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];
    [key deleteCharactersInRange:NSMakeRange(0, 3)];
    NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString];
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar];
    if (value) {
        [mSelf.backStore setObject:value forKey:key];
    } else {
        [mSelf.backStore removeObjectForKey:key];
    }
}

使用的时候:

    SSAutoDictionary *dict = [SSAutoDictionary new];
    dict.date = [NSDate new];
    NSLog(@"dict.date = %@", dict.date);

输出:

RuntimeDemo[61043:9339954] dict.date = 2017-07-25 13:19:22 +0000

源码地址

Demo:https://github.com/dulingkang/Runtime

原文地址

上一篇下一篇

猜你喜欢

热点阅读