# \[iOS] Memory Management

## 變數與常數的生命週期

(1.) Function

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

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

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

(2.) Structure

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

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

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

(3.) Class

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

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

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

## 類別解構函式deinit

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

```swift
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

```swift
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, 則可以正常釋放物件

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

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

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

（2.）經典例子: Apple官方例子

```swift
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`, 則可以正常釋放記憶體

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

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

```swift
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的變數

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

`[unowned self]`可以看做`self!`

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

(2.) weak用於optional的變數

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

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

```swift
onComplete = { [weak self] in
    if let strongSelf = self {
          print("\(strongSelf.name): onComplete!")
    }  
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://frost.gitbook.io/iosbook/ios/ios-book/ios-memory.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
