NotificationCenter.addObserver(…)のクロージャはリークしている？

誤りがあれば指摘してください。

## NotificationCenter.addObserver(forName:object:queue:using:)で登録されるクロージャがリークしているのではないかという問題

一度だけNotificationを受けたい時の使用例として次のような例が示されている。

```swift
let center = NSNotificationCenter.defaultCenter()
var token: NSObjectProtocol!
token = center.addObserverForName("OneTimeNotification", object: nil, queue: nil) { (note) in

    print("Received the notification!")
    center.removeObserver(token)
}
```

この、クロージャが一体いつ解放されるのかを調べてみた。

### テスト１

クロージャにキャプチャされたインスタンスが解放されるタイミングで調べてみる。

まず、deinitされたらそれと分かるクラスを用意した。

```swift
class DeinitTester: CustomStringConvertible {
    
    deinit {
        print("#########  deinit  #########")
    }
    
    var description: String { return "alive" }
}
```

このクラスのインスタンスをクロージャにキャプチャさせて観察してみる。

```swift

let n = Notification.Name("Notification")

do {
    
    let tester = DeinitTester()
    
    var token: NSObjectProtocol!
    token = NotificationCenter.default
        .addObserver(forName: n,
                     object: nil,
                     queue: nil) { _ in
                        
                        
                        print(tester)
                        
                        NotificationCenter.default.removeObserver(token)
    }
}

let no = Notification(name: n)

NotificationCenter.default.post(no)    // print "alive"
NotificationCenter.default.post(no)    // print nothing
```

インスタンスは解放されることはなかった。

###コメントで @karupanerura さまにリークしない方法を教えていただきました


```swift

let n = Notification.Name("Notification")

do {
    
    let tester = DeinitTester()
    
    weak var token: NSObjectProtocol?    // weakにする
    token = NotificationCenter.default
        .addObserver(forName: n,
                     object: nil,
                     queue: nil) { _ in
                        
                        print(tester)
                        
                        token.map(NotificationCenter.default.removeObserver)
    }
}

let no = Notification(name: n)

print(0)
NotificationCenter.default.post(no)    // print "alive" and tester is released
print(1)
NotificationCenter.default.post(no)    // print nothing
print(2)
```

出力

```
0
alive
#########  deinit  #########
1
2
```

`token`を`weak`とすることで、`removeObserver(_:)`が実行されるとクロージャが解放されることがわかりました。

これ、Appleの[リファレンス](https://developer.apple.com/documentation/foundation/notificationcenter/1411723-addobserver)の例文でも`weak`としてないので気を付けましょう。

----
以下はいろいろ考えたがまったく間違っていたお話です。無視してください。

### テストのテスト

クロージャに対する理解が間違えていないか、以下のようにクロージャが解放される時の動作を調査した。

```swift

do {
    var f: (() -> ())?
    
    do {
        
        let tester = DeinitTester()
        
        f = { print(tester) }
        
        f?()
        
        print("end nest 2")
    }
    
    print("end nest 1")
}

print("finish")

```

出力は以下のようになり、予想どおりの段階でクロージャが解放され、同時にキャプチャされたインスタンスが解放されるのが確認された。

```
alive
end nest 2
end nest 1
#########  deinit  #########
finish
```

### さらなるテスト（テスト２）
では、`addObserver(forName:object:queue:using:)`が返すいわゆるtokenを無効化すればどうなるのかを調査してみた。

```swift
var token: NSObjectProtocol?
do {
    
    let tester = DeinitTester()
    
    token = NotificationCenter.default
        .addObserver(forName: n,
                     object: nil,
                     queue: nil) { _ in
                        
                        
                        print(tester)
                        
                        NotificationCenter.default.removeObserver(token!)
    }
}

let no = Notification(name: n)

NotificationCenter.default.post(no)    // print "alive"
NotificationCenter.default.post(no)    // print nothing

print("befor")
token = nil
print("after")
```
出力

```
alive
befor
#########  deinit  #########
after
```

いわゆるtokenが無効化されるタイミングでクロージャが解放されるのが確認された。

あえて「無効化」と書いたのは、tokenはクロージャがキャプチャしているので、このタイミングで解放されるわけがなく、変数tokenがnilになっただけだからである。

なぜテスト１と違う結果になったのか理由がわからない。
テスト１でもdoブロックを抜けた段階で変数tokenは無効化されているはずである。

### 追加でテスト（テスト３）

`removeObserver()`が呼ばれる前にtokenを無効化するとどうなるのかを調査。

```swift
var token: NSObjectProtocol?
do {
    
    let tester = DeinitTester()
    
    token = NotificationCenter.default
        .addObserver(forName: n,
                     object: nil,
                     queue: nil) { _ in
                        
                        
                        print(tester)
                        
                        NotificationCenter.default.removeObserver(token!)
    }
}

token = nil

```

クロージャは解放されなかった。

### さらに追加（テスト４）

では、そのあとにNotificationが発行されたらどうなるか調査。

```swift
var token: NSObjectProtocol?
do {
    
    let tester = DeinitTester()
    
    token = NotificationCenter.default
        .addObserver(forName: n,
                     object: nil,
                     queue: nil) { _ in
                        
                        
                        print(tester)
                        
                        NotificationCenter.default.removeObserver(token!)  // fatal error: unexpectedly found nil while unwrapping an Optional value
    }
}

token = nil

let no = Notification(name: n)
NotificationCenter.default.post(no)

```
tokenの強制アンラップに失敗してしまった。


###最後のテスト（テスト５）

runloop内で何かしてるかもしれないのでrunloopを回してみる。

```swift
do {
    
    let tester = DeinitTester()
    
    var token: NSObjectProtocol?
    token = NotificationCenter.default
        .addObserver(forName: n,
                     object: nil,
                     queue: nil) { _ in
                        
                        
                        print(tester)
                        
                        NotificationCenter.default.removeObserver(token!)
    }
}

let no = Notification(name: n)
NotificationCenter.default.post(no)

RunLoop.main.run(until: Date(timeIntervalSinceNow: 2))
```
クロージャは解放されなかった。


##考察
テスト１ではdoブロックの中でtokenを宣言していたので、doブロックを抜けた段階でtokenは無効化されるはずであるが、当然ながらここではクロージャは解放されていない。されては困る。
さらに、`removeObserver()`が呼ばれた段階でもクロージャは解放されなかった。

にもかかわらず、テスト２では変数tokenが無効化される段階でクロージャは解放された。

またしかし、テスト３、テスト４から闇雲にtokenを無効化することはまずいこともわかった。

## 結論
意味がわからない。

何が起こっているの？
どうすればいいの？