swift ARC

swift个属性也是通过指针的方式进行传递或者引用,则需要使用类似的Ojbect-C的智能指针的方式(ARC)

Ojbect-c有 __weak, __strong, __unsafe_unretained

swift则有与之对应的 weak, strong(默认使用strong), unowned

 

swift的所有赋值均为strong,但是因为使用ARC来管理追踪和管理内存则不可避免的会出现循环引用的问题

 

当出现循环引用的问题则需要使用weak或者unowned来解除循环引用的问题

 

一、weak

当引用的对象是可为空的时候,正常是使用weak

复制代码
class Person {
    let name: String
    init(name: String) {self.name = name}
    var apartment: Apartment?
    deinit {
        print("\(name) is being deinitial")
    }
}
class Apartment {
    let unit: String
    init(unit: String) {
        self.unit = unit
    }
    weak var tenant: Person?
    deinit {
        print("Apartment \(unit) is being deinit")
    }
}
复制代码

如上所示,使用的是weak;并且注意Person后面有个? 表示是可为空的。

弱引用则说明该参数不一定要有值,可以为空所以需要?, 那么如果把?person后面的问好去掉是否可以,答案是不行的。

 

二、unowned

当引用的对象必须有值,但是又是循环引用的时候;此时解循环饮用的方法就是使用unowned;但是使用unowned需要注意该修饰的属性是否保证有效,如果无效则会导致程序的崩溃

复制代码
class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit {
        print("\(name) is being deinit")
    }
}
class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit {
        print("Card #\(number) is being deinit")
    }
}
复制代码

如上所示,因为每张信用卡都要有一个对应的持有人,所以customer不能为空。但是因为customer跟creditcard产生了循环引用,则解循环引用的方法是使用unowned

如上分析的是从实际出发来判断是否使用unowned

那么如果想把customer改为可选的是否可以呢? 答案是可以的,可以这么声明该属性:unowned let customer: Customer?  那么customer就可为空。但是这样子操作其实可以使用weak直接操作,不需要使用unowned

使用weak修饰的属性如果无效之后则会变为空,无需担心改属性当前是否有效

 

三、循环闭包

如同objectc,swift的闭包也有捕获。

复制代码
class HTMLElement {
    let name: String
    let text: String?
    
    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
    
    deinit {
        print("\(name) is being deinit")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello")
print(paragraph!.asHTML())
paragraph = nil

// <p>hello</p>
复制代码

如上的结果只打印了<p>hello</p> 没有paragraph设为空之后的deinit的打印

出现该情况的原因是asHTML是HTMLElement的属性,但是asHTML内部却引用了self,导致HTMLElement拥有asHTML并且asHTML捕获了HTMLElement 的self则出现了循环引用

这里如果把捕获的self变为弱引用或者无主引用则不会出现循环引用的问题

可以这么修改

    lazy var asHTML: () -> String = {
        [unowned self] in
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

在闭包里面的参数列表之前描述捕获的状态为无主引用;类似的格式如下所示

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // closure body goes here
}

各个捕获的参数使用逗号隔开

 

这里额外注意的,asHTML使用的是lazy。使用lazy的意思是使用到的时候才真正的进行初始化。那么如果这里把lazy去掉则会报错。因为在HTMLElement的成员未初始化完前(asHTML还没初始化),self是不可用的

 

posted @   LCAC  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示