swift 懒加载 & 单例

744 阅读2分钟

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

1. Lazy 懒加载

1.1 创建

swift懒加载是使用Lazy进行修饰

  • 必须是var(可变存储属性),不可以是let(不可变属性),也不能是option(可选值)。
class HTPerson {
    // 懒加载属性
    lazy var name: String = "ht"
}
  • 初始时,没有值

image.png

  • 首次访问后,有值

image.png

  • 所以Lazy修饰的属性,具备延时加载功能。(首次访问时才加载

1.2 大小

  • 懒加载属性大小,与本身属性大小不同
    swift中int(64位系统)原本8字节,但lazy修饰后,就变成16字节

image.png

1.3 懒加载的本质

  • 懒加载属性,创建时,是可选值。但是在首次访问(getter)时,进行初始赋值,返回非可选类型的值。
  • 懒加载是线程不安全的。 读写未加锁,多线程同时访问(getter)时,可能多次赋值

Q: 为何lazy修饰的Int属性是16字节:

  • 因为lazy修饰的属性,会变成可选类型
    option: 可选类型。本质是枚举值类型
    包含some<Int>none两个枚举类型。其中none0x0

-   其中:`none``1字节``some<Int>``8字节`。所以`实际大小`(size)为`9字节`。
-   对外遵循`align8`8字节对齐)原则,`系统`会开辟`16字节空间`8的倍数)来存储`真实大小``9字节``数据`\
    (`align8原则`:为了避免`五花八门``空间大小`,增加系统`读取数据``困难性`。所以`统一``8字节`为一个`单位`,进行`一段一段``截取`,提高`读取效率`。)

lazy总结

  • lazy必须修饰var(可变类型)存储属性,
  • 必须有默认初始值,但初始值会延迟首次加载赋值
    (所以lazy修饰属性,叫延迟存储属性,也叫懒加载属性)
  • 延迟存储属性线程不安全的(可能多次赋值)
  • 延迟存储属性影响实例对象大小

2. static 单例

Swift单例:

直接static创建,将init方法藏起来(private私有重写)。

class HJPerson {
    // 创建单例对象
    static let sharedInstance = HJPerson()
    // 重写init方法,设为私有方法
    private init(){}
}
  • swift源码查看swift_once,内部调用gcddispatch_once_f,创建单例线程安全(内部有锁,读写安全)。

image.png 使用gcd创建,使用父类alloc初始化,拦截alloc,任何方式实例化,返回的都是单例对象。

@implementation HTPerson

static HTPerson *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 [HTPerson sharedInstance] ;
}
 
-(id)copyWithZone:(NSZone *)zone {
    return [HTPerson sharedInstance] ;
}
 
-(id)mutablecopyWithZone:(NSZone *)zone {
    return [HTPerson sharedInstance] ;
}

@end