「这是我参与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)
//****** 打印结果 ******
延迟存储属性
验证:
- 懒加载存储属性只有在第一次访问时才会被赋值
我们也可以通过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类型
【 setter+getter】:从getter方法中可以验证,在第一次访问时,就从没值变成了有值的操作
通过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值(这个后续会详细讲解)