1. 程式人生 > >swift 閉包引用迴圈中的迷魂陣

swift 閉包引用迴圈中的迷魂陣

         相信大家都知道,閉包使用不當,會造成引用迴圈,從而造成App的記憶體洩露。但是有時候,看起來會造成引用迴圈的程式碼實際上並沒有造成引用迴圈。這個需要我們正確的區分。

【栗子1】請大家看看下面的程式碼,分析請看註釋:

class ClassA {
    let power =2
    var transform: (Int ->Int)?
    var dataArr = [1,2,3,4]
    deinit {
    print("ClassA例項被銷燬")
    }
}


var a: ClassA =ClassA()   // ClassA例項的引用計數為1
a.transform = {  numin
    returna.power * num   //閉包捕獲a, 則ClassA例項的引用計數 增加1,現在為2 ;同時,a.transform指向該閉包,閉包的引用計數為1
}
a.transform!(5)
a = ClassA()<span style="font-family: Arial, Helvetica, sans-serif;"> //將a重新賦值。原來的ClassA例項的引用計數減一。原來的ClassA例項的引用計數為1。</span>

       由此得出結論:執行的最後,原來的ClassA例項持有一個指向閉包的引用;閉包也持有一個指向原來ClassA例項的引用。從而造成引用迴圈。

似乎如此。如圖:


                                                       圖 1-1

       但是,當我們把這段程式碼拿到playground上執行,結果卻發現我們的判斷是錯誤的——最先建立的那個ClassA例項被正常銷燬了。為什麼會這樣呢?

       原因就是,閉包捕獲的是閉包外部變數的引用,是引用,是引用,重要的事情說3遍!如果那個變數本身是引用,那麼,閉包捕獲的就是那個引用的引用了。所以在這裡,閉包捕獲的是變數a的引用,並不是ClassA例項的引用。也就沒有造成引用迴圈。如圖


                                                       圖 1-2

         相信大家現在明白了吧。

【栗子2】接下來,大家看看這個程式碼:

class ClassA {
    let power =2
    lazyvar transform: (Int ->Int) = {num in num *self.power}
    var dataArr = [1,2,3,4]
    deinit {
    print("ClassA例項被銷燬")
    }
}

var a =ClassA()
a.transform(8)
a =ClassA()

       這段程式碼跟例子1的程式碼很相似。但是在閉包裡用的是self。在playground上執行,發現最開始的ClassA例項沒有被正常銷燬,這是由於其真正造成了記憶體洩露。為什麼呢?

       self,代表當前的例項。所以,在閉包中使用了self,使得閉包捕獲了當前例項的引用,從而持有了指向當前例項的引用。從而造成引用迴圈。如圖


                                                       圖 2-1

【栗子3】可能有人列印過閉包內外變數的地址,發現它們的地址是一樣的。如圖


                                                                                        圖 3-1

       當我們分別在閉包外和閉包內列印變數a的地址的時候,發現這兩個的地址一樣的。這個可以在inout關鍵字中一樣。如圖


                                                                                        圖 3-2

       swift 中,inout是將值傳遞給函式,在函式執行完後,將這個值替換掉原來的那個值。從而達到使用inout能起到引用傳遞修改值的效果。我們在函式內外都對實參的地址進行了列印,結果兩者是一摸一樣的。圖3-1中,閉包捕獲的引數跟外部的地址一樣,估計是蘋果做了些處理。

備註:本文下的Xcode的版本為7.3.1。

相關推薦

swift 引用迴圈迷魂陣

         相信大家都知道,閉包使用不當,會造成引用迴圈,從而造成App的記憶體洩露。但是有時候,看起來會造成引用迴圈的程式碼實際上並沒有造成引用迴圈。這個需要我們正確的區分。 【栗子1】請大家看看下面的程式碼,分析請看註釋: class ClassA {     l

swift迴圈引用

例子是一個簡單通訊錄,列表介面點選新增聯絡人按鈕跳到新增介面,新增聯絡人後返回到列表介面ListVC,但是新增介面DetailVC沒有釋放 // // ListVC.swift // contract // // Created by targetcloud on 2

迴圈的函式

1.es5中的函式迴圈遍歷 由於在es5中沒有塊級作用域,會導致一些問題 //迴圈中的函式 var func = []; //這是個陣列 for(var i=0;i<10;i++){ console.log(i);//列印了

swift 迴圈引用的解決辦法

模擬網路請求,封裝工具類,使用閉包變數對閉包進行強引用 // // NetworkRequestTool.swift // Test // // Created by fe on 2017/2/28. // Copyright © 2017年 fe. All ri

Swift3.0 -- 迴圈引用與OC的對比

import UIKit class ViewController: UIViewController { var a: (() -> ())? override func viewDidLoad() { su

Swift 反向傳值

反向 size spa 控制器 nbsp name str tail string Swift中閉包反向傳值 1.第二控制器申明一個閉包類型 typealias BackBlock = (String) -> Void 2.第二控制器定義一個變量 var Bac

swift新手詳解(新手必看)

alt 關鍵字 itl 代碼塊 回調 類型 例子 圖片 使用 閉包的含義 閉包是自包含的函數代碼塊,可以在代碼中被傳遞和使用。Swift 中的閉包與 C 和 Objective-C 中的代碼塊(blocks)以及其他一些編程語言中的匿名函數比較相似。

swift - -定義和使用

ESS alias str call order ray ssr .com 定義類 方法一: 1.定義 typealias OpenOrderSuccessResultBlock = ( _ dataArray:[String])->Void 2.類方法實現屬性

【轉】Python關鍵語法-:函式的函式用法例項

本文例項講述了Python閉包的用法。分享給大家供大家參考,具體如下: Python函式中也可以定義函式,也就是閉包。跟js中的閉包概念其實差不多,舉個Python中閉包的例子。 def make_adder(addend): def adder(augend

javascript 的立即呼叫函式模式、及es6的塊級作用域

先來看一個在牛客上看到的面試題: 這裡一開始會以為是不就是隔1秒輸出i的值嗎,最後結果就是輸出0~9 的十個數字呀,真的是太young了。 但是真是擼了一遍程式碼,控制檯輸出刺眼的10個10,what? 這個查了資料是說因為這個函式為每一個i都設定了一個計時器,那麼

前端知乎:關於阮一峰部落格《學習Javascript》章節最後兩個思考題

阮一峰部落格:《學習Javascript閉包》章節中最後有個思考題: 如果你能理解下面兩段程式碼的執行結果,應該就算理解閉包的執行機制了。 程式碼片段一 var name = "The Window"; var object = { name: "My Obj

swift_040(Swift宣告與用途)

一、閉包的概念 閉包其實是oc裡面的block,語法格式不一樣,但作用是一樣的。主要是用於callBack(非同步回撥)或者兩個類之間的通訊。它的本質一個函式,一個可執行的程式碼塊,只是這個函式是沒有名字的,也就是匿名函式。你也可以把他看作如 int、float一樣,是一種資料型別,一種可以作為引數傳遞的資

JS導致迴圈給按鈕新增事件時總是執行最後一個

今天再做需求時有一個功能是這樣的,就是有不定個的按鈕,且點選按鈕時都需要執行一個方法(引數不一樣) 那麼我很自然的就想到了,迴圈給每個按鈕新增事件和引數就好了,由於不方便上傳系統程式碼,下面以一個簡單例子來說明. <pre name="code" class="ht

SWIFT,介紹,使用(ALAMOFIRE封裝 非同步請求)

閉包說明: 首先說明簡明扼要的說明一下:閉包,可以看做 JAVA中匿名函式。 我們來看APPLE文件:一個函式,可以看做一個特殊的閉包巢狀函式,是一個捕獲其所在函式中上下文的閉包閉包表示式,是一個寫法奇特,可以捕獲上下文變數的【匿名閉包】 那麼讓我們開始:        我

Swift --表示式與(彙編分析)

在Swift中,可以通過func定義一個函式,也可以通過閉包表示式定義一個函式! 一、閉包表示式 概念 閉包表示式與定義函式的語法相對比,有區別如下: 去除了func 去除函式名 返回值型別添加了關鍵字in { }放在形參列表的前邊 閉包表示式的形式如下: {    (引數列表) -> 返回值

swift解決迴圈引用的幾種方式

import UIKit class ViewController: UIViewController { // VC --strong -- 閉包 // 閉包- strong -- VC

swift-防止迴圈引用的方法

迴圈引用場景描述有兩個類HttpTool類和ViewController類,ViewController有HttpTool屬性,HttpTool中有callBack型別的閉包做屬性;即ViewController引用了HttpTool例項,HttpTool裡引用了callBa

Swift學習記錄 -- 14.的使用和解決迴圈引用方法

Swift中的閉包 , 幾乎和OC中的block一模一樣 , 我個人又比較偏好block , 所以覺得閉包還是蠻不錯的 . 在迴圈引用問題上 , 解決方案也更加簡潔 // HttpTool類

Swift 的Closures()詳解

mount light sca ring 需要 line rem sin 代碼 Swift 中的Closures(閉包)詳解 在Swift沒有發布之前,所有人使用OC語言編寫Cocoa上的程序,而其中經常被人們討論的其中之一 -- Block 一直備受大家的喜愛。在Swif

iOS開發之OC與swift開發混編教程,代理的相互呼叫,block的實現。OC呼叫Swift的代理, OC呼叫Swift的Block

  本文章將從兩個方向分別介紹 OC 與 swift 混編   1. 第一個方向從 swift工程 中引入 oc類    1. 1 如何在swift的類中使用oc類    1.2  如何在swift中實現oc的代理方法  &