Frank's Book
  • Introduction
  • Privacy Policy
  • Git
    • [Git]Fully delete a git repository created with init
  • iOS
    • iOS Book
      • [iOS] Swift Coding Style
      • [iOS] UnitTest With XCode
      • [iOS] Build a Universal Framework for iOS using Swift
      • [iOS] Memory Management
      • [iOS] GCD And Operation Queue
      • [iOS] MVVM
      • [iOS] CoreData
      • [iOS] KVO & KVC
      • [iOS] Asynchronous Operation
      • [iOS] URLSession
      • [iOS] NSTimer & CADisplayLink
      • [iOS] UITextField & UITestView
      • [iOS] UITableView & UICollectionView
      • [iOS] Codable
      • [iOS] Array的sort, filter, map, reduce 函式
      • [iOS] Class vs Struct
      • [iOS] Reference Type vs Value Type
      • [iOS] Object
      • [iOS] IBDesignable & IBInspectable
  • Jenkins
    • Jenkins & Docker
      • [Docker] Docker Commands
      • [Docker] 1. Setup Jenkins with Docker on Mac
      • [Docker] 2. Jenkins建立Mac子節點
      • [Docker] 3. Mac節點build Xcode project設定
      • [Jenkins] 1. Install Jenkins
      • [Jenkins] 2. Change Default User Of Jenkins
      • [Jenkins] 3. Integration with Git
      • [Jenkins] 4. Integration with XCode
      • [Jenkins] 5. Integration with Unit Test
      • [Jenkins] 6. Uninstall Jenkins
      • [Jenkins] 7. Jenkins Commands
      • [Jenkins] 8. Jenkins Plugins
      • [Jenkins] 9. Problem Solving
  • Flutter
    • Flutter Book
      • [Flutter] Update your Flutter path (Mac OS)
      • [Flutter] Release Command
      • [Flutter] Life Cycles
      • [Flutter] AppLifecycleState
      • [Flutter] Navigator Pop時回傳資料
      • [Flutter] Install appium-flutter-driver
  • Leet Code
    • LeetCode Solutions
      • [LeetCode] 1. Two Sum [Easy]
      • [LeetCode] 2. Add Two Numbers [Medium]
      • [LeetCode] 3. Longest Substring Without Repeating Characters [Medium]
      • [LeetCode] 5. Longest Palindromic Substring [Medium]
      • [LeetCode] 7. Reverse Integer [Easy] [LeetCode]
      • [LeetCode] 8. String to Integer (atoi)
      • [LeetCode] 11. Container With Most Water
      • [LeetCode] 13. Roman to Integer
      • [LeetCode]14. Longest Common Prefix
      • [LeetCode] 15. 3Sum
      • [LeetCode] 17. Letter Combinations of a Phone Number
      • [LeetCode] 19. Remove Nth Node From End of List
      • [LeetCode] 20. Valid Parentheses
      • [LeetCode] 21. Merge Two Sorted Lists
      • [LeetCode] 22. Generate Parentheses
      • [LeetCode] 26. Remove Duplicates from Sorted Array
      • [LeetCode] 28. Implement strStr()
      • [LeetCode] 33. Search in Rotated Sorted Array
      • [LeetCode] 34. Find First and Last Position of Element in Sorted Array
      • [LeetCode]36. Valid Sudoku
      • [LeetCode] 38. Count and Say
      • [LeetCode] 46. Permutations
      • [LeetCode] 48. Rotate Image
      • [LeetCode] 49. Group Anagrams
  • Git
    • Git Commands
      • [LeetCode] 50. Pow(x, n)
  • About Author
    • Frank Chen
Powered by GitBook
On this page
  • 變數與常數的生命週期
  • 類別解構函式deinit
  • Reference Cycle
  • unowned 與 weak
  1. iOS
  2. iOS Book

[iOS] Memory Management

變數與常數的生命週期

(1.) Function

Function的參數, 區塊{ }內的變數與常數會隨著執行任務的結束而釋放記憶體空間

func test(text: String) {
    var value1: Int = 1
    let value2: Int = 2
}

test("haha")
//此時釋放 text, value1, value2 記憶體

(2.) Structure

Structure區塊{ }內的變數與常數會隨著Structure變數不再被參考到而釋放記憶體空間

struct Test {
    var value1: Int = 1
    let value2: Int = 2
}

var test: Test? = Test()
test = nil   //此時釋放 value1, value2 記憶體

(3.) Class

同樣的, Class區塊{ }內的變數與常數會隨著Class變數不再被參考到而釋放記憶體空間

Class Test {
    var value1: Int = 1
    let value2: Int = 2
}

var test: Test? = Test()
test = nil   //此時釋放 value1, value2 記憶體

類別解構函式deinit

deinit{} 會在Class release前被呼叫, 我們可以利用這個function觀察Class是否有被釋放或執行一些動作

class Test {
    var title: String

    init(title: String) {
        self.title = name
        println("class is init")
    }

    deinit {
        println("Class is released")
    }
}

var test = Test(name: "Hemingway")        //output: "class is init"
test = nil                                //output: "Class is released"

Reference Cycle

Swift是遵循著自動引用計數 (ARC) 的規則, 通常當一個物件沒有被其他對象引用時會自動被銷毀, 但有些情況下物件會互相參考, 行成Reference Cycle, 造成物件的記憶體永遠不會被釋放.

(1.) 經典例子: Closure

Class Test {
  typealias Complete = ()->()
  var name: String
  var onComplete : Complete?

  init(_ name: String) {
    self.name = name
    onComplete = {
      print("\(self.name): onComplete!") // --> Closure引用self, reference count + 1
    }
  }

  deinit {
    print("deinit: \(self.name)")
  }
}

var a: Test? = Test("A") // --> reference count + 1
a = nil // reference count: 2 - 1 = 1, 還剩下1故無法釋放

Reference Cycle: self擁有了onComplete, onComplete也擁有self

Solution:

若是在release Test 物件之前, 先release onComplete, 則可以正常釋放物件

var b : Test? = Test("B") // reference count = 2
b?.onComplete = nil // --> reference count - 1
b = nil //reference count - 1, reference count = 0, 被釋放

但有更好的做法, 使用unowned 或weak關鍵字

onComplete = { [unowned self] in
  print("\(self.name): onComplete!") // --> 不會強制持有 self
}

(2.)經典例子: Apple官方例子

class User {
  var name : String
  var lover: User?

  init(_ name: String){
    self.name = name
  }

  func setLover(_ user: User) {
    lover = user
  }

  deinit {
    print("deinit: \(self.name)")
  }
}

var A = User("A")
var B = User("B")

A.setLover(B)  //互相引用, reference cycle
B.setLover(A)

A = nil  // 因為B.lover引用了A, 所以a不會正確被釋放
print(B?.lover!)  //此時A還存在
B = nil //  因為B也等於nil了, 沒有人可以再參考到A.lover與B.lover, 記憶體永遠無法被釋放了

Solution:

在 A = nil 之後 把B?.lover也設成nil, 則可以正常釋放記憶體

A = nil  
B?.lover = nil // 無人再參考到A, A被回收了
B = nil // B 也可以順利回收了

但有更好的做法, 使用weak關鍵字

class User {
  var name : String
  weak var lover: User?

  init(_ name: String){
    self.name = name
  }

  func setLover(_ user: User) {
    lover = user
  }

  deinit {
    print("deinit: \(self.name)")
  }
}

var A = User("A")
var B = User("B")

A.setLover(B)  //互相引用, reference cycle
B.setLover(A)

A = nil  // B.lover引用了A, 但因為是weak, 所以也會被釋放
print(B?.lover!)  //此時A不存在了
B = nil //  A, B皆釋放了

unowned 與 weak

(1.) unowned用於非optional的變數

unowned 通常都用於該物件不會比或持有它的物件更早被release,

[unowned self]可以看做self!

 onComplete = { [unowned self] in
     print("\(self.name): onComplete!") // --> unowned self
 }

(2.) weak用於optional的變數

如遇到網路request, user在 response回來之前就返回上一頁, 使的物件有可能會比或持有它的物件更早被release, 則必須使用weak, [weak self]可以看做self?

(若使用unowned 此時會造成crash)

onComplete = { [weak self] in
    if let strongSelf = self {
          print("\(strongSelf.name): onComplete!")
    }  
}
Previous[iOS] Build a Universal Framework for iOS using SwiftNext[iOS] GCD And Operation Queue

Last updated 6 years ago