swift的struct和class的异同?
相同点
·定义属性
·定义方法
·定义初始化器
·定义下标以及使用下标语法访问提供对其值访问
·可以通过extension来拓展 ·遵循协议protocol来提供某种功能
最本质的区别:
·struct是值类型,分配内存在栈区。 ·class是引用类型,分配内存在堆区。
验证:
、
结构体赋值是值拷贝,栈上不同内存。
类赋值是指针拷贝,指向堆上同一个内存地址。
struct和class的其他不同:
·类有继承特性,struct没有 ·class可以类型转换(能够在运行时检查和解释类实例的类型) ·类有析构函数用来释放其分配的资源(堆内存) ·引用计数允许对一个实例多个引用(引用类型)
struct和class执行效率上的差别
·struct在栈上,线程安全 ·struct不需要分配、销毁堆空间,访问效率更高。
小结:
struct作为值类型相对于class有更加安全高效的访问。
在不需要的继承的合适场景,可以考虑多使用struct。
类的初始化器
默认初始化器
编译器不会为class提供默认的初始化器,需要程序员自己提供一个指定初始化器。
struct构体会提供默认的初始化方法。
如果calss的所有属性都有默认的初始化值,或者可空,也可以不提供指定初始化器
指定初始化器
每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器,类偏向于少量指定初始化器,一个类通常只有一个指定初始化器。
class QPPerson{
var age: Int
var name: String
//指定初始化器
init(age: Int,name: String) {
self.age = age;
self.name = name;
}
}
·指定初始化器必须保证向上委托给父类初始化器之前,本类引入的所有属性都要初始化完成。
·指定初始化器必须先向上委托父类初始化器,然后才能为其继承来的属性设置新值。
便捷初始化器
便捷初始化器必须先委托同类中的其他初始化器,然后再为任意属性赋新值。
可失败初始化器
当前因为参数的不合法或者外部条件的不满足,存在初始化失败的情况。
init?(age: Int,name: String) {
if age < 18 {
print("未成nian人");
return nil
}
self.age = age;
self.name = name;
}
必要初始化器
在类的初始化器前添加required修饰符来表明该类的所有子类都必须实现该初始化器。
required init(age: Int,name: String) {
if age < 18 {
print("未成nian人");
return nil
}
self.age = age;
self.name = name;
}
对象和类的底层结构探索
swift的编译
iOS开发语言不管是OC还是Swift后端都是通过LLVM进行编译的,如下图所示:
基于LLVM的编译前后端分离式架构。中间通过LLVM IR承接。
oc的编译前端是clang。
swift的编译前端是swift。
swift的编译过程,如下图:
//相关的编译指令
// 分析输出AST
swiftc main.swift -dump-parse
// 分析并且检查类型输出AST
swiftc main.swift -dump-ast
// 生成中间体语言(SIL),未优化
swiftc main.swift -emit-silgen
// 生成中间体语言(SIL),优化后的
swiftc main.swift -emit-sil
// 生成LLVM中间体语言 (.ll文件)
swiftc main.swift -emit-ir
// 生成LLVM中间体语言 (.bc文件)
swiftc main.swift -emit-bc
// 生成汇编
swiftc main.swift -emit-assembly
// 编译生成可执行.out文件
swiftc -o main.o main.swift
sil文件分析
swift编译比clang更加复杂,多了一种新的中间语言SIL .
SIL(swift intermediate language)特点:
1.能够完整的表达程序的语义。 2.被设计用于代码生成和静态分析 3.处于编译的流水线中,而不是独立于之外 4.在源代码和 LLVM 之间架起了抽象的桥梁
生成.sil文件
示例代码
class QPIOSCoder{
var name:String = "ramon"
var level:Int = 0
}
var coder = QPIOSCoder()
设置xcode的build的run自动生成.sil文件
解析.sil文件
swift的类型安全
int8_t x = 100;
int8_t y = x + 100;
NSLog(@"%d",y);
编译器,可以检测出OC检测不出的溢出。
swift对象和类结构探索
继承自NSObject的swift对象的创建
可以很明显看到,继承自NSObject,对象创建就调用objc_allocWithZone函数及objc_msgSend函数。
如果在swift开发中想使用OC的runtime特性,可以使用这种方式。
纯swift对象的创建
__allocating_init->swift_allocObject->
为了追踪纯swift对象的创建及底层数据结构,我们需要看下swift源码
源码分析
HeapObject *swift::swift_allocObject(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
CALL_IMPL(swift_allocObject, (metadata, requiredSize, requiredAlignmentMask));
}
看下CALL_IMPL的定义
#define CALL_IMPL(name, args) do { \
void *fptr; \
memcpy(&fptr, (void *)&_ ## name, sizeof(fptr)); \
extern char _ ## name ## _as_char asm("__" #name "_"); \
fptr = __ptrauth_swift_runtime_function_entry_strip(fptr); \
if (SWIFT_UNLIKELY(fptr != &_ ## name ## _as_char)) \
return _ ## name args; \
return _ ## name ## _ args; \
} while(0)
对函数名做了拼接,真正调用的应该是_swift_allocObject_函数
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
assert(isAlignmentMask(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);
// If leak tracking is enabled, start tracking this object.
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;
}
可以看到底层的数据类型是HeapObject。
// The members of the HeapObject header that are not shared by a
// standard Objective-C instance
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
/// The Swift heap-object header.
/// This must match RefCountedStructTy in IRGen.
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *__ptrauth_objc_isa_pointer metadata;
//refcounts见名知意,类型经过多次typedef转换
//可以简单看成是UInt64:64位系统下。
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
#ifndef __swift__
HeapObject() = default;
// Initialize a HeapObject header as appropriate for a newly-allocated object.
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Initialized)
{ }
// Initialize a HeapObject header for an immortal object
constexpr HeapObject(HeapMetadata const *newMetadata,
InlineRefCounts::Immortal_t immortal)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Immortal)
{ }
#ifndef NDEBUG
void dump() const SWIFT_USED;
#endif
#endif // __swift__
};
结论1:
//简化后的swift对象的数据结构
struct HeapObject{
HeapMetadata const *__ptrauth_objc_isa_pointer metadata;
UInt64 refCounts;
}
继续追踪源码查看HeapMetaData *,看注释可以看做是我们OC的isa指针。
//定义别名
using HeapMetadata = TargetHeapMetadata<InProcess>;
//继承自TargetMetadata
struct TargetHeapMetadata : TargetMetadata<Runtime> {
using HeaderType = TargetHeapMetadataHeader<Runtime>;
TargetHeapMetadata() = default;
constexpr TargetHeapMetadata(MetadataKind kind)
: TargetMetadata<Runtime>(kind) {}
#if SWIFT_OBJC_INTEROP
constexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa)
: TargetMetadata<Runtime>(isa) {}
#endif
};
//TargetMetadata源码太长,做了精简
struct TargetMetadata {
//初始化函数
private:
//唯一的成员kind,标识类型枚举
StoredPointer Kind;
//kind的所有枚举值
getTypeContextDescriptor() const {
switch (getKind()) {
case MetadataKind::Class: {
const auto cls = static_cast<const TargetClassMetadata<Runtime> *>(this);
if (!cls->isTypeMetadata())
return nullptr;
if (cls->isArtificialSubclass())
return nullptr;
return cls->getDescription();
}
case MetadataKind::Struct:
case MetadataKind::Enum:
case MetadataKind::Optional:
return static_cast<const TargetValueMetadata<Runtime> *>(this)
->Description;
case MetadataKind::ForeignClass:
return static_cast<const TargetForeignClassMetadata<Runtime> *>(this)
->Description;
default:
return nullptr;
}
}
kind所有类型
可以认为TargetMetadata是所有swift数据类型的基类。
另外我们也注意到了TargetClassMetadata,它是不是我们swift中class的底层数据结构呢?它的数据成员有哪些呢?
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;
using StoredSize = typename Runtime::StoredSize;
TargetClassMetadata() = default;
constexpr TargetClassMetadata(const TargetAnyClassMetadata<Runtime> &base,
ClassFlags flags,
ClassIVarDestroyer *ivarDestroyer,
StoredPointer size, StoredPointer addressPoint,
StoredPointer alignMask,
StoredPointer classSize, StoredPointer classAddressPoint)
: TargetAnyClassMetadata<Runtime>(base),
Flags(flags), InstanceAddressPoint(addressPoint),
InstanceSize(size), InstanceAlignMask(alignMask),
Reserved(0), ClassSize(classSize), ClassAddressPoint(classAddressPoint),
Description(nullptr), IVarDestroyer(ivarDestroyer) {}
// The remaining fields are valid only when isTypeMetadata().
// The Objective-C runtime knows the offsets to some of these fields.
// Be careful when accessing them.
/// Swift-specific class flags.
ClassFlags Flags;
/// The address point of instances of this type.
uint32_t InstanceAddressPoint;
/// The required size of instances of this type.
/// 'InstanceAddressPoint' bytes go before the address point;
/// 'InstanceSize - InstanceAddressPoint' bytes go after it.
uint32_t InstanceSize;
/// The alignment mask of the address point of instances of this type.
uint16_t InstanceAlignMask;
/// Reserved for runtime use.
uint16_t Reserved;
/// The total size of the class object, including prefix and suffix
/// extents.
uint32_t ClassSize;
/// The offset of the address point within the class object.
uint32_t ClassAddressPoint;
// Description is by far the most likely field for a client to try
// to access directly, so we force access to go through accessors.
private:
/// An out-of-line Swift-specific description of the type, or null
/// if this is an artificial subclass. We currently provide no
/// supported mechanism for making a non-artificial subclass
/// dynamically.
TargetSignedPointer<Runtime, const TargetClassDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;
using StoredSize = typename Runtime::StoredSize;
#if SWIFT_OBJC_INTEROP
constexpr TargetAnyClassMetadata(TargetAnyClassMetadata<Runtime> *isa,
TargetClassMetadata<Runtime> *superclass)
: TargetHeapMetadata<Runtime>(isa),
Superclass(superclass),
CacheData{nullptr, nullptr},
Data(SWIFT_CLASS_IS_SWIFT_MASK) {}
#endif
constexpr TargetAnyClassMetadata(TargetClassMetadata<Runtime> *superclass)
: TargetHeapMetadata<Runtime>(MetadataKind::Class),
Superclass(superclass)
#if SWIFT_OBJC_INTEROP
, CacheData{nullptr, nullptr},
Data(SWIFT_CLASS_IS_SWIFT_MASK)
#endif
{}
#if SWIFT_OBJC_INTEROP
// Allow setting the metadata kind to a class ISA on class metadata.
using TargetMetadata<Runtime>::getClassISA;
using TargetMetadata<Runtime>::setClassISA;
#endif
// Note that ObjC classes do not have a metadata header.
/// The metadata for the superclass. This is null for the root class.
TargetSignedPointer<Runtime, const TargetClassMetadata<Runtime> *
__ptrauth_swift_objc_superclass>
Superclass;
#if SWIFT_OBJC_INTEROP
/// The cache data is used for certain dynamic lookups; it is owned
/// by the runtime and generally needs to interoperate with
/// Objective-C's use.
TargetPointer<Runtime, void> CacheData[2];
/// The data pointer is used for out-of-line metadata and is
/// generally opaque, except that the compiler sets the low bit in
/// order to indicate that this is a Swift metatype and therefore
/// that the type metadata header is present.
StoredSize Data;
static constexpr StoredPointer offsetToData() {
return offsetof(TargetAnyClassMetadata, Data);
}
#endif
/// Is this object a valid swift type metadata? That is, can it be
/// safely downcast to ClassMetadata?
bool isTypeMetadata() const {
#if SWIFT_OBJC_INTEROP
return (Data & SWIFT_CLASS_IS_SWIFT_MASK);
#else
return true;
#endif
}
/// A different perspective on the same bit
bool isPureObjC() const {
return !isTypeMetadata();
}
};
推到下:TargetClassMetaData的数据成员,我们直接用struct嵌套来表示继承。
struct TargetClassMetaData{
struct TargetAnyClassMetaData{
struct TargetHeapMetaData{
StoredPointer Kind;
}
TargetSignedPointer<Runtime, const TargetClassMetadata<Runtime> *
__ptrauth_swift_objc_superclass>
Superclass;
TargetPointer<Runtime, void> CacheData[2];
StoredSize Data;
}
ClassFlags Flags;
/// The address point of instances of this type.
uint32_t InstanceAddressPoint;
/// The required size of instances of this type.
/// 'InstanceAddressPoint' bytes go before the address point;
/// 'InstanceSize - InstanceAddressPoint' bytes go after it.
uint32_t InstanceSize;
/// The alignment mask of the address point of instances of this type.
uint16_t InstanceAlignMask;
/// Reserved for runtime use.
uint16_t Reserved;
/// The total size of the class object, including prefix and suffix
/// extents.
uint32_t ClassSize;
/// The offset of the address point within the class object.
uint32_t ClassAddressPoint;
TargetSignedPointer<Runtime, const TargetClassDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;
TargetSignedPointer<Runtime, ClassIVarDestroyer * __ptrauth_swift_heap_object_destructor> IVarDestroyer;
}
进一步精简
小结2:
struct Metadata{
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
验证数据结构
struct HeapObject{
var metadata: UnsafeRawPointer
var refcount: UInt64
}
struct MetaData{
var kind: UnsafeRawPointer
var superClass: Any.Type
var cacheData: (Int,Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reverved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
class QPPerson{
var age: Int
var name: String
init(age:Int,name:String) {
self.age = 18
self.name = "qlpdddddddd"
}
}
var p = QPPerson(age: 18, name: "ramon")
let objcRawPtr = Unmanaged.passUnretained(p as AnyObject).toOpaque()
let objcPtr = objcRawPtr.bindMemory(to: HeapObject.self, capacity: 1)
let metaData = objcPtr.pointee.metadata.bindMemory(to: MetaData.self, capacity: MemoryLayout<MetaData>.stride).pointee
print(MemoryLayout.stride(ofValue: "ddddddddddd"))
print(objcPtr.pointee)
print(metaData)
print("end")
打印如下:
通过打印结果,可以验证我们推导的swift对象和class的底层数据结构正确😄。