Compilation phase

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(a) {}
    @objc func objc_instance_method(a) {}
    @objc dynamic func objc_dynamic_instance_method(a){}}Copy the code

Here is the class information generated at compile time:

_$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
}
Copy the code

Based on the data generated by the compiler above, you can get some information:

class

  • SwiftThe class compilation phase generates andObjective-CSame class metadata, that’s why, rightSwiftandObjective-CThey can call each other.

Generic classes do not generate class metadata __objc_class structures, but roData.

  • classIf a class is not explicitly inherited, it is implicitly inheritedSwiftObject.

attribute

  • All attributes will be added toclass_ro_tIn theivarsStructure, includingprivateProperties.
  • use@objcModified properties,varProperty will addset/getMethod,letAttributes will only be addedgetMethods.

Properties of the Swift class can be modified and obtained using objC-Runtime.

methods

  • use@objcThe decorated methods are added toro_class_tthemethodsIn the structure.

Swift structure

ClassMetadata

ClassMetadata is all the ClassMetadata formats in Swift.

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;/ / class
    uint32_t instanceAddressOffset;
    uint32_t instanceSize;// Object instance size
    uint16_t instanceAlignMask;//
    uint16_t reserved;// Reserve the field
    uint32_t classSize;// Size of the class object
    uint32_t classAddressOffset;// 
    void *description;/ / class description
};
Copy the code

Swift and Objective-C class metadata are shared, and Swift class metadata is just some fields added to Objective-C.

There are also places in the source code where a reinterpret_cast is used directly for interconversion.

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

HeapObject

In Swift, a class object is really just a pointer to a HeapObject structure. HeapObject consists of HeapMetadata, which is a pointer to class object metadata, and InlineRefCounts, which manages reference counts.

struct HeapObject {
  HeapMetadata const *metadata;
  InlineRefCounts refCounts;
};
Copy the code
  • HeapMetadataandObjective-CIn theisa_tStructure the same as useISA_MASKGet the class object.
@interface ObjcClass: NSObject {
}

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

[heapObject retain];
Copy the code

However, since objective-C and Swift reference count management are different, reference count management will be performed in the same way after conversion.

Objective-c and Swift object structure:

Objc {isa_T, instance variables} Swift object {metadata, refCounts, instance variables}Copy the code

Create an object

swift_allocObject
  • swift_allocObjectThe method is used to create aSwiftObject.
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);// Create a new object
  return object;
}
Copy the code
  • Called after byte alignment based on object sizemallocMemory is allocated, after which instance variables are initialized.
  • metadataRepresents class object metadata.
  • requiredSizeandrequiredAlignmentMaskRepresents object size and byte alignment.
swift_initStackObject
  • In some scenarios object creation is optimized by the compiler forswift_initStackObjectMethods.swift_initStackObjectCreate an object on the stack. No reference count consumption and no usemallocMemory.
HeapObject *
swift::swift_initStackObject(HeapMetadata const *metadata,
                             HeapObject *object) {
  object->metadata = metadata;
  object->refCounts.initForNotFreeing();
  return object;
}
Copy the code

Destruction of objects

swift_deallocClassInstance
  • swift_deallocClassInstanceUsed to destroy objects in objectsdeallocWhen the call.
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);//
}
Copy the code
  • callobjc_destructInstanceMethod to release associated objects and weak reference release processing.

Object weak reference of Objc Runtime, not weak reference of Swift environment.

  • callswift_deallocObjectThe method callfreeReclaim memory.

Reference count correlation method

  • swift_retainandobjcSimilar to the implementation of reference counting+ 1.The overflowSave a portion of the reference count value tosideTableIn the.
  • swift_releaseFor reference counting- 1, when the reference count is0The destroy object method is called.
  • swift_weakRelated methods are used for managementweakA weak reference.

SwiftObject

In Swift, a class implicitly inherits SwiftObject if it does not explicitly inherit from any other class. SwiftObject implements all methods of the NSObject protocol and some methods of the NSObject class. The main thing is to rewrite part of the method and change the method implementation to swift-related methods.

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

Does not implement resolveInstanceMethod forwardingTargetForSelector methods, these methods can be cannot find a particular method can dynamically processing, should be don’t want to provide pure Swift class in this capacity.

For example, the retain release method was changed to use swift Runtime for reference count management:

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

Since pure Swift classes cannot interact directly with Objective-C, what purpose is SwiftObject designed this way?

Here are two usage scenarios:

  • Is the pureSwiftClass as aidParameter passing toObjective-CMethod.
- (void)test:(id)object {
  [object retain];
  [object performSelector:@selector(objc_instance_method)];
}
Copy the code
let object = NSObject()
test(object)
Copy the code
  • Calls methods using message sending.
class SwiftClass {
    @objc dynamic func objc_dynamic_instance_method(a){}}let object = SwiftClass()
object.objc_dynamic_instance_method()
Copy the code

However, the above scenario should be rarely used, and it is not clear if there is any other purpose. And by design, the pure Swift class should also be used directly by Objective-C.

Initialize an object

Objective-C

Objective-c uses Swift-NSObject subclass

class SwiftClass: NSObject {}Copy the code
SwiftClass *object = [[SwiftClass alloc] init];
Copy the code
  • Because in binarySwiftThe class contains andObjective-CSame class data information, so create way and use directlyObjective-CThe same.

Swift

Swift class

Create a pure Swift class object.

class SwiftClass {}SwiftClass(a)Copy the code
swift_getInitializedObjCClass
Class swift::swift_getInitializedObjCClass(Class c) {
  [c self];// To ensure that the objC-Runtime realize class
  return c;
}
Copy the code
Class objcClass = swift_getInitializedObjCClass(SwiftClass);
HeapObject *object = swift_allocObject(objcClass);
/ / release
swift_release(object);
Copy the code

nativeObjective-Cclass

Create a native Objective-C class object.

@interface ObjectClass
@end
Copy the code
ObjectClass(a)Copy the code
Class objcClass = swift_getInitializedObjCClass(ObjectClass);
Metadata *metadata = swift_getObjCClassMetadata(objcClass);
ClassMetadata *classMetadata = swift_getObjCClassFromMetadata(metadata);
ObjectClass *object = [classMetadata allocWithZone] init];
/ / release
objc_release(object);
Copy the code

What does swift_getObjCClassMetadata and swift_getObjCClassFromMetadata do?

Swift – NSObject subclass

Create a swift-Nsobject subclass object.

class SwiftClass: NSObject {}SwiftClass(a)Copy the code
Class objcClass = swift_getInitializedObjCClass(SwiftClass);
HeapObject *object = objc_allocWithZone(objcClass);
/ / release
objc_release(object);
Copy the code

Swift, a generic class

Create a Swift generic class object.

class GenericClass<T> {}GenericClass<Int> ()Copy the code
MetadataResponse response = swift_getGenericMetadata();
ClassMetadata *classMetadata = swift_allocateGenericClassMetadata();
swift_initClassMetadata2(classMetadata);
HeapObject *object = swift_allocObject(objcClass);
Copy the code
  • Call based on a generic type as an argumentswift_getGenericMetadataMethod to get the class object cache. There is cache directly return, no cache, callswift_allocateGenericClassMetadataMethods.

Each different generic type creates a new ClassMetadata, which is then stored in the cache for reuse.

Swift_allocateGenericClassMetadata:

  • Create a new oneClassMetadataStructure.
  • Initialize theobjc_classandswift_class_tRelated properties, while settingisaandroData.

Swift_initClassMetadataImpl:

  • Set up theSuperclassIf no parent class is specified, it is set toSwiftObject.
  • Initialize theVtable.
  • Set up theclass_ro_ttheInstanceStartandInstanceSizeField, traversalivarsModify eachivartheoffset.
  • Register the class toobjc runtime.