關於Swift ARC(下)

我們現在來演示一下在實務中會碰到的強參考造成記憶體無法被釋放的情況以及如何解決的這些情況的方法吧。

首先來為大家示範一個使用protocol時會遇到的一種Retain Cycle情況

protocol BViewController:class {}

再來建立一個class叫做AViewController,並在建構階段將變數bVC的代理人指定為自己。

class AViewController:UIViewController {
   var bVC: BViewController!

init() {
      super.init(nibName: nil,bundle: nil)
      self.bVC = BViewController();
      self.bVC.delegate = self;
}
   required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder);
}
   deinit {
      print("AVC deinit");
}
}

再來是新增一個extension來擴增AViewController並採納BViewDelegate這個protocol。 因為沒有要實作的func所以什麼都不寫。

extension AViewController:BViewDelegate {}

再新增一個class叫做BViewController

class BViewController:UIViewController {
   var delegate:BViewDelegate?
   deinit {
      print("BVC deinit");
}
}

到這邊大功告成,有沒有覺得跟ARC 上篇文章中的範例非常類似呀?

另外這個採納protocol、指定代理角色的程式結構有沒有覺得在開發情況中時常會遇到類似這樣的情況呢?

這時如果如下圖這樣做的話….

釋放了aVC,但aVC裡面的bVC裡面的delegate變數卻沒有被釋放。

這時如果在delegate變數前面加上weak修飾字

再來為大家示範使用closure會遇到的Retain Cycle情況

在執行printName()之前,因為lazy修飾字的關係,var printName這個closure變數還未建立。

因為self.name的關係如果直接把小明給釋放掉,就出現了Retain Cycle的情況。

這時有兩種寫法給大家參考一下。

  1. 加上[weak self],但這時Person會變成optional的型態,self.name若沒有使用到的話,有可能不存在,所以self.name的部份需要另外做解包optional型態的處理。

2.加上[unowned self],兩者差別在於,使用unowned不會像weak一樣會是optional型態,但使用unowned必須確保這個block一定要在Person這個Instance被釋放掉之前執行到,不然會出現錯誤。

最後一個相似的例子,如果一樣的狀況下,該closure並不是直接Retain在本身Instance上,而有轉藉別的實體來Retain的話,就不需要使用weak跟unowned修飾字也能順利釋放記憶體。

首先import PlaygroundSupport來擴充playground的功能

並在下面寫下這行。

PlaygroundPage.current.needsIndefiniteExecution = true

接下來我們利用abcde等英文字母註解來代表記憶體建立參考的順序

012等數字註解來代表在該時間點ARC自動引用計數為多少Count。

並沒有使用修飾字,也能釋放該記憶體。

ARC暫時分享到這邊,如果有機會再繼續新增。

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.