欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Swift中優(yōu)雅處理閉包導(dǎo)致的循環(huán)引用詳解

 更新時(shí)間:2019年08月06日 09:29:49   作者:小橘爺  
這篇文章主要給大家介紹了關(guān)于Swift中優(yōu)雅的處理閉包導(dǎo)致的循環(huán)引用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Swift具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

前言

Objective-C 作為一門資歷很老的語言,添加了 Block 這個(gè)特性后深受廣大 iOS 開發(fā)者的喜愛。在 Swift 中,對應(yīng)的概念叫做 Closure,即閉包。雖然更換了名字,但是概念和用法還是相似的,就算是副作用也一樣,有可能導(dǎo)致循環(huán)引用。

下面我們用一個(gè)例子看一下,首先我們需要第一個(gè)控制器(FirstViewController),它所做的就是簡單的推出第二個(gè)控制器(SecondViewController)。

class FirstViewController: UIViewController {
 
 private let button: UIButton = {
  let button = UIButton()
  button.setTitleColor(UIColor.black, for: .normal)
  button.setTitle("跳轉(zhuǎn)到 SecondViewController", for: .normal)
  button.sizeToFit()
  return button
 }()
 
 override func viewDidLoad() {
  super.viewDidLoad()
  
  button.center = view.center
  view.addSubview(button)
  button.addTarget(self, action: #selector(buttonClick), for: .touchUpInside)
 }
 
 @objc private func buttonClick() {
  let secondViewController = SecondViewController()  
  navigationController?.pushViewController(secondViewController, animated: true)
 }
}

下面是 SecondViewController 的代碼。SecondViewController 所做的事情是推出第三個(gè)控制器(ThirdViewController),不同的是,thirdViewController 是作為一個(gè)屬性存在的,同時(shí)它還有一個(gè)閉包 closure ,這是我們用來測試循環(huán)引用問題的。還實(shí)現(xiàn)了 deinit 方法,用來打印一條語句,看該控制器是否被釋放了。

class SecondViewController: UIViewController {
 
 private let thirdViewController = ThirdViewController()
 private let button: UIButton = {
  let button = UIButton()
  button.setTitleColor(UIColor.black, for: .normal)
  button.setTitle("跳轉(zhuǎn)到 ThirdViewController", for: .normal)
  button.sizeToFit()
  return button
 }()
 
 override func viewDidLoad() {
  super.viewDidLoad()
  
  button.center = view.center
  view.addSubview(button)
  button.addTarget(self, action: #selector(buttonClick), for: .touchUpInside)
 }
 
 deinit {
  print("SecondViewController-被釋放了")
 }
 
 @objc private func buttonClick() {
  thirdViewController.closure = {
   self.test()
  }
  navigationController?.pushViewController(thirdViewController, animated: true)
 }
 
 private func test() {
  print("調(diào)用 test 方法")
 }
}

接下來我們看一下 ThirdViewController 的代碼。在 ThirdViewController 中有一個(gè)按鈕,點(diǎn)擊一下就會(huì)觸發(fā)閉包。同時(shí)我們還實(shí)現(xiàn)了 deinit 方法,用來打印一條語句,看該控制器是否被釋放了。

class ThirdViewController: UIViewController {
 
 private let button: UIButton = {
  let button = UIButton()
  button.setTitleColor(UIColor.black, for: .normal)
  button.setTitle("點(diǎn)擊按鈕", for: .normal)
  button.sizeToFit()
  return button
 }()
 
 var closure: (() -> Void)?
 
 override func viewDidLoad() {
  super.viewDidLoad()
  
  button.center = view.center
  view.addSubview(button)
  button.addTarget(self, action: #selector(buttonClick), for: .touchUpInside)
 }
 
 deinit {
  print("ThirdViewController-被釋放了")
 }
 
 @objc private func buttonClick() {
  closure?()
 }
}

當(dāng)我們連續(xù)推到第三個(gè)控制器,點(diǎn)擊按鈕(觸發(fā)閉包)后,再回到第一個(gè)控制器,看一下三個(gè)控制器的生命周期。當(dāng)流程走完后,發(fā)現(xiàn)控制臺(tái)只有一條語句:

調(diào)用 test 方法

這說明閉包已經(jīng)引起了循環(huán)引用問題,導(dǎo)致第二個(gè)控制器沒能被釋放(內(nèi)存泄漏)。正是因?yàn)殚]包會(huì)導(dǎo)致循環(huán)引用,所以在閉包中調(diào)用對象內(nèi)部的方法時(shí),都要顯式的使用 self,提醒我們要注意可能引起的內(nèi)存泄漏問題。與 Objective-C 不同的是,我們不需要在每一次使用閉包之前再繁瑣的寫上 __weak typeof(self) weakSelf = self; 了,取而代之的是捕獲列表的概念:

@objc private func buttonClick() { 
 thirdViewController.closure = { [weak self] in 
  self?.test()
 }
 navigationController?.pushViewController(thirdViewController, animated: true)
}

再重復(fù)一次上面的流程,可以看到控制臺(tái)多了兩條語句:

調(diào)用 test 方法
SecondViewController-被釋放了
ThirdViewController-被釋放了

只要在捕獲列表中聲明了你想要用弱引用的方式捕獲的對象,就可以及時(shí)的規(guī)避由閉包導(dǎo)致的循環(huán)引用了。但是同時(shí)可以看到,閉包中對于方法的調(diào)用從常規(guī)的 self.test() 變?yōu)榱丝蛇x鏈的 self?.test()。這是因?yàn)榧僭O(shè)閉包在子線程中執(zhí)行,執(zhí)行過程中 self 在主線程隨時(shí)有可能被釋放。由于 self 在閉包中成為了一個(gè)弱引用,因此會(huì)自動(dòng)變?yōu)?nil。在 Swift 中,可選類型的概念讓我們只能以可選鏈的方式來調(diào)用 test。下面修改一下 ThirdViewController 中的代碼:

@objc private func buttonClick() {
 // 模擬網(wǎng)絡(luò)請求
 DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 5) {
  self.closure?()
 }
}

再次執(zhí)行相同的操作步驟,這次我們發(fā)現(xiàn) test 方法沒能正確的得到調(diào)用:

SecondViewController-被釋放了
ThirdViewController-被釋放了

在實(shí)際的項(xiàng)目中,這可能會(huì)導(dǎo)致一些問題,閉包中捕獲的 self 是 weak 的,有可能在閉包執(zhí)行的過程中就被釋放了,導(dǎo)致閉包中的一部分方法被執(zhí)行了而一部分沒有,應(yīng)用的狀態(tài)因此變得不一致。于是這個(gè)時(shí)候就要用到 Weak-Strong Dance 了。

既然知道了 self 在閉包中成為了可選類型,那么除了可選鏈,還可以使用可選綁定來處理可選類型:

@objc private func buttonClick() { 
 thirdViewController.closure = { [weak self] in 
  if let strongSelf = self {
   strongSelf.test()
  } else {
   // 處理 self 被釋放時(shí)的情況。
  }
 }
 navigationController?.pushViewController(thirdViewController, animated: true)
}

但這樣總是會(huì)讓我們在閉包中的代碼多出兩句甚至更多,于是還有更優(yōu)雅的方法,就是使用 guard 語句:

@objc private func buttonClick() { 
 thirdViewController.closure = { [weak self] in 
  guard let strongSelf = self else { return } 
  strongSelf.test()
 }
 navigationController?.pushViewController(thirdViewController, animated: true)
}

一句代碼搞定~

當(dāng)然,有人看到這里會(huì)說,每次都要使用 strongSelf 來調(diào)用 self 的方法,好煩啊……那么這一點(diǎn)還是可以進(jìn)一步被優(yōu)化的,Swift 與 Objective-C 不同,是可以使用部分關(guān)鍵字來聲明變量的,于是我們可以:

@objc private func buttonClick() { 
 thirdViewController.closure = { [weak self] in 
  guard let `self` = self else { return } 
  self.test()
 }
 navigationController?.pushViewController(thirdViewController, animated: true)
}

這樣就可以避免每次書寫 strongSelf 的煩躁感了~

原文地址:Weak-Strong Dance In Swift——如何在 Swift 中優(yōu)雅的處理閉包導(dǎo)致的循環(huán)引用

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Swift并發(fā)系統(tǒng)并行運(yùn)行多個(gè)任務(wù)使用詳解

    Swift并發(fā)系統(tǒng)并行運(yùn)行多個(gè)任務(wù)使用詳解

    這篇文章主要為大家介紹了Swift并發(fā)系統(tǒng)并行運(yùn)行多個(gè)任務(wù)使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • Swift使用編解碼庫Codable的過程詳解

    Swift使用編解碼庫Codable的過程詳解

    Codable 是 Swift 引入的全新的編解碼庫,使開發(fā)者更方便的解析JSON 或 plist 文件,支持枚舉、結(jié)構(gòu)體和類,這篇文章主要介紹了Swift使用編解碼庫Codable,需要的朋友可以參考下
    2023-09-09
  • Swift?Error重構(gòu)優(yōu)化詳解

    Swift?Error重構(gòu)優(yōu)化詳解

    這篇文章主要為大家介紹了Swift?Error的問題解決及重構(gòu)優(yōu)化方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 深入理解Swift中的變量與常量

    深入理解Swift中的變量與常量

    本文主要是介紹Swift中最常用的常量和變量,將從“變量常量的定義”、"如何聲明變量常量"、“變量和常量的命名”,"變量常量的本質(zhì)區(qū)別"四個(gè)方面入手,重點(diǎn)介紹變量和常量的使用以及區(qū)別,希望大家在閱讀完本文后都可以熟練使用它們。有需要的朋友們下面來一起學(xué)習(xí)吧。
    2017-01-01
  • Swift設(shè)計(jì)思想Result<T>與Result<T,?E:?Error>類型解析

    Swift設(shè)計(jì)思想Result<T>與Result<T,?E:?Error>類型解析

    這篇文章主要為大家介紹了Swift設(shè)計(jì)思想Result<T>與Result<T,?E:?Error>的類型示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Swift條件判斷中逗號(hào)的使用方法示例

    Swift條件判斷中逗號(hào)的使用方法示例

    判斷語句是我們?nèi)粘i_發(fā)經(jīng)常會(huì)遇到的一個(gè)功能,下面這篇文章主要給大家介紹了關(guān)于Swift條件判斷中逗號(hào)的使用方法,文中給出了詳細(xì)的示例代碼供大家參考學(xué)習(xí),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • OpenStack的Swift組件詳解

    OpenStack的Swift組件詳解

    這篇文章主要介紹了OpenStack的Swift組件,對swift感興趣的同學(xué),可以參考下
    2021-04-04
  • 理解二叉堆數(shù)據(jù)結(jié)構(gòu)及Swift的堆排序算法實(shí)現(xiàn)示例

    理解二叉堆數(shù)據(jù)結(jié)構(gòu)及Swift的堆排序算法實(shí)現(xiàn)示例

    二插堆即是完全二叉樹,對于排序可以按構(gòu)建最大堆或最小堆的方式來實(shí)現(xiàn),這里我們就來共同理解二叉堆數(shù)據(jù)結(jié)構(gòu)及Swift的堆排序算法實(shí)現(xiàn)示例
    2016-07-07
  • Swift教程之閉包詳解

    Swift教程之閉包詳解

    這篇文章主要介紹了Swift教程之閉包詳解,閉包可以在上下文的范圍內(nèi)捕獲、存儲(chǔ)任何被定義的常量和變量引用,因這些常量和變量的封閉性,而命名為“閉包(Closures)”,需要的朋友可以參考下
    2015-01-01
  • Combine中錯(cuò)誤處理和Scheduler使用詳解

    Combine中錯(cuò)誤處理和Scheduler使用詳解

    這篇文章主要為大家介紹了Combine中錯(cuò)誤處理和Scheduler使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12

最新評(píng)論