Swift Runtime - 类和对象

编译阶段

class PureSwiftClass {
    private var private_var_property = 0
    @objc private var objc_private_var_property = 0
    var instance_property = 0
    @objc let objc_instance_let_property = 0
    @objc var objc_instance_var_property = 0

    func instance_method() {}
    @objc func objc_instance_method() {}
    @objc dynamic func objc_dynamic_instance_method() {}
}

下面是编译阶段生成的类信息:

_$s10TestObjectSwiftClassCN:
struct __objc_class {
    _OBJC_METACLASS_$__TtC10TestObjectSwiftClass, // metaclass
    _OBJC_CLASS_$_SwiftObject, // superclass
    __objc_empty_cache, // cache
    0x0, // vtable
    __objc_class__TtC10TestObjectSwiftClass_data+1 // data
}

__objc_class__ObjectSwiftClass_data:
struct __objc_data {
    0x80, // flags
    8,// instance start
    48,                                  // instance size
    0x0,
    0x0,                                 // ivar layout
    "ObjectSwiftClass",                     // name
    __objc_class__TtC10TestObjectSwiftClass_methods, // base methods
    0x0,                                 // base protocols
    __objc_class__TtC10Test6ObjectSwiftClass_ivars, // ivars
    0x0,                                 // weak ivar layout
    __objc_class__TtC10TestObjectSwiftClass_properties // base properties
}

// methods
__objc_class__ObjectSwiftClass_methods:
struct __objc_method_list { 
    0x18,                                // flags
    8                                    // method count
}

struct __objc_method {                                 
    "objc_private_instance_var_property",                     // name
    "q16@0:8",                              // signature
    -[_TtC10TestObjectSwiftClass objc_private_instance_var_property] // implementation
}
struct __objc_method {                                 
    "setObjc_private_var_property:",                     // name
}
struct __objc_method {
    "objc_instance_var_property",                     // name
}
struct __objc_method {
    "setObjc_instance_var_property:",                     // name
}
struct __objc_method {                                 
    "objc_instance_let_property",                     // name
}
struct __objc_method {                                 
    "objc_instance_method",                     // name
}
struct __objc_method {                                 
    "objc_dynamic_instance_method",                     // name
}
struct __objc_method {                                
    "init",                               // name
}

// ivars
__objc_class__TtC10TestObjectSwiftClass_ivars:
struct __objc_ivars {                               
    32,                                  // entsize
    5                                    // count
}
struct __objc_ivar {                                   
    "private_var_property",                     // name
}
struct __objc_ivar {                                   
    "objc_private_var_property",           // name
}
struct __objc_ivar {                                   
    "instance_var_property",                     // name
}
struct __objc_ivar {                                   
    "objc_instance_var_property",           // name
}
struct __objc_ivar {                                   
    "objc_instance_let_property",           // name
}

根据上面编译器生成的数据,可以得到一些信息:

class

  • Swift类编译阶段会生成与Objective-C一样的类元数据,这也是为什么SwiftObjective-C可以互相调用。

泛型类不会生成类元数据__objc_class结构,不过会生成roData

  • class如果没有显式继承某个类,都被隐式继承SwiftObject

属性

  • 所有属性都会添加到class_ro_t中的ivars结构中,包括private属性。
  • 使用@objc修饰的属性,var属性会添加set/get方法,let属性只会添加get方法。

Swift类的 属性可以通过 objc-runtime进行修改和获取。

方法

  • 使用@objc修饰的方法会添加到ro_class_tmethods结构中。

Swift结构

ClassMetadata

ClassMetadataSwift中所有类元数据格式。

struct objc_object {
    Class isa;
}
struct objc_class: objc_object {
    Class superclass;
    cache_t cache;           
    class_data_bits_t bits;
}
struct swift_class_t: objc_class {
    uint32_t flags;//类标示
    uint32_t instanceAddressOffset;
    uint32_t instanceSize;//对象实例大小
    uint16_t instanceAlignMask;//
    uint16_t reserved;// 保留字段
    uint32_t classSize;// 类对象的大小
    uint32_t classAddressOffset;// 
    void *description;//类描述
};

SwiftObjective-C的类元数据是共用的,Swift类元数据只是Objective-C的基础上增加了一些字段。

源代码中也有一些地方直接使用 reinterpret_cast进行相互转换。

Class objcClass = [objcObject class];
ClassMetadata *classAsMetadata = reinterpret_cast<const ClassMetadata *>(objcClass);

HeapObject

Swift中,一个class对象实际上就是一个HeapObject结构体指针。HeapObjectHeapMetadataInlineRefCounts组成,HeapMetadata是类对象元数据的指针,InlineRefCounts用于管理引用计数。

struct HeapObject {
  HeapMetadata const *metadata;
  InlineRefCounts refCounts;
};
  • HeapMetadataObjective-C中的isa_t结构一样,使用ISA_MASK获取到类对象。
@interface ObjcClass: NSObject {
}

ObjcClass *objcObject = [ObjcClass new];
HeapObject *heapObject = static_cast<HeapObject *>(objcObject);
ObjcClass *objcObject2 =  static_cast<ObjcClass *>(heapObject);

[heapObject retain];

不过因为 Objective-CSwift引用计数管理方式不一样,所以转换以后依然要使用之前的方式进行引用计数管理。

Objective-CSwift对象结构:

Objc对象结构 {
    isa_t,
    实例变量
}
Swift对象结构 {
    metadata,
    refCounts, 
    实例变量
}

创建对象

swift_allocObject
  • swift_allocObject方法用于创建一个Swift对象。
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
  void *p;
  // This check also forces "default" alignment to use AlignedAlloc.
  if (alignMask <= MALLOC_ALIGN_MASK) {
    p = malloc(size);
  } else {
    size_t alignment = (alignMask == ~(size_t(0)))
                           ? _swift_MinAllocationAlignment
                           : alignMask + 1;
    p = AlignedAlloc(size, alignment);
  }
  if (!p) swift::crash("Could not allocate memory.");
  return p;
}
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
                                       size_t requiredSize,
                                       size_t requiredAlignmentMask) {
  auto object = reinterpret_cast<HeapObject *>(
      swift_slowAlloc(requiredSize, requiredAlignmentMask));
  // NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
  // check on the placement new allocator which we have observed on Windows,
  // Linux, and macOS.
  new (object) HeapObject(metadata);//创建一个新对象,
  return object;
}
  • 根据对象大小做字节对齐处理,之后调用malloc分配内存,之后会初始化实例变量。
  • metadata表示类对象元数据。
  • requiredSizerequiredAlignmentMask表示对象大小和字节对齐方式。
swift_initStackObject
  • 在某些场景对象创建会被编译器优化为swift_initStackObject方法。swift_initStackObject在栈上创建一个对象。没有引用计数消耗,也不用malloc内存。
HeapObject *
swift::swift_initStackObject(HeapMetadata const *metadata,
                             HeapObject *object) {
  object->metadata = metadata;
  object->refCounts.initForNotFreeing();
  return object;
}

销毁对象

swift_deallocClassInstance
  • swift_deallocClassInstance用于销毁对象,在对象dealloc时调用。
void swift::swift_deallocClassInstance(HeapObject *object,
                                       size_t allocatedSize,
                                       size_t allocatedAlignMask) {
#if SWIFT_OBJC_INTEROP
  objc_destructInstance((id)object);
#endif
  swift_deallocObject(object, allocatedSize, allocatedAlignMask);//
}
  • 调用objc_destructInstance方法释放关联对象和弱引用释放处理。

Objc runtime的对象弱引用,不是Swift环境的弱引用。

  • 调用swift_deallocObject方法调用free回收内存。

引用计数相关方法

  • swift_retainobjc的实现类似,对引用计数进行+1溢出时将一部分引用计数值保存到sideTable中。
  • swift_release对引用计数进行-1,当引用计数为0时,调用销毁对象方法。
  • swift_weak相关的方法用于管理weak弱引用。

SwiftObject

Swift中,一个class如果没有显式继承其他的类,都被隐式继承SwiftObjectSwiftObject实现了NSObject协议的所有方法和一部分NSObject类的方法。主要是重写了一部分方法,将方法实现改为Swift相关方法。

@interface SwiftObject<NSObject> {
 @private
  Class isa;
  InlineRefCounts refCounts;
}
@end

没有实现 resolveInstanceMethodforwardingTargetForSelector等方法,这些方法可以在找不到特定方法时可以进行动态处理,应该是不想提供纯 Swift类在这块的能力。

比如retainrelease方法改为了使用swift runtime进行引用计数管理:

- (id)retain {
  auto SELF = reinterpret_cast<HeapObject *>(self);
  swift_retain(SELF);
  return self;
}
- (void)release {
  auto SELF = reinterpret_cast<HeapObject *>(self);
  swift_release(SELF);
}

因为纯 Swift类不能直接与Objective-C交互,那么SwiftObject这样设计的目的是什么?

下面是两种使用场景:

  • 就是将纯Swift类作为id参数传递到Objective-C方法中使用。
- (void)test:(id)object {
  [object retain];
  [object performSelector:@selector(objc_instance_method)];
}
let object = NSObject()
test(object)
  • 使用消息发送的方式调用方法。
class SwiftClass {
    @objc dynamic func objc_dynamic_instance_method() {}
}
let object = SwiftClass()
object.objc_dynamic_instance_method()

不过以上场景应该是很少使用的,不清楚还有没有其它目的。而且这样设计的话,纯Swift类也应该可以被Objective-C直接使用。

初始化对象

Objective-C

Objective-C使用Swift-NSObject子类

class SwiftClass: NSObject {
}
SwiftClass *object = [[SwiftClass alloc] init];
  • 因为二进制文件中Swift类包含了和Objective-C一样的类数据信息,所以可以直接使用Objective-C的方式创建。

Swift

Swift类

创建一个纯Swift类对象。

class SwiftClass {
}
SwiftClass()
swift_getInitializedObjCClass
Class swift::swift_getInitializedObjCClass(Class c) {
  [c self];// 为了确保objc-runtime realize class
  return c;
}
Class objcClass = swift_getInitializedObjCClass(SwiftClass);
HeapObject *object = swift_allocObject(objcClass);
// 释放
swift_release(object);

原生Objective-C

创建一个原生Objective-C类对象。

@interface ObjectClass
@end
ObjectClass()
Class objcClass = swift_getInitializedObjCClass(ObjectClass);
Metadata *metadata = swift_getObjCClassMetadata(objcClass);
ClassMetadata *classMetadata = swift_getObjCClassFromMetadata(metadata);
ObjectClass *object = [classMetadata allocWithZone] init];
// 释放
objc_release(object);

swift_getObjCClassMetadataswift_getObjCClassFromMetadata有什么作用?

Swift-NSObject子类

创建一个Swift-NSObject子类对象。

class SwiftClass: NSObject {
}
SwiftClass()
Class objcClass = swift_getInitializedObjCClass(SwiftClass);
HeapObject *object = objc_allocWithZone(objcClass);
// 释放
objc_release(object);

Swift泛型类

创建一个Swift泛型类对象。

class GenericClass<T> {
}
GenericClass<Int>()
MetadataResponse response = swift_getGenericMetadata();
ClassMetadata *classMetadata = swift_allocateGenericClassMetadata();
swift_initClassMetadata2(classMetadata);
HeapObject *object = swift_allocObject(objcClass);
  • 根据泛型类型作为参数,调用swift_getGenericMetadata方法获取类对象缓存。存在缓存直接返回,没有缓存,调用swift_allocateGenericClassMetadata方法。

每一个不同的泛型类型都会创建一个新的ClassMetadata,之后保存到缓存中复用。

swift_allocateGenericClassMetadata:

  • 创建一个新的ClassMetadata结构。
  • 初始化objc_classswift_class_t相关的属性, 同时设置isaroData

swift_initClassMetadataImpl:

  • 设置Superclass,如果没有指明父类,会被设置为SwiftObject
  • 初始化Vtable
  • 设置class_ro_tInstanceStartInstanceSize字段,遍历ivars修改每个ivaroffset
  • 将该类注册到objc runtime
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,084评论 1 32
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,729评论 2 9
  • 本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的...
    lylaut阅读 792评论 0 4
  • 文中的实验代码我放在了这个项目中。 以下内容是我通过整理[这篇博客] (http://yulingtianxia....
    茗涙阅读 909评论 0 6
  • 拥一盏清茶的暖,将生命行至阑珊。 我不知道自己行走了多少路程,辗转过凋零的热烈,潋滟的孤寂,一抬头,又将告别这一个...
    彗心姑娘阅读 449评论 1 5