🐾

[AnyCancellable]は関数内に置かない方が良いという話

2023/03/10に公開

[AnyCancellable]関数内に配置すると...

当たり前ですが、関数を抜ける時に解放されてしまうので購読が終了してしまいます。
従って下記のコードでは、sink内で値を拾うことができません。

[AnyCancellable]は解放されないところにおきましょう

class Subscriber {    
    private func publisher() -> AnyPublisher<String, Never> {
        return Future { promise in
            promise(.success("AAA"))
        }
        .delay(for: .seconds(1), scheduler: RunLoop.main)
        .eraseToAnyPublisher()
    }
    
    func subscribe() {
        print("start subscribe")
        var cancellables: [AnyCancellable] = []
        
        publisher()
            .sink { print("receive value: \($0)") }
            .store(in: &cancellables)
    }
}

let subscriber = Subscriber()
subscriber.subscribe()

関数内に配置しても値を拾えるパターンがある

実は関数内に配置しても値を拾えるパターンがありました。
同期的なタイミングで値を流す場合です。

class Subscriber {    

    // .delayを消した
    private func publisher() -> AnyPublisher<String, Never> {
        return Future { promise in
            promise(.success("AAA"))
        }
        .eraseToAnyPublisher()
    }
    
    func subscribe() {
        print("start subscribe")
        var cancellables: [AnyCancellable] = []
        
        publisher()
            .sink { print("receive value: \($0)") }
            .store(in: &cancellables)
    }
}

let subscriber = Subscriber()
subscriber.subscribe()

最後に

まず、もともと書かれていたコードの制約で、関数内しか[AnyCancellable]を
配置できませんでした。protocolをextensionする形で実装されていたためです。

API通信をモックで実装していた時は、関数内に[AnyCancellable]を配置してもsinkが実行されていたので、この挙動に気づきませんでした。

関数内に置くことはあんまりないけど、動くしまぁ大丈夫かーくらいで実装を進めてました笑
モックは非同期で値を返すようにしておいたほうが良いですね。

最後まで読んでいただき、ありがとうございました。

GitHubで編集を提案
カラビナテクノロジー デベロッパーブログ

Discussion