Runtime #1 Overview

2017-06-22  本文已影响9人  Karl87

使用场景


定义

Objective C语言把能在运行期做的事情就推迟到运行期再决定。这就意味着,Objective C不仅需要一个编译器,而且需要一个运行期环境。这个运行期环境就是Runtime。

  typedef struct objc_selector *SEL
  typedef struct objc_object *id;
  struct objc_object{
    Class isa OBJC_ISA AVAILABILITY;
}
  typedef struct objc_class *Class;
    struct objc_class {
          Class isa  OBJC_ISA_AVAILABILITY;
      #if !__OBJC2__
          Class super_class                                    OBJC2_UNAVAILABLE;
          const char *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 *` */
    #if !OBJC_OLD_DISPATCH_PROTOTYPES
          typedef void (*IMP)(void /* id, SEL, ... */ ); 
    #else
          typedef id (*IMP)(id, SEL, ...); 
    #endif
  typedef struct objc_method *Method
 struct objc_method {
        SEL method_name                        OBJC2_UNAVAILABLE;
        char *method_types                     OBJC2_UNAVAILABLE;
        IMP method_imp                           OBJC2_UNAVAILABLE;
    }   
    typedef struct objc_ivar *Ivar;
//objc_ivar 结构体
struct objc_ivar {
    char *ivar_name                                          OBJC2_UNAVAILABLE;
    char *ivar_type                                          OBJC2_UNAVAILABLE;
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}    

消息机制

ObjC中的实例方法

//oc中调用方法
[receiver doSomething];

//编译后
objc_msgSend (receiver, selector)

objc_msgSend:

id objc_msgSend (id self, SEL op, ...)

Runtime找到方法执行体的过程:

ObjC中的类方法

实例对象又类对象创建,类对象保存了实例对象的方法列表。
objc_class中的isa指向的Class类型中保存类方法。Class类型即类元对象。

类对象由类元对象(meta class)创建,类元对象保存类类对象的类方法,类名字等信息。类元对象和类对象都是Class类型,只不过服务的对象不同。类元对象由NSObject类元对象创建。

Class class = [CustomObject class];//类对象
Class metaClass = object_getClass(class);//类元对象
Class metaOfMetaClass = object_getClass(metaClass);//NSObject类元对象
Class rootMetaClass = object_getClass(metaOfMetaClass);//NSObject类元对象的类元对象

SuperClass

ObjC作为面向对象语言,其类的继承关系如下:
CustomObject --> NSObject --> nil (-->SuperClass)

ClassCache

为了避免每次调用方法都查询一次Method list的分发表(dispatch table)于是就引入了Class Cache.

Class Cache认为,当一个方法被调用,那么它之后被调用的可能性比较大。

已alloc和init方法为例:

CustomObject *obj = [[CustomObject alloc] init];

alloc方法,初始化isa,其他所有属性设为0
NSObject的init方法返回self,其余子类要用super init进行必要的初始化工作。

alloc和init可能返回不同的对象

NSObject

NSObject类除了定义一些基本方法,如description、alloc等。
也定义了一些Runtime的基础方法,从isa和superclass的流程可知,NSObject是整个消息机制的核心。

//method swizzling核心方法
+ initialize //一个类接受第一条消息之前
+ load //一个类加载到Runtime时调用,一次

//检查是否可以想实例对象发送某条消息
+(BOOL) instanceRespondToSelector:(SEL)aSelector
- respondToSelector:

//向对象发送消息
- (id) performSelector:(SEL) aSelector
- performSelector:withObject:
- performSelector:withObject:withObject:
...

//动态消息转发处理机制
+ resolveInstanceMethod:
- forwardingTargetForSelector:
- forwardInvocation:

对象无法处理实例方法

在ObjC中,对一个对象发送一个它没有实现的Selector是完全合法的,这样做可以隐藏某一个消息背后实现,也可以模拟多继承(OC不支持多继承)。这个机制就是动态转发机制。

//动态消息转发处理机制
+ resolveInstanceMethod:
- forwardingTargetForSelector:
- forwardInvocation:

动态为实例方法提供一个实现。这个方法在Objective C消息转发机制之前被调用。如果 respondsToSelector或者instancesRespondToSelector: 被调用,可以为改Selector提供动态的实现者。

调用未声明的实例方法(动态添加实例方法)

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

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [self performSelector:@selector(dynamicSelector) withObject:nil];
#pragma clang diagnostic pop

}

void myMehtod(id self,SEL _cmd){
    NSLog(@"This is added dynamic");
}

+(BOOL)resolveInstanceMethod:(SEL)sel{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    if (sel == @selector(dynamicSelector)) {
#pragma clang diagnostic pop
        class_addMethod([self class],sel, (IMP)myMehtod,"v@:");
        return YES;
    }else{
        return [super resolveInstanceMethod:sel];
    }
}

@end

函数class_addMethod的作用是给一个类添加实例方法

BOOL class_addMethod (Class cls, SEL name, IMP imp, const char * types)

c char
i int
s short
l long // l is treated as a 32-bit quantity on 64-bit programs.
q A long long
C An unsigned char
I An unsigned int
S An unsigned short
L An unsigned long
Q An unsigned long long
f A float
d A double
B A C++ bool or a C99 _Bool
v A void
*A character string (char *)
@ An object (whether statically typed or typed id)
"#" -- A class object (Class)
":" -- A method selector (SEL)
"[array type]" -- An array
"{name=type...}" -- A structure
"(name=type...)" -- A union
bnum A bit field of num bits
^type A pointer to type
? An unknown type (among other things, this code is used for function pointers)

返回值YES:本类可以处理
NO:需要消息转发

#import "ViewController.h" 
#import <objc/runtime.h>
@interface CustomObject : NSObject
-(void)dynamicSelector;
@end
@implementation CustomObject

-(void)dynamicSelector{
    NSLog(@"hello world");
}
@end
@interface ViewController ()
@property (strong,nonatomic)CustomObject * myObj;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.myObj = [[CustomObject alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [self performSelector:@selector(dynamicSelector) withObject:nil];
#pragma clang diagnostic pop

}

-(id)forwardingTargetForSelector:(SEL)aSelector{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    if (aSelector == @selector(dynamicSelector) && [self.myObj respondsToSelector:@selector(dynamicSelector)]) {
        return self.myObj;
    }else{
        return [super forwardingTargetForSelector:aSelector];
    }
#pragma clang diagnostic pop

}
@end

*前两次无法处理时,调用forwardInvocation转发给其他类,返回值仍然返回给最初的Selector

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

@interface CustomObject : NSObject
-(void)dynamicSelector;
@end
@implementation CustomObject

-(void)dynamicSelector{
    NSLog(@"hello world");
}
@end
@interface ViewController ()
@property (strong,nonatomic)CustomObject * myObj;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.myObj = [[CustomObject alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [self performSelector:@selector(dynamicSelector) withObject:nil];
#pragma clang diagnostic pop

}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
    if ([self.myObj respondsToSelector:[anInvocation selector]]) {
        [anInvocation invokeWithTarget:self.myObj];
    }else{
        [super forwardInvocation:anInvocation];
    }
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [CustomObject instanceMethodSignatureForSelector:aSelector];
}
@end

消息转发机制使得OC可以进行”多继承”,比如有一个消息中心负责处理消息,这个消息中心很多个类都要用,继承或者聚合都不是很好的解决方案,使用单例看似可以,但单例的缺点也是很明显的。这时候,把消息转发给消息中心,无疑是一个较好的解决方案。

Runtime实用

增删改查类的属性和方法,协议,Block
如使用SEL属性动态执行方法

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

@interface ViewController ()
@property (nonatomic)SEL methodToInvoke;

@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    int a = arc4random() % 2;
    _methodToInvoke = NSSelectorFromString([[self numToSelector] objectForKey:@(a)]);
    #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [self performSelector:_methodToInvoke withObject:nil];
#pragma clang diagnostic pop
}
-(NSDictionary *)numToSelector{
    return @{@(0):@"method1",
             @(1):@"method2"};
}
-(void)method1{
    NSLog(@"method1");
}
-(void)method2{
    NSLog(@"method2");
}
@end
上一篇 下一篇

猜你喜欢

热点阅读