Objective-C Runtime简介

OC是一种动态类型语言

不知道其他初学Objective-C的童鞋是不是也和我一样,对oc里面的函数调用感到有些陌生,首先它不像c语言或者c++或者java这些语言,将函数调用统称为“调函数”或者“调方法”,而是称之为“发消息”,其次,在oc中“调用”一些根本没有实现的方法时,它甚至也是可以通过编译的。

这两个问题的根源在于OC是一门动态类型的语言,动态与静态分别意味着对象的方法实现是在运行时和编译时决定。实际上面向对象语言的鼻祖Smalltalk就是一门动态类型的语言,而现代的主流编程语言如java,c++等都是静态类型。

而我们经常提到的runtime直接翻译出来就是“运行时”,而OC中的runtime特指runtime library,即一个包含了若干API的库,在官方的说明中是这样介绍runtime的:

You typically don't need to use the Objective-C runtime library directly when programming in Objective-C. 
This API is useful primarily for developing bridge layers between Objective-C and other languages, or for low-level debugging.

简意为一般人在用OC开发时并不需要用到runtime库,这个API主要是用于做OC与其他语言桥接开发,亦或是底层调试。

如何获取runtime的源代码

https://opensource.apple.com/source/objc4/

苹果将objc相关的代码在这里面开源,可以在里面找一个较新的版本下载阅读。

Objective-C是基于C的语言,Objective-C中各种对象在底层都有相应的C的实现。

实例/类/对象

实例/类/对象分别是instance/class/object,很多人容易混淆实例与对象的含义,实际上在面向对象的概念里万物皆对象,因此对象是OC中的一个基本数据结构,实例属于一种对象,类也是一种对象。

首先打开objc.h。

找到关于objc_object的声明部分。

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;
#endif

上面提到,Class是objc_class的一个别名,代表oc下的类对象。

objc_class我们稍后会了解,这里先看objc_object。

objc_object代表一个类的实例对象。它的结构非常简单,只有一个名为isa的成员,它代表一个指向其类对象的指针。(Stack Overflow上有人将isa这种命名解释为 “is a”,有一点道理,因为它代表了对象的类型)

id代表指向oc对象的指针。

打开runtime.h很快能在前面找到struct objc_class,这即是OC的对象在C中的实现,实际上是一个结构体,来看看其中比较关键的成员。

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

第一个成员即为isa指针,类对象的isa指针指向其元类(meta class)对象。

super_class,指向其父类对象的指针。

objc_ivar_list代表实例成员列表, ivar(instance variable)指实例变量。

objc_method_list代表方法列表。

objc_cache代表方法缓存。

objc_protocol_list代表协议列表。

方法

Method

method是我们很熟悉的函数概念,在oc中是一个名为objc_method的结构体。

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

struct objc_method {
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

值得注意的是SEL和IMP,我们在以target action形式设置回调时通常会要求我们传一个SEL类型的数据作为触发方法。

[view addTarget:self action:@selector(touchAction) forControlEvents:UIControlEventTouchUpInside];

就像这样,在@selector()中传入方法名即可得到一个SEL,那么SEL究竟是什么?根据官方文档的说明:

Defines an opaque type that represents a method selector.

typedef struct objc_selector *SEL;

它是一个objc_selector的封装,因此我们无法得知selector的内部实现,目前广泛认为SEL实际上是一个已经注册过的方法名的字符串,因为通过nslog对@selector打印出来的内容就是一个字符串。

下面是SEL的注册过程,即@selector()背后做的事情:

static SEL __sel_registerName(const char *name, bool shouldLock, bool copy) 
{
    SEL result = 0;

    if (shouldLock) selLock.assertUnlocked();
    else selLock.assertLocked();

    if (!name) return (SEL)0;

    result = search_builtins(name);
    if (result) return result;
    
    conditional_mutex_locker_t lock(selLock, shouldLock);
	auto it = namedSelectors.get().insert(name);
	if (it.second) {
		// No match. Insert.
		*it.first = (const char *)sel_alloc(name, copy);
	}
	return (SEL)*it.first;
}

static SEL search_builtins(const char *name) 
{
#if SUPPORT_PREOPT
  if (builtins) {
      SEL result = 0;
      if ((result = (SEL)builtins->get(name)))
          return result;

      if ((result = (SEL)_dyld_get_objc_selector(name)))
          return result;
  } else if (useDyldSelectorLookup) {
      if (SEL result = (SEL)_dyld_get_objc_selector(name))
          return result;
  }
#endif
    return nil;
}

种一棵树最好的时间是在十年前,而后是现在。

Loading Disqus comments...
Table of Contents