swift类和结构体(一)

220 阅读8分钟

一、初识类和结构体

//结构体
struct NBCBBankStruct{
    var name = ""
    var age = ""
    
    init(_ name:String, _ age:String){
        
    }
}
//类
class NBCBBankClass{
    var name = ""
    var age = ""
    
    init(_ name:String, _ age:String){
        
    }
    deinit {
        
    }
}

结构体和类的主要相同点有:

  • 定义存储值的属性
  • 定义方法
  • 定义下标以使用下标语法提供对其值的访问
  • 定义初始化器
  • 使用 extension 来拓展功能
  • 遵循协议来提供某种功能

主要的不同点有:

  • 类有继承的特性,而结构体没有
  • 类型转换使您能够在运行时检查和解释类实例的类型
  • 类有析构函数用来释放其分配的资源
  • 引用计数允许对一个类实例有多个引用

类是引用类型。也就意味着一个类类型的变量并不直接存储具体的实例对象,是对当前存储具体实例内存地址的引用。

借助两个指令来查看当前变量的内存结构

po : p  po 的区别在于使用 po 只会输出对应的值,而 p 则会返回值的类型以及命令结果
的引用名。
x/8g: 读取内存中的值(8g: 8字节格式输出)

image.png

查看sil中间代码

image.png

初始化一次NBCBBankClass对象,然后给a b引用

结构体是直接存储,c和d是独立的 image.png

查看sil中间代码

image.png

初始化一次NBCBBankStruct对象,然后给c d存储

func test(){
        var a = NBCBBankClass()
        var b = a
        a.age = "18"
        
        var c = NBCBBankStruct()
        var d = c
        c.name = "nb"
        print("end")
    }

image.png 查看sil代码

类是修改最初的实例对象

image.png

结构体是只修改c image.png

引用类型和值类型还有一个最直观的区别就是存储的位置不同:一般情况,值类型存储的在 栈上,引用类型存储在堆上。

内存结构图:

image.png

栈区(stack): 局部变量和函数运行过程中的上下文

Heap: 存储所有对象

Global: 存储全局变量;常量;代码区

Segment & Section: Mach-O 文件有多个段( Segment ),每个段有不同的功能。然后每

个段又分为很多小的 Section

TEXT.text : 机器码

TEXT.cstring : 硬编码的字符串

TEXT.const: 初始化过的常量

DATA.data: 初始化过的可变的(静态/全局)数据

DATA.const: 没有初始化过的常量

DATA.bss: 没有初始化的(静态/全局)变量DATA.common: 没有初始化过的符号声明

结构体的内存存储在栈区,类的内存存储在堆区

image.png

image.png

堆区的释放和创建分配是比栈区慢的,所以我们在写代码的时候,可以酌情用值类型来代替class类型

func checkUser(name:String, usrid:Int){
    let key =  "\(name)\(usrid)"
    var cache = [AnyHashable:Any]()
    if let value = cache[key]{
    }
}
替换为
struct nameInfo:Hashable{
    var name = "jwk"
    var usrid = 1010141
    
}
func checkUser(name:String, usrid:Int){
    let key = nameInfo.init(name: name, usrid: usrid)  //编译器会默认给结构体创建成员的初始化器
    var cache = [AnyHashable:Any]()
    if let value = cache[key]{
    }
}
// 1 field
class IntClass {
    let value: Int
    init(_ val: Int) { self.value = val }
}

struct IntStruct {
    let value: Int
    init(_ val: Int) { self.value = val }
}

func + (x: IntClass, y: IntClass) -> IntClass {
    return IntClass(x.value + y.value)
}

func + (x: IntStruct, y: IntStruct) -> IntStruct {
    return IntStruct(x.value + y.value)
}

// 10 fields
class Int10Class {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

struct Int10Struct {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

调用
static func runTests() {
        print("Running tests")
        
        measure("class (1 field)") {
            var x = IntClass(0)
            for _ in 1...10000000 {
                x = x + IntClass(1)
            }
        }
        
        measure("struct (1 field)") {
            var x = IntStruct(0)
            for _ in 1...10000000 {
                x = x + IntStruct(1)
            }
        }
        
        measure("class (10 fields)") {
            var x = Int10Class(0)
            for _ in 1...10000000 {
                x = x + Int10Class(1)
            }
        }
        
        measure("struct (10 fields)") {
            var x = Int10Struct(0)
            for _ in 1...10000000 {
                x = x + Int10Struct(1)
            }
        }
    }
    static private func measure(_ name: String, block: @escaping () -> ()) {
        print()
        print("\(name)")
        let t0 = CACurrentMediaTime()
        
        block()
        
        let dt = CACurrentMediaTime() - t0
        print("\(dt)")
    }
    
    结果 Running tests
    class (1 field)    struct (1 field)
    2.905227375042159  2.0113152083358727
    class (10 fields)  struct (10 fields)
    2.859269750013482  2.218389624962583

二、类的初始化器

编译器不会给类创建成员的初始化器,结构体默认有

image.png

image.png

指定初始化器和便捷初始化器

类中可以自己定义很多初始化器,但是这样不利于接口的设计,为了遵循更好的设计规范,我们可以指定某个初始化器为指定初始化器,其他的为便捷初始化器

class NBCBBankClass{
    var name = ""
    var age = ""
    
    init(_ name:String, _ age:String){
        self.name = name
        self.age = age
    }
    convenience init(_ name:String){
        self.init(name, "18")
    }
    convenience init(){
        self.init("jwk", "18")
    }
}

便捷初始化器必须从相同的类里调用另一个初始化器(可以是另一个便捷初始化器)。

子类的初始化器

image.png

class NBCBBankChildClass:NBCBBankClass{
    var chilidName :String
    
    override init(_ name:String, _ age:String){
        self.chilidName = "yinhang"
        super.init(name, age)
    }
}

image.png 注意事项(为了访问self和成员变量安全)

  • 指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性 都要初始化完成。
  • 指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如 果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖
  • 便捷初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括 同类里定义的属性)。如果没这么做,便捷构初始化器赋予的新值将被自己类中其 它指定初始化器所覆盖。
  • 初始化器在第一阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例 属性的值,也不能引用 self 作为值。

可失败初始化器

着当前因为参数的不合法或者外部条件 的不满足,存在初始化失败的情况。这种 Swift 中可失败初始化器写 return nil 语句, 来表明可失败初始化器在何种情况下会触发初始化失败。

image.png

必要初始化器

在类的初始化器前添加 required 修饰符来表明所有该类的子类都必须 实现该初始化器 image.png

三、类的生命周期

OC 通过 clang 编译器,编译成 IR,然后再生成可执行文件 .o(这里也就是我们的机器 码).

Swift 则是通过 Swift 编译器编译成 IR,然后在生成可执行文件。

image.png

swift编译过程

image.png

生成sil代码文件

swiftc -emit-sil ${SRCROOT}/ClassStruct/ViewController.swift > ViewController1.sil && open ViewController1.sil 这个命令会报错 unable to load standard library for target

image.png 采用如下指令 swiftc -emit-sil -target x86_64-apple-ios13.5-simulator -sdk $(xcrun --show-sdk-path --sdk iphonesimulator) ${SRCROOT}/ClassStruct/ViewController.swift > ViewController1.sil && open ViewController1.sil

image.png

image.png xcrun swift-demangle xxxxx 还原混写 常用指令

// 分析输出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语法链接

对象的数据结构

oc objc_objec

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
8字节只有一个isa指针

swift HeapObject

Swift 对象内存分配:

  • __allocating_init -----> swift_allocObject -----> _swift_allocObject_ -----> swift_slowAlloc -----> Malloc
  • Swift 对象的内存结构 HeapObject (OC objc_object) ,有两个属性: 一个是Metadata ,一个是 RefCount ,总共默认占用 16 字节大小。
class NBCBBankClass:NSObject{
    var name = ""
    var age = ""
}
let t = NBCBBankClass()

image.png 打住断点控制台输入si

image.png

class NBCBBankClass:NSObject{
    var name = ""
    var age = ""
}
let t = NBCBBankClass()

打住断点 控制台输入si image.png

requiredSize需要的大小 requiredAlignmentMask对齐的掩码 7
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));

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a4b8871027524a08a14a423f4f0532e6~tplv-k3u1fbpfcp-watermark.image?)
  // 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;
}

// When alignMask == ~(size_t(0)), allocation uses the "default"
// _swift_MinAllocationAlignment. This is different than calling swift_slowAlloc
// with `alignMask == _swift_MinAllocationAlignment - 1` because it forces
// the use of AlignedAlloc. This allows manually allocated to memory to always
// be deallocated with AlignedFree without knowledge of its original allocation
// alignment.
//
// For alignMask > (_minAllocationAlignment-1)
// i.e. alignment == 0 || alignment > _minAllocationAlignment:
//   The runtime must use AlignedAlloc, and the standard library must
//   deallocate using an alignment that meets the same condition.
//
// For alignMask <= (_minAllocationAlignment-1)
// i.e. 0 < alignment <= _minAllocationAlignment:
//   The runtime may use either malloc or AlignedAlloc, and the standard library
//   must deallocate using an identical alignment.
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) {
#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC
    p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
    p = malloc(size);
#endif
  } 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;
}
struct HeapObject {
  typename Runtime::StoredPointer Metadata;
  typename Runtime::StoredSize 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;

  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__
};

类的数据结构

oc objc_class image.png swift Metadata

/// The common structure of all metadata for heap-allocated types.  A
/// pointer to one of these can be retrieved by loading the 'isa'
/// field of any heap object, whether it was managed by Swift or by
/// Objective-C.  However, when loading from an Objective-C object,
/// this metadata may not have the heap-metadata header, and it may
/// not be the Swift type metadata for the object's dynamic type.
template <typename Runtime>
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
};
using HeapMetadata = TargetHeapMetadata<InProcess>;

/// The portion of a class metadata object that is compatible with
/// all classes, even non-Swift ones.
template <typename Runtime>
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

swift类的结构

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 
}

证明结构

class NBCBBankClass{
    var name = ""
    var age = ""
}
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
}
class NBCBBank{
    func test(){
        var a = NBCBBankClass()
        let objcRaw = Unmanaged.passUnretained(a).toOpaque()
        let objc = objcRaw.bindMemory(to: HeapObject.self, capacity: 1)
        print(objc.pointee)
               
        let metadata = objc.pointee.metadata.bindMemory(to: Metadata.self, capacity: 100)
        print(metadata.pointee)

        print("end")
    }
}
将实例对象的指针还原成结构体

结果为 HeapObject(metadata: 0x000000010fdd7e88, refcount: 3)

Metadata(kind: 4386733664, superClass: _TtCs12_SwiftObject, cacheData: (140703395923968, 140978006523904), data: 105553138192322, classFlags: 2, instanceAddressPoint: 0, instanceSize: 48, instanceAlignmentMask: 7, reserved: 0, classSize: 168, classAddressPoint: 16, typeDescriptor: 0x000000010577d858, iVarDestroyer: 0x0000000000000000)