简体   繁体   中英

Memory leak when using CombineLatest in Swift Combine

I am using the Redux pattern for building a messaging application. Everything works fine so far but then I notice a memory leak in some parts of the app that I'm unable to solve. My view controller that binds to messages publisher. Deinit won't get called when the view controller is dismissed.

        let messages = {
            store.$state
                .map { $0.chatState.messagesByChannel[self.channelId] }
                .removeDuplicates()
                .eraseToAnyPublisher()
        }()

        messages.combineLatest(Just("Hello world"))
            .sink { [weak self] (messages, state) in

        }
        .store(in: &cancellableSet)

When I changed from referencing a dictionary object to another object in the chat state deinit gets called

        let chatRoomDetailResponse = {
            store.$state
            .map { $0.chatState.getChatRoomDetailResponse }
                .removeDuplicates()
                .eraseToAnyPublisher()
        }()

        chatRoomDetailResponse.combineLatest(Just("Hello world"))
            .sink { [weak self] (messages, state) in

        }
        .store(in: &cancellableSet)

This is a small snapshot of my store:

final public class Store<State: FluxState>: ObservableObject {
    @Published public var state: State

    private var dispatchFunction: DispatchFunction!
    private let reducer: Reducer<State>

and my ChatState:


public struct ChatState: FluxState {

    public typealias ChannelID = String

    public var messagesByChannel: [ChannelID: [Message]] = [:]

    public var getChatRoomDetailResponse: NetworkResponse<ChatChannel>? = nil
}

$0.chatState.messagesByChannel[self.channelId] is capturing self strongly, for the sake of being able to access its most-up-to-date channelId value.

Either catpure self weakly:

.map { [weak self] in 
    guard let strongSelf = self else  { return ??? }
    $0.chatState.messagesByChannel[strongSelf.channelId]
}

Or if channelId doesn't change, you can use a capture list to capture it by value:

.map { [channelId] in $0.chatState.messagesByChannel[channelId] }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM