目录

Class, Object 与 isa

作为 Runtime 系列的第一篇博文(也是 2022 年第一篇),先从最基础的对象、类开始学习。

参考 OC 编译过程 我们可以借助 clang 工具将 OC 源码预处理成 CPP 来窥探 OC 的内部实现。

定义一个简单的类:

@interface Foo : NSObject

@property (nonatomic, assign) BOOL ppty1;
@property (nonatomic, copy) NSString *ppty2;

- (BOOL)iMethod1;

- (void)iMethod2:(NSString *)p1;

+ (void)cMethod1;

@end

通过命令 clang -rewrite-objc xx.m 得到一个 9 万多行的 cpp 文件 😱

https://gist.github.com/ryderfang/75a852f1ae0961aeea97127f7d9ca6c5

太长不看版:

🧸 objc_object 是所有实例对象的底层结构,内部只有一个 isa

🎁 objc_class 是所有类/元类对象的底层结构,它也有一个 isa 指针,指向自己的元类

OC 1.0 OC 2.0
id
实例
typedef struct objc_object *id; typedef struct objc_object *id;
objc_object struct objc_object {} struct objc_object {}
Class
typedef struct objc_class *Class; typedef struct objc_class *Class;
objc_class struct objc_class {} struct objc_class : objc_object {}

<objc/objc.h> 中有这样一行:

typedef struct objc_class *Class;

在 iOS 15 之前 <objc/runtime.h> 中曾经有 objc_class 的定义:

https://opensource.apple.com/source/objc4/objc4-750/runtime/runtime.h.auto.html

/* Types */

#if !OBJC_TYPES_DEFINED

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 *` */

#endif

目前我使用的 Xcode 13.2.1 中已经找不到这个定义,也就是说在 macOS 12.1 / iOS 15.2 中,OBJC1 已经被完全废弃。

Hint: 按照官方文档 Runtime Version 的解释, legacy 版本 (OC 1.0) 在 2007 年就完全废弃了,所有 iPhone 和 OSX 10.5 以后的 Mac 上使用的都是 modern 版本 (OC 2.0)!

根据 wiki 的说明,OC 2.0 于 2006 年发布,而 iPhone 第一代 是在 2007 年发布的,Mac OS X 10.5 Leopard 也是在 2007 年上市。

从这个宏 OBJC2_UNAVAILABLE 可以看出来 OBJC2 这种定义会失效。那么 OBJC2 是什么?

objc-runtime-new.h 中定义了 objc_class 的 2.0 版本:

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    ...
}

参考 12

了解到,苹果在 2006 年重写了 runtime,发布了新的 Runtime 2.0,那么 __OBJC2__ 这个宏是什么时候生效的呢?

通过查找苹果公开的 runtime 源码:https://opensource.apple.com/source/objc4/ 发现:

objc4-750 这个版本开始,在 objc-config.h 中开始有了 __OBJC2__ 的定义!

// Define __OBJC2__ for the benefit of our asm files.
#ifndef __OBJC2__
#   if TARGET_OS_OSX  &&  !TARGET_OS_IOSMAC  &&  __i386__
        // old ABI
#   else
#       define __OBJC2__ 1
#   endif
#endif

来自 apple 的 commit:

https://ryder-1252249141.cos.ap-shanghai.myqcloud.com/uPic/2022-01-13-WGJpaA.png

https://github.com/apple-oss-distributions/objc4/commit/26c7408b94ead1f04a0b5976e354a03966ce61ea

objc-api.h 中定义了 OBJC_ISA_AVAILABILITY:

/* OBJC_ISA_AVAILABILITY: `isa` will be deprecated or unavailable 
 * in the future */
#if !defined(OBJC_ISA_AVAILABILITY)
#   if __OBJC2__
#       define OBJC_ISA_AVAILABILITY  __attribute__((deprecated))
#   else
#       define OBJC_ISA_AVAILABILITY  /* still available */
#   endif
#endif

objc-private.h 中同时定义了 OBJC_TYPES_DEFINED:

/* Isolate ourselves from the definitions of id and Class in the compiler 
 * and public headers.
 */

#ifdef _OBJC_OBJC_H_
#error include objc-private.h before other headers
#endif

#define OBJC_TYPES_DEFINED 1

同时也定义了 Class 和 id

typedef struct objc_class *Class;
typedef struct objc_object *id;

所以,不管 OC 1.0 还是 2.0,

Class 都是一个叫 objc_class 的结构体指针。

<objc/objc.h> 中,定义

https://opensource.apple.com/source/objc4/objc4-750/runtime/objc.h.auto.html

#if !OBJC_TYPES_DEFINED
/// 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

同样被包在 OBJC_TYPES_DEFINED 宏中,也就是 OC 2.0 并不生效。

那么,OC 2.0 的 objc_object 是什么样的呢,在 objc-private.h 中定义了:

struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // 省略大量方法
private:
    void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);

    // 省略大量方法
};

可以看出来不管 OC 1.0 还是 2.0,

objc_object 都是一个包含着一个叫 isa 指针/变量 的对象。

在 1.0 版本中 isa 就是一个 Class 对象,也就是一个指向 objc_class 的指针,而在 2.0 时,isa 是一个叫 isa_t 的结构体变量。

那么 isa_t 是什么呢?

同样在 objc-private.h 中有定义:

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

// TODO


  1. https://halfrost.com/objc_runtime_isa_class/ ↩︎

  2. https://kangzubin.com/objc1.0-class-object/ ↩︎

人生如逆旅,我亦是行人
人生如逆旅,我亦是行人

Комментарии