swift 属性3

119 阅读3分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。

swift单例:

直接static创建,将init方法藏起来(private私有重写)。
支持懒加载, 线程安全

class HJPerson {
    // 创建单例对象
    static let sharedInstance = HJPerson()
    // 重写init方法,设为私有方法
    private init(){}
}

回忆标准OC单例:

@implementation HJPerson

static HJPerson rson *sharedInstance = nil;

+ (instancetype)sharedInstance{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 不使用alloc方法,而是调用[[super allocWithZone:NULL] init]
        // 重载allocWithZone基本的对象分配方法,所以要借用父类(NSObject)的功能处理底层内存分配
        // 此时不管外部使用设么方式创建对象,最终返回的都是单例对象
        sharedInstance = [[super allocWithZone:NULL] init] ;
    });
    return sharedInstance;
}

+(id)allocWithZone:(struct _NSZone *)zone {
    return [HJPerson sharedInstance] ;
}
 
-(id)copyWithZone:(NSZone *)zone {
    return [HJPerson sharedInstance] ;
}
 
-(id)mutablecopyWithZone:(NSZone *)zone {
    return [HJPerson sharedInstance] ;
}

@end

四、 延迟存储属性

延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。
在属性声明前使用 lazy 来标示一个延迟存储属性。

注意:

  • 必须将延迟存储属性声明成变量(使用var关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
  • 延迟存储属性并不能保证线程安全
  • 延迟存储属性对实例对象大小的影响

延迟存储属性一般用于:

  • 延迟对象的创建。
  • 当属性的值依赖于其他未知类
import Cocoa

class sample {
    lazy var no = number() // `var` 关键字是必须的
}

class number {
    var name = "延迟存储属性"
}

var firstsample = sample()
print(firstsample.no.name)

//****** 打印结果 ******
延迟存储属性

验证:

image.png

  • 懒加载存储属性只有在第一次访问时才会被赋值

我们也可以通过sil文件来查看,这里可以在生成sil文件时,加上还原swift中混淆名称的命令(即xcrun swift-demangle):swiftc -emit-sil main.swift | xcrun swift-demangle >> ./main.sil && code main.sil,demo代码如下

class CJLTeacher{
    lazy var age: Int = 18
}

var t = CJLTeacher()
t.age = 30

【类+main】:lazy修饰的存储属性在底层是一个optional类型

image.png 【 setter+getter】:从getter方法中可以验证,在第一次访问时,就从没值变成了有值的操作

image.png

通过sil,有以下两点说明:

  • 1、lazy修饰的属性,在底层默认是optional,在没有被访问时,默认是nil,在内存中的表现就是0x0。在第一次访问过程中,调用的是属性的getter方法,其内部实现是通过当前enum的分支,来进行一个赋值操作
  • 2、可选类型是16字节吗?可以通过MemoryLayout打印

size:实际大小
stride:分配大小(主要是由于内存对齐)

print(MemoryLayout<Optional<Int>>.stride)
print(MemoryLayout<Optional<Int>>.size)

//*********** 打印结果 ***********
16
9

为什么实际大小是9?Optional其本质是一个enum,其中Int占8字节,另一个字节主要用于存储case值(这个后续会详细讲解)