Swift里你可能不知道的事儿(2)——处理对象reference cycle的三
处理对象reference cycle的三种方式泊学高清学习视频 class member允许为nil时 - weak reference在我们之前的例子里,Apartment和Person中引起reference cycle的数据成员都允许为nil,对于这种情况,我们可以像下面这样使用weak reference来解决reference cycle: weak var name: Type 对于一个weak reference来说:
“由于weak reference的特定,它只能被定义成var。” 了解了weak reference特性之后,我们可以把Apartment修改成这样: class Apartment { let unit: String weak var tenant: Person? // omit for simplicity... } var mars: Person? = Person(name: "Mars") var apt11: Apartment? = Apartment(unit: "11",owner: mars!) mars!.apartment = apt11 apt11!.tenant = mars mars = nil apt11 = nil 之后,当我们把mars设置为nil的时候,Apartment和Person的关系就变成了这样: 由于已经没有strong reference指向mars,于是mars就被ARC释放了。接下来,我们把apt11设置成nil: 这时,也没有任何strong reference指向apt11了,它也会被ARC释放。这就是当引起reference cycle的两个member允许为nil时,我们使用的解决方案。 “weak reference用于解决成员允许为nil的reference cycle。” 当有一个member不能为nil时 - unowned来看另外一个应用场景。我们把Person改成下面这样: class Person { let name: String var apartment: Apartment? var property: Apartment? // omit for simplicity... } 由于不是每个人名下都有房产,因此property允许为nil,我们把它定义为Apartment optional。接下来,把Apartment的代码改成这样: class Apartment { let unit: String weak var tenant: Person? let owner: Person init(unit: String,owner: Person) { self.unit = unit self.owner = owner print("Apartment (unit) is being initialized.") } // omit for simplicity... } 由于每个Apartment一定会有房东,不能为nil,因此,我们把它定义为Person。 接下来,当我们再次创建mars和apt11的时候: var mars: Person? = Person(name: "Mars") var apt11: Apartment? = Apartment(unit: "11",owner: mars!) mars!.apartment = apt11 apt11!.tenant = mars mars = nil apt11 = nil 我们不难发现,添加的owner又引入了一个reference cycle。 在上面这个图里,即使我们把mars和apt11设置为nil: owner这个strong reference会让mars存活,进而apartment会让apt11存活。这就形成了一个新的reference cycl。 解决这种类型的reference cycle也很简单,我们把用下面的方式: unowned let name: Type 把owner设置为一个unowned reference就可以了 class Apartment { let unit: String weak var tenant: Person? unowned let owner: Person // Omit for simplicity... } 和strong reference相比,unowned reference只有一个特别:不会引起对象引用计数的变化。因此,当我们把mars和apt11设置为nil时,对象的关系就会变成这样: 此时,已经没有strong reference引用mars,它会先被ARC释放掉,之后,引用apt11的apartment也不存在了,apt11也会被ARC清除。这就是reference cycle中一方不能为nil时的解决方案。 “unowned reference用于解决成员不允许为nil的reference cycle。” 当两个member都不允许为nil时 class Country { let name: String init(name: String) { self.name = name } } class City { let name: String init(name: String) { self.name = name } } 起初,它们很简单,Country代表国家,City代表城市,各自的init()函数用来构建对象。接下来,我们希望添加下面这样的语义:Country要有一个member表示首都,City要有一个country表示城市的归属。我们先做第一步的修改: class Country { let name: String var capital: City init(name: String) { self.name = name } } class City { let name: String let country: Country init(name: String) { self.name = name } } 在我们的例子里,一个国家不可能没有首都,一个城市也不可能没有国家归属,因此,它们都只能是普通的类对象,而不能是一个Optional。接下来,我们来处理它们的初始化问题。首先,我们按照一般的方式处理City: class City { let name: String let country: Country init(name: String,country: Country) { self.name = name self.country = country } } 然后,处理Country的init(): class Country { let name: String var capital: City init(name: String,capitalName: String) { self.name = name // Syntax Error!!! self.capital = City(name: capitalName,country: self) } } 在Country的init()里,我们把正在创建的Country对象传递给了City的init(),这样做,只在语义上是正确的,语法上,Swift认为构建City时,Country还没有完成初始化(self.captical还没有确定的值),因此,它不允许我们在这个时候把self传递给Country。 要想构建City时,让Swift认为Country已经构造完,唯一的做法就是captical有一个默认值nil。至此,对于Capital,我们有了两个看似冲突的需求:
而解决这种冲突唯一的办法,就是把capital定义为一个Implicitly Unwrapped Optional。 class Country { let name: String // Implicitly Unwrapped Optional var capital: City! // default to nil init(name: String,country: self) } } 至此,两个彼此关联的类就可以正常的构建和初始化了。我们分别定义一个变量: var cn: Country? = Country(name: "China",capitalName: "Beijing") var bj: City? = City(name: "Beijing",country: cn) cn = nil bj = nil 经历过之前的多个例子之后,我们很快就可以发现,当cn和bj为nil时,cn.captical和其内建的City仍旧会保持彼此“存活”在内存里,而解决这个问题的办法其实之间我们已经处理过了,把captical定义为unowned就可以了。 “unowned reference和implicitly unwrapped optional配合在一起,用于解决引起reference cycle的两个成员都不允许为nil的情况。” 接下来在这段视频里,我们了解了处理类对象reference cycle的3种不同的方式。在下一段视频里,我们会发现,使用Closure会带来类似的问题。我们也会向大家介绍对应的处理办法 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |