runtime 理解

大家知道OC是一门动态语言,他将一些静态语言以及库的链接的一些事情放在运行时来进行处理,这就给我们增加了编程的灵活性,比如可以随意交换两个方法的执行,消息的转发等等。这就是Runtime运行时。这个库全部由C语言来编写的,该对象用结构体来表示,方法使用C函数来表示。当这些结构体以及方法用runtime函数来修饰封装后,我们就可以对类和对象进行创建、检查、修改等操作了。

1、基本结构

/// An opaque type that represents a method in a class definition.类中定义的方法

typedef struct objc_method *Method;

/// An opaque type that represents an instance variable.类中定义的实例变量

typedef struct objc_ivar *Ivar;

/// An opaque type that represents a category.类别

typedef struct objc_category *Category;

/// An opaque type that represents an Objective-C declared property.类中声明的属性

typedef struct objc_property *objc_property_t;


struct objc_class {

    Class isa  OBJC_ISA_AVAILABILITY; isa指针,通过它可以找到对象所述的类,isa指针在代码运行时并不总是指向实例所属的类,因此不能通过其获取类型,但是可以通过‘’-class来确定。KVO的实现机理就是通过isa指针指向一个中间类而不是真实类来实现的。


#if !__OBJC2__

    Class super_class                                        OBJC2_UNAVAILABLE;

    constchar *name                                         OBJC2_UNAVAILABLE;   ------类名

    long version                                             OBJC2_UNAVAILABLE;

    long info                                                OBJC2_UNAVAILABLE;

    long instance_size                                       OBJC2_UNAVAILABLE;

    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;  ------属性列表

    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;------方法列表

    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;------缓存,一种优化机制

    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;------协议列表

#endif

} OBJC2_UNAVAILABLE;

/* Use `Class` instead of `struct objc_class *` */

2、常用的获取列表的属性

unsigned int count;
    //获取属性列表
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]);
    }

    //获取方法列表
    Method *methodList = class_copyMethodList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Method method = methodList[i];
        NSLog(@"method---->%@", NSStringFromSelector(method_getName(method)));
    }

    //获取成员变量列表
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Ivar myIvar = ivarList[i];
        const char *ivarName = ivar_getName(myIvar);
        NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
    }

    //获取协议列表
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Protocol *myProtocal = protocolList[i];
        const char *protocolName = protocol_getName(myProtocal);
        NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
    }
3、消息转发

在OC中调用方法一般是如下调用

id returnValue = [someObject messageName:parameter];,someObject是消息的接受者,messageName是一个选择器,parameter则为参数。选择器+参数 就是我们所称为的消息。

其在底层中是将我们的消息转换为标准的C语言函数,Void objc_msgSend(id self , SEL cmd , .....)  ,self 为消息接收者,cmd为选择器,省略号为参数,表示可变长度参数。因此以上的消息方法会转换为id returnValue = objc_msgSend( someObject , @selector(messageName:) , parameter);

之所以Objc_msgsend方法总能找到正确的函数去执行,原因是OC中每个类都有一张方法的列表存储这个类的所有方法,当发出objc_msgsend时,会根据object的isa指针找到类结构的方法,如果找不到则会到父类寻找该方法的实现,直到NSObject类。上面有提到cach缓存机制,苹果为了加快寻找速率,runtime 系统会缓存使用过的SEL 和方法地址。

  • 通过object的isa指针找到他的类结构class
  • 在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行(IMP)
  • 如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行(IMP)
  • 如果还未找到则会去父类中重复上面操作寻找
  • 如果最终还是未找到,则会调用实例方法调用+(bool)resolveInstanceMethod:(SEL)sel ;类方法调用:+(bool)resolveClassMethod:(SEL)sel;如果返回为YES,则会重启一次消息发送过程,调用你自己添加的方法。

+ (BOOL)resolveInstanceMethod:(SEL)sel

{

    if (sel ==@selector(sendMessage:)) {

        class_addMethod([selfclass], sel, imp_implementationWithBlock(^(idself, NSString *word) {

            NSLog(@"method resolution way : send message = %@", word);

        }), "v@*");

    }

    returnYES;

}

- (void)sendMessage:(NSString *)word

{

    NSLog(@"normal way : send message = %@", word);

}

如果返回NO,则会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法,再次方法中可以将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target,

- (id)forwardingTargetForSelector:(SEL)aSelector

{

    if (aSelector ==@selector(sendMessage:)) {

        return [MessageForwardingnew];//新建的类,见下

    }

    returnnil;

}

#import <Foundation/Foundation.h>

@interface MessageForwarding :NSObject

- (void)sendMessage:(NSString *)word;

@end

如果return nil,则会- (void)forwardInvocation:(NSInvocation *)anInvocation将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法。

- (void)forwardInvocation:(NSInvocation *)anInvocation

{

    MessageForwarding *messageForwarding = [MessageForwardingnew];

    if ([messageForwardingrespondsToSelector:anInvocation.selector]) {

        [anInvocation invokeWithTarget:messageForwarding];

    }

}

#import <Foundation/Foundation.h>

@interface MessageForwarding :NSObject

- (void)sendMessage:(NSString *)word;

@end

4、runtime实际应用

更改私有属性 、给category添加属性、归档解档、AOP等等,具体事例请看本博客其他文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值