亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Swift開發必備技巧:內存管理、weak和unowned

2019-11-08 02:59:58
字體:
來源:轉載
供稿:網友

因為 Playground 本身會持有所有聲明在其中的東西,因此本節中的示例代碼需要在 Xcode 項目環境中運行。在 Playground 中可能無法得到正確的結果。

不管在什么語言里,內存管理的內容都很重要,所以我打算花上比其他 tip 長一些的篇幅仔細地說說這塊內容。

Swift 是自動管理內存的,這也就是說,我們不再需要操心內存的申請和分配。當我們通過初始化創建一個對象時,Swift 會替我們管理和分配內存。而釋放的原則遵循了自動引用計數 (ARC) 的規則:當一個對象沒有引用的時候,其內存將會被自動回收。這套機制從很大程度上簡化了我們的編碼,我們只需要保證在合適的時候將引用置空 (比如超過作用域,或者手動設為 nil 等),就可以確保內存使用不出現問題。

但是,所有的自動引用計數機制都有一個從理論上無法繞過的限制,那就是循環引用 (retain cycle) 的情況。

什么是循環引用

雖然我覺得循環引用這樣的概念介紹不太應該出現在這本書中,但是為了更清晰地解釋 Swift 中的循環引用的一般情況,這里還是簡單進行說明。假設我們有兩個類 A 和 B , 它們之中分別有一個存儲屬性持有對方:

class A {	let b: B	init() {		b = B()		b.a = self	}	deinit {		PRintln("A deinit")	}}class B {	var a: A? = nil	deinit {		println("B deinit")	}}

在 A 的初始化方法中,我們生成了一個 B 的實例并將其存儲在屬性中。然后我們又將 A 的實例賦值給了 b.a 。這樣 a.b 和 b.a 將在初始化的時候形成一個引用循環?,F在當有第三方的調用初始化了 A ,然后即使立即將其釋放, A 和 B 兩個類實例的 deinit 方法也不會被調用,說明它們并沒有被釋放。

func application(application: UIApplication!, 				 didFinishLaunchingWithOptions launchOptions: NSDictionary!) 				 -> Bool {	// Override point for customization after application launch.	var obj: A? = A()	obj = nil	// 內存沒有釋放	return true}

因為即使 obj 不再持有 A 的這個對象,b 中的 b.a 依然引用著這個對象,導致它無法釋放。而進一步,a 中也持有著 b,導致 b 也無法釋放。在將 obj 設為 nil 之后,我們在代碼里再也拿不到對于這個對象的引用了,所以除非是殺掉整個進程,我們已經 永遠 也無法將它釋放了。多么悲傷的故事啊..

在 Swift 里防止循環引用

為了防止這種人神共憤的悲劇的發生,我們必須給編譯器一點提示,表明我們不希望它們互相持有。一般來說我們習慣希望 "被動" 的一方不要去持有 "主動" 的一方。在這里 b.a 里對 A 的實例的持有是由 A 的方法設定的,我們在之后直接使用的也是 A 的實例,因此認為 b 是被動的一方??梢詫⑸厦娴?nbsp;class B 的聲明改為:

class B {    weak var a: A? = nil    deinit {        println("B deinit")    }}

在 var a 前面加上了 weak ,向編譯器說明我們不希望持有 a。這時,當 obj 指向 nil 時,整個環境中就沒有對 A 的這個實例的持有了,于是這個實例可以得到釋放。接著,這個被釋放的實例上對 b 的引用 a.b 也隨著這次釋放結束了作用域,所以 b 的引用也將歸零,得到釋放。添加 weak 后的輸出:

A deinitB deinit

可能有心的朋友已經注意到,在 Swift 中除了 weak 以外,還有另一個沖著編譯器叫喊著類似的 "不要引用我" 的標識符,那就是 unowned 。它們的區別在哪里呢?如果您是一直寫 Objective-C 過來的,那么從表面的行為上來說 unowned 更像以前的 unsafe_unretained ,而 weak 就是以前的 weak 。用通俗的話說,就是 unowned 設置以后即使它原來引用的內容已經被釋放了,它仍然會保持對被已經釋放了的對象的一個 "無效的" 引用,它不能是 Optional 值,也不會被指向 nil。如果你嘗試調用這個引用的方法或者訪問成員屬性的話,程序就會崩潰。而 weak 則友好一些,在引用的內容被釋放后,標記為 weak 的成員將會自動地變成 nil (因此被標記為 @ weak的變量一定需要是 Optional 值)。關于兩者使用的選擇,Apple 給我們的建議是如果能夠確定在訪問時不會已被釋放的話,盡量使用 unowned ,如果存在被釋放的可能,那就選擇用 weak 。

我們結合實際編碼中的使用來看看選擇吧。日常工作中一般使用弱引用的最常見的場景有兩個:

設置 delegate 時在 self 屬性存儲為閉包時,其中擁有對 self 引用時

前者是 Cocoa 框架的常見設計模式,比如我們有一個負責網絡請求的類,它實現了發送請求以及接收請求結果的任務,其中這個結果是通過實現請求類的 protocol 的方式來實現的,這種時候我們一般設置 delegate 為 weak :

// RequestManager.swiftclass RequestManager: RequestHandler {	func requestFinished() {		println("請求完成")	}	func sendRequest() {		let req = Request()		req.delegate = self		req.send()	}}// Request.swift@objc protocol RequestHandler {	optional func requestFinished()}class Request {	weak var delegate: RequestHandler!;	func send() {		// 發送請求		// 一般來說會將 req 的引用傳遞給網絡框架	}	func gotResponse() {		// 請求返回		delegate?.requestFinished?()	}}

req 中以 weak 的方式持有了 delegate,因為網絡請求是一個異步過程,很可能會遇到用戶不愿意等待而選擇放棄的情況。這種情況下一般都會將 RequestManager 進行清理,所以我們其實是無法保證在拿到返回時作為 delegate 的 RequestManager 對象是一定存在的。因此我們使用了 weak 而非 unowned ,并在調用前進行了判斷。

閉包和循環引用

另一種閉包的情況稍微復雜一些:我們首先要知道,閉包中對任何其他元素的引用都是會被閉包自動持有的。如果我們在閉包中寫了 self 這樣的東西的話,那我們其實也就在閉包內持有了當前的對象。這里就出現了一個在實際開發中比較隱蔽的陷阱:如果當前的實例直接或者間接地對這個閉包又有引用的話,就形成了一個 self -> 閉包 -> self 的循環引用。最簡單的例子是,我們聲明了一個閉包用來以特定的形式打印 self 中的一個字符串:

class Person {	let name: String	lazy var printName: ()->() = {		println("The name is /(self.name)")	}	init(personName: String) {		name = personName	}	deinit {		println("Person deinit /(self.name)")	}}func application(application: UIApplication!, 		didFinishLaunchingWithOptions launchOptions: NSDictionary!) 		-> Bool {	// Override point for customization after application launch.	var xiaoMing: Person = Person(personName: "XiaoMing")	xiaoMing.printName()	return true}// 輸出:// The name is XiaoMing

printName 是 self 的屬性,會被 self 持有,而它本身又在閉包內持有 self ,這導致了 xiaoMing 的 deinit 在自身超過作用域后還是沒有被調用,也就是沒有被釋放。為了解決這種閉包內的循環引用,我們需要在閉包開始的時候添加一個標注,來表示這個閉包內的某些要素應該以何種特定的方式來使用。可以將 printName 修改為這樣:

lazy var printName: ()->() = {    [weak self] in    if let strongSelf = self {        println("The name is /(strongSelf.name)")    }}

現在內存釋放就正確了:

// 輸出:// The name is XiaoMing// Person deinit XiaoMing

如果我們可以確定在整個過程中 self 不會被釋放的話,我們可以將上面的 weak 改為unowned ,這樣就不再需要 strongSelf 的判斷。但是如果在過程中 self 被釋放了而printName 這個閉包沒有被釋放的話 (比如 生成 Person 后,某個外部變量持有了 printName ,隨后這個 Person 對象被釋放了,但是 printName 已然存在并可能被調用),使用 unowned 將造成崩潰。在這里我們需要根據實際的需求來決定是使用 weak 還是 unowned 。

這種在閉包參數的位置進行標注的語法結構是將要標注的內容放在原來參數的前面,并使用中括號括起來。如果有多個需要標注的元素的話,在同一個中括號內用逗號隔開,舉個例子:

// 標注前{ (number: Int) -> Bool in	//...	return true}// 標注后{ [unowned self, weak someObject] (number: Int) -> Bool in	//...	return true}	
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
黄网动漫久久久| 亚洲娇小xxxx欧美娇小| 亚洲v日韩v综合v精品v| 亚洲国产日韩欧美综合久久| 亚洲国产一区二区三区四区| 亚洲欧美日韩久久久久久| 国产亚洲一区二区精品| 91av视频在线免费观看| 欧美黄色三级网站| 日韩欧美高清视频| 少妇高潮久久久久久潘金莲| 亚洲国产精品美女| 欧美重口另类videos人妖| 国产精品第一视频| 日韩电影大全免费观看2023年上| 国产一区二区日韩| 亚洲欧美一区二区三区在线| 国产精品日日摸夜夜添夜夜av| 久久精品久久精品亚洲人| 97国产在线视频| 欧美一区二区三区……| 国产一区深夜福利| 日韩av免费在线看| 日韩中文视频免费在线观看| 午夜精品久久久久久99热| 国产视频在线一区二区| 在线播放日韩欧美| 两个人的视频www国产精品| 国产精品日韩专区| 午夜精品久久久久久久久久久久久| 97精品国产97久久久久久免费| 国产高清在线不卡| 色综合五月天导航| 欧美精品做受xxx性少妇| 北条麻妃一区二区三区中文字幕| 欧美一区视频在线| 日韩在线中文字| 超薄丝袜一区二区| 日韩欧美在线免费观看| 在线国产精品视频| 68精品久久久久久欧美| 国产精品久久久久久婷婷天堂| 久久精品99国产精品酒店日本| 亚洲精品黄网在线观看| 国产一区二区三区直播精品电影| 日韩精品免费电影| 欧洲美女7788成人免费视频| 亚洲人成网站777色婷婷| 欧美性xxxx极品高清hd直播| 亚洲人成电影在线| 欧美肥婆姓交大片| 美日韩丰满少妇在线观看| 日韩精品中文字幕有码专区| 国产视频丨精品|在线观看| 国产精品爽黄69天堂a| 国产女人精品视频| 亚洲999一在线观看www| 精品久久久久久中文字幕一区奶水| 国产精品美女主播在线观看纯欲| 亚洲第五色综合网| 日本精品一区二区三区在线| 精品久久久久久久久久久久久久| 亚洲图片欧美日产| 国产婷婷色综合av蜜臀av| 欧美国产一区二区三区| 成人免费淫片视频软件| 欧美日韩免费在线观看| 欧美一级电影久久| 精品国产乱码久久久久酒店| 成人h视频在线| 91精品国产高清久久久久久| 亚洲女人初尝黑人巨大| 久久久久久九九九| 国产一区二区三区网站| 欧美日韩一区二区三区在线免费观看| 国产成人涩涩涩视频在线观看| 欧美黑人一区二区三区| 国产精品电影网| 欧美资源在线观看| 久久精品欧美视频| 亚洲影院污污.| 91精品国产91久久久久久最新| 亚洲字幕在线观看| 在线观看精品国产视频| 欧美主播福利视频| 久久九九精品99国产精品| 欧美成人免费小视频| 懂色av影视一区二区三区| 国产精品69av| 精品视频在线播放色网色视频| 国产91精品在线播放| 欧美成人手机在线| 国产精品视频成人| 成人激情电影一区二区| 午夜欧美大片免费观看| 亚洲视频一区二区三区| 疯狂做受xxxx高潮欧美日本| 欧美老肥婆性猛交视频| 亚洲精品aⅴ中文字幕乱码| 国产午夜精品理论片a级探花| 日韩成人中文电影| 永久555www成人免费| 性色av一区二区咪爱| 久久精品视频网站| 欧美成人精品一区| 国产日韩综合一区二区性色av| 亚洲欧美成人精品| 久久久久久久久久av| 17婷婷久久www| 欧美日韩性生活视频| 欧美大片免费观看| 国产精品视频免费在线| 菠萝蜜影院一区二区免费| 欧美老女人www| 一区二区三区美女xx视频| 国产一区二区三区在线视频| 久久精品亚洲精品| 91av视频在线免费观看| 久久久国产精品一区| 亚洲精品久久久久| 国产日韩在线观看av| 高清欧美性猛交| 九九热最新视频//这里只有精品| 热久久99这里有精品| 成年人精品视频| 成人黄色免费在线观看| 久久久噜噜噜久久久| 一区二区三区无码高清视频| 国内精品久久久久久久| 中文字幕精品网| 久久久久久久久久亚洲| 欧美黑人极品猛少妇色xxxxx| 欧美高清在线视频观看不卡| 久久久久国产视频| 国产日韩欧美在线看| 久久久99免费视频| 亚洲欧美激情在线视频| 亚洲一区免费网站| 日韩精品极品视频免费观看| 久久久久久伊人| 国语自产精品视频在免费| 亚洲美女又黄又爽在线观看| 亚洲亚裔videos黑人hd| 亚洲www在线观看| 色综合亚洲精品激情狠狠| 久久夜精品香蕉| 日本欧美在线视频| 亚洲伊人久久大香线蕉av| 国产成人精品免费久久久久| 亚洲精品456在线播放狼人| 国产精品丝袜久久久久久不卡| 隔壁老王国产在线精品| 亚洲精品456在线播放狼人| 日韩高清电影好看的电视剧电影| 欧美另类老女人| 69av在线视频| 亚洲激情视频在线播放| 国产精品高潮粉嫩av| 国产精品福利无圣光在线一区| 亚洲老司机av| 欧美久久久精品| 91久久久久久久久久| 日韩中文字幕不卡视频| 久久久999精品|