题目中的代码存在可能循环引用的问题,对象的循环引用会造成ARC引用计数无法释放被引用的任何一个对象,从而造成内存泄露。
上述代码中Customer类包含一个CreditCard类的实例属性,而CreditCard类也包含了一个Customer类的实例属性。一旦在堆空间中这两个类的实例相互引用对方就会造成循环引用。
实例代码:生成Customer类和CreditCard类的实例属性,并让这两个实例属性互相引用。这样会造成循环引用,导致内存泄露。
var person:Customer
var bankCard:CreditCard
person = Customer(name:"Peter")
bankCard = CreditCard(number:111000222333, customer:person)
person.card = bankCard //这样便形成了循环引用
//由于形成了循环引用,下面两句代码执行之后依然不会释放堆里面的内存空间
person = nil
bankCard = nil
解决循环引用造成内存泄露有三种方式:
1、在合适的地方,手动将循环引用解除
var person:Customer
var bankCard:CreditCard
person = Customer(name:"Peter")
bankCard = CreditCard(number:111000222333, customer:person)
person.card = bankCard //这样便形成了循环引用
person.card = nil //在释放两个实例对象前可以手动的释放person对CreditCard类实例的引用
//由于上面一句代码断开了循环引用,下面两句代码执行之后就会释放堆里面的内存空间
person = nil
bankCard = nil
2、可将引用声明为弱引用
class Customer {
let name:String
weak var card:CreditCard? //设置成弱引用,弱引用使用时将不再让ARC计数加一,如果CreditCard的实例提前释放的话,属性将变成nil
init(name:String) {
self.name = name
}
deinit {(print("\(name) is being deinitialized")}
}
3、如果不允许对象引用为nil时,可以将对象声明为无主引用
class Customer {
let name:String
unowned var card:CreditCard //设置无主引用,无主引用使用时将不再让ARC计数加一,如果CreditCard的实例提前释放的话,属性将依然指向那个地址。需要确保不要非法访问,不然会抛出异常
init(name:String) {
self.name = name
}
deinit {(print("\(name) is being deinitialized")}
}
上述三种解决方案我觉得方案二,即使用弱引用最适合题目情况,因为本身Customer类中的属性card可以允许为空,如果使用手动循环的话有可能遗忘。第二种方法保证了不会出现循环引用而导致内存泄露。第三种方法使用无主引用,第一是改变了Customer类中的属性card的可为空属性,第二当card属性指向的堆空间提前释放的话,会有非法访问的风险。所以使用弱引用决绝方案最好。