Runtime #1 Overview

使用场景

  • 动态改变方法
  • Method Swizzling
  • NSSelectorFromString、NSClassFromString
  • 动态添加方法
  • 动态绑定属性
  • 动态遍历属性和方法(数据转对象)
    ......

定义

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

  • selector:透明的数据结构,可以理解为C String

  • SEL:是指向一个C String的指针,方法名,字符串指针类型

  typedef struct objc_selector *SEL
  • id:指向一个类的实例对象
  typedef struct objc_object *id;
  • objc_object:保存了一个指向Objective-C对应类的指针
  struct objc_object{
    Class isa OBJC_ISA AVAILABILITY;
}
  • Class:指向Objective-C类对象(objc_class) 的一个指针
  typedef struct objc_class *Class;
  • objc_class: Objective-C类对象结构体
    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 *` */
  • IMP:指向实际执行函数体的函数指针,参数id是一个对象,消息接受者,SEL方法名
    #if !OBJC_OLD_DISPATCH_PROTOTYPES
          typedef void (*IMP)(void /* id, SEL, ... */ ); 
    #else
          typedef id (*IMP)(id, SEL, ...); 
    #endif
  • Method:指向Objc中方法的指针
  typedef struct objc_method *Method
  • objc_method:
 struct objc_method {
        SEL method_name                        OBJC2_UNAVAILABLE;
        char *method_types                     OBJC2_UNAVAILABLE;
        IMP method_imp                           OBJC2_UNAVAILABLE;
    }   
  • _cmd:SEL类型的一个变量,Objc函数的前两个隐藏参数为self和_cmd
  • Ivar:objc中的实例变量
    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:

  • self为消息的接收者
  • op为selector(字符串指针)
  • ...参数数组
id objc_msgSend (id self, SEL op, ...)

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

  • 调用者调用方法
  • objc_msgSend中的参数id self
  • 通过实例id self 的Class isa 找到其类对象objc_object
  • 通过objc_class的methodLists找到其方法列表
  • 在表中找到对应的方法执行体
  • 执行方法,返回值给调用者

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找到CustomObject类元对象,发现没有实现alloc
  • 沿着super,找到NSObject类元方法,执行alloc方法,并把alloc加入到NSObject类元对象的ClassCache中
  • init是实例方法,沿着isa找到了CustomObject的类对象,发现没有实现init
  • 沿着super,找到NSObject类对象,执行init,并把init加入到NSObject类对象的ClassCache中

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:
  • 首先类使用resolveInstanceMethod自己处理接受到的实例方法。

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

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

#import "ViewController.h"
#import 

@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)
  • cls 添加方法的类
  • name selector名称
  • imp 具体实现
  • 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:需要消息转发

  • 本类无法处理时,调用forwardingTargetForSelector方法,简单的将任务转发给另一个对象
#import "ViewController.h" 
#import 
@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 

@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 

@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
  • 动态改变方法的执行体
  • Method Swizzling
  • NSSelectorFromString,NSClassFromString…
  • 动态添加属性(主要是类别)
  • 动态遍历属性和方法,动态为类添加方法(写Model类的wrapper很有用)
  • 消息转发机制使得架构更容易

你可能感兴趣的:(Runtime #1 Overview)