Fix memory leaks #4329

pull/4330/head
Krypto Pank 3 years ago
parent 50f6354123
commit c50818c60a
  1. 21
      AlphaWallet/Core/TokensAutodetector/AutoDetectTokensOperation.swift
  2. 21
      AlphaWallet/Core/TokensAutodetector/AutoDetectTransactedTokensOperation.swift
  3. 43
      AlphaWallet/Core/TokensAutodetector/TokensAutodetector.swift
  4. 2
      AlphaWallet/InCoordinator.swift
  5. 10
      AlphaWallet/TokenScriptClient/Coordinators/EventSourceCoordinator.swift
  6. 8
      AlphaWallet/TokenScriptClient/Coordinators/EventSourceCoordinatorForActivities.swift
  7. 7
      AlphaWallet/Tokens/Coordinators/TokensCoordinator.swift
  8. 4
      AlphaWallet/Transactions/EtherscanSingleChainTransactionProvider.swift
  9. 4
      AlphaWallet/Transactions/TransactionsService.swift

@ -11,13 +11,11 @@ import PromiseKit
protocol AutoDetectTokensOperationDelegate: class {
var isAutoDetectingTokens: Bool { get set }
func autoDetectTokensImpl(withContracts contractsToDetect: [(name: String, contract: AlphaWallet.Address)], server: RPCServer) -> Promise<Void>
func autoDetectTokensImpl(withContracts contractsToDetect: [(name: String, contract: AlphaWallet.Address)], server: RPCServer) -> Promise<[SingleChainTokensAutodetector.AddTokenObjectOperation]>
}
class AutoDetectTokensOperation: Operation {
private let wallet: AlphaWallet.Address
final class AutoDetectTokensOperation: Operation {
private let tokens: [(name: String, contract: AlphaWallet.Address)]
private let server: RPCServer
weak private var delegate: AutoDetectTokensOperationDelegate?
override var isExecuting: Bool {
@ -29,20 +27,22 @@ class AutoDetectTokensOperation: Operation {
override var isAsynchronous: Bool {
return true
}
private let session: WalletSession
private let tokensDataStore: TokensDataStore
init(forServer server: RPCServer, delegate: AutoDetectTokensOperationDelegate, wallet: AlphaWallet.Address, tokens: [(name: String, contract: AlphaWallet.Address)]) {
init(session: WalletSession, tokensDataStore: TokensDataStore, delegate: AutoDetectTokensOperationDelegate, tokens: [(name: String, contract: AlphaWallet.Address)]) {
self.delegate = delegate
self.wallet = wallet
self.session = session
self.tokens = tokens
self.server = server
self.tokensDataStore = tokensDataStore
super.init()
self.queuePriority = server.networkRequestsQueuePriority
self.queuePriority = session.server.networkRequestsQueuePriority
}
override func main() {
guard let strongDelegate = delegate else { return }
strongDelegate.autoDetectTokensImpl(withContracts: tokens, server: server).done { [weak self] in
strongDelegate.autoDetectTokensImpl(withContracts: tokens, server: session.server).done { [weak self] values in
guard let strongSelf = self else { return }
strongSelf.willChangeValue(forKey: "isExecuting")
@ -50,6 +50,9 @@ class AutoDetectTokensOperation: Operation {
strongDelegate.isAutoDetectingTokens = false
strongSelf.didChangeValue(forKey: "isExecuting")
strongSelf.didChangeValue(forKey: "isFinished")
guard !strongSelf.isCancelled else { return }
strongSelf.tokensDataStore.addTokenObjects(values: values)
}.cauterize()
}
}

@ -11,12 +11,10 @@ import PromiseKit
protocol AutoDetectTransactedTokensOperationDelegate: class {
var isAutoDetectingTransactedTokens: Bool { get set }
func autoDetectTransactedErc20AndNonErc20Tokens(wallet: AlphaWallet.Address) -> Promise<Void>
func autoDetectTransactedErc20AndNonErc20Tokens(wallet: AlphaWallet.Address) -> Promise<[SingleChainTokensAutodetector.AddTokenObjectOperation]>
}
class AutoDetectTransactedTokensOperation: Operation {
private let wallet: AlphaWallet.Address
final class AutoDetectTransactedTokensOperation: Operation {
weak private var delegate: AutoDetectTransactedTokensOperationDelegate?
override var isExecuting: Bool {
@ -29,17 +27,21 @@ class AutoDetectTransactedTokensOperation: Operation {
return true
}
init(forServer server: RPCServer, delegate: AutoDetectTransactedTokensOperationDelegate, wallet: AlphaWallet.Address) {
private let session: WalletSession
private let tokensDataStore: TokensDataStore
init(session: WalletSession, tokensDataStore: TokensDataStore, delegate: AutoDetectTransactedTokensOperationDelegate) {
self.delegate = delegate
self.wallet = wallet
self.session = session
self.tokensDataStore = tokensDataStore
super.init()
self.queuePriority = server.networkRequestsQueuePriority
self.queuePriority = session.server.networkRequestsQueuePriority
}
override func main() {
guard let delegate = delegate else { return }
delegate.autoDetectTransactedErc20AndNonErc20Tokens(wallet: wallet).done { [weak self] _ in
delegate.autoDetectTransactedErc20AndNonErc20Tokens(wallet: session.account.address).done { [weak self] values in
guard let strongSelf = self else { return }
strongSelf.willChangeValue(forKey: "isExecuting")
@ -47,6 +49,9 @@ class AutoDetectTransactedTokensOperation: Operation {
delegate.isAutoDetectingTransactedTokens = false
strongSelf.didChangeValue(forKey: "isExecuting")
strongSelf.didChangeValue(forKey: "isFinished")
guard !strongSelf.isCancelled else { return }
strongSelf.tokensDataStore.addTokenObjects(values: values)
}.cauterize()
}
}

@ -43,9 +43,8 @@ class SingleChainTokensAutodetector: NSObject, TokensAutodetector {
private let assetDefinitionStore: AssetDefinitionStore
private let autoDetectTransactedTokensQueue: OperationQueue
private let autoDetectTokensQueue: OperationQueue
private let server: RPCServer
private let config: Config
private let wallet: Wallet
private let session: WalletSession
private let queue: DispatchQueue
private let tokenObjectFetcher: TokenObjectFetcher
@ -53,8 +52,7 @@ class SingleChainTokensAutodetector: NSObject, TokensAutodetector {
var isAutoDetectingTokens = false
init(
wallet: Wallet,
server: RPCServer,
session: WalletSession,
config: Config,
tokensDataStore: TokensDataStore,
assetDefinitionStore: AssetDefinitionStore,
@ -65,8 +63,7 @@ class SingleChainTokensAutodetector: NSObject, TokensAutodetector {
) {
self.tokenObjectFetcher = tokenObjectFetcher
self.queue = queue
self.wallet = wallet
self.server = server
self.session = session
self.config = config
self.tokensDataStore = tokensDataStore
self.assetDefinitionStore = assetDefinitionStore
@ -90,7 +87,7 @@ class SingleChainTokensAutodetector: NSObject, TokensAutodetector {
guard !isAutoDetectingTransactedTokens else { return }
isAutoDetectingTransactedTokens = true
let operation = AutoDetectTransactedTokensOperation(forServer: server, delegate: self, wallet: wallet.address)
let operation = AutoDetectTransactedTokensOperation(session: session, tokensDataStore: tokensDataStore, delegate: self)
autoDetectTransactedTokensQueue.addOperation(operation)
}
@ -128,7 +125,7 @@ class SingleChainTokensAutodetector: NSObject, TokensAutodetector {
}
private func autoDetectTransactedTokensImpl(wallet: AlphaWallet.Address, erc20: Bool) -> Promise<[SingleChainTokensAutodetector.AddTokenObjectOperation]> {
let server = server
let server = session.server
return firstly {
autoDetectTransactedContractsImpl(wallet: wallet, erc20: erc20, server: server)
@ -149,7 +146,7 @@ class SingleChainTokensAutodetector: NSObject, TokensAutodetector {
private func autoDetectPartnerTokens() {
guard !config.development.isAutoFetchingDisabled else { return }
switch server {
switch session.server {
case .main:
autoDetectMainnetPartnerTokens()
case .xDai:
@ -177,7 +174,7 @@ class SingleChainTokensAutodetector: NSObject, TokensAutodetector {
guard !isAutoDetectingTokens else { return }
isAutoDetectingTokens = true
let operation = AutoDetectTokensOperation(forServer: server, delegate: self, wallet: wallet.address, tokens: contractsToDetect)
let operation = AutoDetectTokensOperation(session: session, tokensDataStore: tokensDataStore, delegate: self, tokens: contractsToDetect)
autoDetectTokensQueue.addOperation(operation)
}
@ -191,10 +188,10 @@ class SingleChainTokensAutodetector: NSObject, TokensAutodetector {
}
private func fetchCreateErc875OrErc20Token(forContract contract: AlphaWallet.Address, forServer server: RPCServer) -> Promise<AddTokenObjectOperation> {
let accountAddress = wallet.address
let accountAddress = session.account.address
let queue = queue
return TokenProvider(account: wallet, server: server)
return TokenProvider(account: session.account, server: server)
.getTokenType(for: contract)
.then(on: queue, { [weak tokenObjectFetcher] tokenType -> Promise<AddTokenObjectOperation> in
guard let tokenObjectFetcher = tokenObjectFetcher else { return .init(error: PMKError.cancelled) }
@ -233,34 +230,28 @@ class SingleChainTokensAutodetector: NSObject, TokensAutodetector {
extension SingleChainTokensAutodetector: AutoDetectTransactedTokensOperationDelegate {
func autoDetectTransactedErc20AndNonErc20Tokens(wallet: AlphaWallet.Address) -> Promise<Void> {
func autoDetectTransactedErc20AndNonErc20Tokens(wallet: AlphaWallet.Address) -> Promise<[SingleChainTokensAutodetector.AddTokenObjectOperation]> {
let fetchErc20Tokens = autoDetectTransactedTokensImpl(wallet: wallet, erc20: true)
let fetchNonErc20Tokens = autoDetectTransactedTokensImpl(wallet: wallet, erc20: false)
return when(resolved: [fetchErc20Tokens, fetchNonErc20Tokens])
.get(on: queue, { [weak tokensDataStore] results in
guard let tokensDataStore = tokensDataStore else { return }
let values = results.compactMap { $0.optionalValue }.flatMap { $0 }
tokensDataStore.addTokenObjects(values: values)
}).asVoid()
.map(on: queue, { results in
return results.compactMap { $0.optionalValue }.flatMap { $0 }
})
}
}
extension SingleChainTokensAutodetector: AutoDetectTokensOperationDelegate {
func autoDetectTokensImpl(withContracts contractsToDetect: [(name: String, contract: AlphaWallet.Address)], server: RPCServer) -> Promise<Void> {
func autoDetectTokensImpl(withContracts contractsToDetect: [(name: String, contract: AlphaWallet.Address)], server: RPCServer) -> Promise<[SingleChainTokensAutodetector.AddTokenObjectOperation]> {
let promises = contractsToAutodetectTokens(withContracts: contractsToDetect, forServer: server)
.map { each -> Promise<AddTokenObjectOperation> in
return fetchCreateErc875OrErc20Token(forContract: each, forServer: server)
}
return when(resolved: promises)
.get(on: queue, { [weak tokensDataStore] results in
guard let tokensDataStore = tokensDataStore else { return }
let values = results.compactMap { $0.optionalValue }
tokensDataStore.addTokenObjects(values: values)
}).asVoid()
.map(on: queue, { results in
return results.compactMap { $0.optionalValue }
})
}
}

@ -35,7 +35,7 @@ class InCoordinator: NSObject, Coordinator, DappRequestHandlerDelegate {
return MultipleChainsTokensDataStore(realm: realm, servers: config.enabledServers)
}()
private lazy var eventSourceCoordinator: EventSourceCoordinatorType = {
private lazy var eventSourceCoordinator: EventSourceCoordinator = {
EventSourceCoordinator(wallet: wallet, tokensDataStore: tokensDataStore, assetDefinitionStore: assetDefinitionStore, eventsDataStore: eventsDataStore, config: config)
}()

@ -17,13 +17,7 @@ extension PromiseKit.Result {
}
}
protocol EventSourceCoordinatorType: AnyObject {
func start()
}
//TODO: Create XMLHandler store and pass it everwhere we use it
//TODO: Rename this generic name to reflect that it's for event instances, not for event activity
class EventSourceCoordinator: NSObject, EventSourceCoordinatorType {
final class EventSourceCoordinator: NSObject {
private var wallet: Wallet
private let tokensDataStore: TokensDataStore
private let assetDefinitionStore: AssetDefinitionStore
@ -64,7 +58,7 @@ class EventSourceCoordinator: NSObject, EventSourceCoordinatorType {
//TODO this is firing twice for each contract. We can be more efficient
assetDefinitionStore.bodyChange
.receive(on: RunLoop.main)
.compactMap { self.tokensDataStore.token(forContract: $0) }
.compactMap { [weak self] in self?.tokensDataStore.token(forContract: $0) }
.sink { [weak self] token in
guard let strongSelf = self else { return }

@ -6,11 +6,7 @@ import PromiseKit
import web3swift
import Combine
protocol EventSourceCoordinatorForActivitiesType: AnyObject {
func start()
}
class EventSourceCoordinatorForActivities: EventSourceCoordinatorForActivitiesType {
final class EventSourceCoordinatorForActivities {
private var wallet: Wallet
private let config: Config
private let tokensDataStore: TokensDataStore
@ -48,7 +44,7 @@ class EventSourceCoordinatorForActivities: EventSourceCoordinatorForActivitiesTy
private func setupWatchingTokenScriptFileChangesToFetchEvents() {
assetDefinitionStore.bodyChange
.receive(on: RunLoop.main)
.compactMap { self.tokensDataStore.token(forContract: $0) }
.compactMap { [weak self] in self?.tokensDataStore.token(forContract: $0) }
.sink { [weak self] token in
guard let strongSelf = self else { return }

@ -166,6 +166,11 @@ class TokensCoordinator: Coordinator {
alertService.start()
}
deinit {
autoDetectTransactedTokensQueue.cancelAllOperations()
autoDetectTokensQueue.cancelAllOperations()
}
private func setupSingleChainTokenCoordinators() {
for session in sessions.values {
let server = session.server
@ -175,7 +180,7 @@ class TokensCoordinator: Coordinator {
}()
let tokensAutodetector: TokensAutodetector = {
SingleChainTokensAutodetector(wallet: session.account, server: server, config: config, tokensDataStore: tokensDataStore, assetDefinitionStore: assetDefinitionStore, withAutoDetectTransactedTokensQueue: autoDetectTransactedTokensQueue, withAutoDetectTokensQueue: autoDetectTokensQueue, queue: tokensAutoDetectionQueue, tokenObjectFetcher: tokenObjectFetcher)
SingleChainTokensAutodetector(session: session, config: config, tokensDataStore: tokensDataStore, assetDefinitionStore: assetDefinitionStore, withAutoDetectTransactedTokensQueue: autoDetectTransactedTokensQueue, withAutoDetectTokensQueue: autoDetectTokensQueue, queue: tokensAutoDetectionQueue, tokenObjectFetcher: tokenObjectFetcher)
}()
let coordinator = SingleChainTokenCoordinator(session: session, keystore: keystore, tokensStorage: tokensDataStore, assetDefinitionStore: assetDefinitionStore, eventsDataStore: eventsDataStore, analyticsCoordinator: analyticsCoordinator, tokenActionsProvider: tokenActionsService, coinTickersFetcher: coinTickersFetcher, activitiesService: activitiesService, alertService: alertService, tokensAutodetector: tokensAutodetector)

@ -282,7 +282,9 @@ class EtherscanSingleChainTransactionProvider: SingleChainTransactionProvider {
firstly {
EtherscanSingleChainTransactionProvider.functional.fetchTransactions(startBlock: startBlock, sortOrder: sortOrder, session: coordinator.session, alphaWalletProvider: coordinator.alphaWalletProvider, tokensDataStore: coordinator.tokensDataStore, queue: coordinator.queue)
}.done { transactions in
}.done { [weak self] transactions in
guard let strongSelf = self else { return }
guard !strongSelf.isCancelled else { return }
coordinator.addOrUpdate(transactions: transactions)
}.catch { e in
error(value: e, rpcServer: coordinator.session.server, address: self.session.account.address)

@ -55,6 +55,10 @@ class TransactionsService {
NotificationCenter.default.addObserver(self, selector: #selector(restartTimers), name: UIApplication.didBecomeActiveNotification, object: nil)
}
deinit {
fetchLatestTransactionsQueue.cancelAllOperations()
}
private func setupSingleChainTransactionProviders() {
providers = sessions.values.map { each in
let providerType = each.server.transactionProviderType

Loading…
Cancel
Save