diff --git a/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift b/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift index 4ad78d07e6..6734d0d8db 100644 --- a/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift +++ b/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift @@ -109,7 +109,7 @@ private final class ContentNode: ASDisplayNode { strongSelf.updateImage(image: image, size: size, spacing: spacing) } }) - self.disposable = disposable + self.disposable = disposable.strict() } else { let image = generateImage(size, rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) @@ -342,7 +342,7 @@ public final class AnimatedAvatarSetView: UIView { strongSelf.updateImage(image: image, size: size, spacing: spacing) } }) - self.disposable = disposable + self.disposable = disposable.strict() } else { let image = generateImage(size, rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) diff --git a/submodules/AnimatedStickerNode/Sources/DirectAnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/DirectAnimatedStickerNode.swift index ca911ce2c6..b597911dfa 100644 --- a/submodules/AnimatedStickerNode/Sources/DirectAnimatedStickerNode.swift +++ b/submodules/AnimatedStickerNode/Sources/DirectAnimatedStickerNode.swift @@ -129,7 +129,7 @@ public final class DirectAnimatedStickerNode: ASDisplayNode, AnimatedStickerNode strongSelf.setupPlayback(lottieInstance: lottieInstance) } - }) + }).strict() } private func updatePlayback() { diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 766a54d4de..0415cd0747 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -94,7 +94,7 @@ private final class IconComponent: Component { } else { self?.image = image } - }) + }).strict() } else { if let tintColor = component.tintColor { self.image = generateTintedImage(image: UIImage(bundleImageName: component.name), color: tintColor, backgroundColor: nil) @@ -1042,7 +1042,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { let _ = strongSelf.update(layout: layout, buttons: strongSelf.buttons, isSelecting: strongSelf.isSelecting, elevateProgress: strongSelf.elevateProgress, transition: .immediate) } } - }) + }).strict() } deinit { diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index d6ad8d1dc9..a8516a55e7 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -84,7 +84,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail |> distinctUntilChanged |> deliverOnMainQueue).start(next: { [weak self] state in self?.updateState(state: state) - }) + }).strict() } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index 554f931678..7b7ed71609 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -563,7 +563,7 @@ public final class AvatarNode: ASDisplayNode { return } self.imageNode.contents = image?.cgImage - }) + }).strict() } } } diff --git a/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift b/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift index a4b1f77650..5c3060be6e 100644 --- a/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift +++ b/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift @@ -180,7 +180,7 @@ public final class AvatarVideoNode: ASDisplayNode { strongSelf.animationFile = file strongSelf.setupAnimation() } - }) + }).strict() case let .sticker(packReference, fileId): self.fileDisposable = (self.context.engine.stickers.loadedStickerPack(reference: packReference, forceActualized: false) |> map { pack -> TelegramMediaFile? in @@ -194,7 +194,7 @@ public final class AvatarVideoNode: ASDisplayNode { strongSelf.animationFile = file strongSelf.setupAnimation() } - }) + }).strict() } } diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift index 900d33e343..bdc23e1a58 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift @@ -1116,7 +1116,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz strongSelf.updateActionButton() } - }) + }).strict() self.addSubnode(self.actionButtonPanelNode) self.actionButtonPanelNode.addSubnode(self.actionButtonPanelSeparator) @@ -1140,7 +1140,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz strongSelf.passwordTip = hint } } - }) + }).strict() self.actionButtonPanelSeparator.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor self.actionButtonPanelNode.backgroundColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentMethodSheet.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentMethodSheet.swift index f93c6e9fc6..3ebc536d6d 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentMethodSheet.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentMethodSheet.swift @@ -49,7 +49,7 @@ final class BotCheckoutPaymentMethodSheetController: ActionSheetController { if let strongSelf = self { strongSelf.theme = ActionSheetControllerTheme(presentationData: presentationData) } - }) + }).strict() var items: [ActionSheetItem] = [] diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentShippingOptionSheetController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentShippingOptionSheetController.swift index 4c00294d07..10b86ab94f 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentShippingOptionSheetController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentShippingOptionSheetController.swift @@ -20,7 +20,7 @@ final class BotCheckoutPaymentShippingOptionSheetController: ActionSheetControll if let strongSelf = self { strongSelf.theme = ActionSheetControllerTheme(presentationData: presentationData) } - }) + }).strict() var items: [ActionSheetItem] = [] diff --git a/submodules/BotPaymentsUI/Sources/BotReceiptControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotReceiptControllerNode.swift index 319e8b4e5c..6100325d62 100644 --- a/submodules/BotPaymentsUI/Sources/BotReceiptControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotReceiptControllerNode.swift @@ -319,7 +319,7 @@ final class BotReceiptControllerNode: ItemListControllerNode { strongSelf.receiptData.set(.single((receipt.invoice, receipt.info, receipt.shippingOption, receipt.credentialsTitle, receipt.invoiceMedia, receipt.tipAmount))) } - }) + }).strict() self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside) diff --git a/submodules/BrowserUI/Sources/BrowserScreen.swift b/submodules/BrowserUI/Sources/BrowserScreen.swift index b59a23ab54..95e06060e3 100644 --- a/submodules/BrowserUI/Sources/BrowserScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserScreen.swift @@ -293,7 +293,7 @@ public class BrowserScreen: ViewController { } strongSelf.contentState = state strongSelf.requestLayout(transition: .immediate) - }) + }).strict() self.content?.onScrollingUpdate = { [weak self] update in self?.onContentScrollingUpdate(update) diff --git a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift index 3de647da08..8ce880ad55 100644 --- a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift +++ b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift @@ -87,7 +87,7 @@ private final class MediaPreviewView: SimpleLayer { } strongSelf.contents = image.cgImage } - }) + }).strict() } } } @@ -1185,7 +1185,7 @@ public final class CalendarMessageScreen: ViewController { return } strongSelf.calendarSource.loadMore() - }) + }).strict() self.stateDisposable = (self.calendarSource.state |> deliverOnMainQueue).start(next: { [weak self] state in @@ -1194,7 +1194,7 @@ public final class CalendarMessageScreen: ViewController { } strongSelf.calendarState = state strongSelf.reloadMediaInfo() - }) + }).strict() } deinit { diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 029beeaa00..aeeafc34e2 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -138,7 +138,7 @@ public final class CallListController: TelegramBaseController { strongSelf.updateThemeAndStrings() } } - }) + }).strict() self.scrollToTop = { [weak self] in self?.controllerNode.scrollToLatest() diff --git a/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift b/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift index a7b16cf5c4..4e8268e557 100644 --- a/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift +++ b/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift @@ -198,7 +198,7 @@ final class ChatListContainerItemNode: ASDisplayNode { } if let filter, case let .filter(id, _, _, data) = filter, data.isShared { - self.pollFilterUpdatesDisposable = self.context.engine.peers.pollChatFolderUpdates(folderId: id).start() + self.pollFilterUpdatesDisposable = self.context.engine.peers.pollChatFolderUpdates(folderId: id).start().strict() self.chatFilterUpdatesDisposable = (self.context.engine.peers.subscribedChatFolderUpdates(folderId: id) |> deliverOnMainQueue).start(next: { [weak self] result in guard let self else { @@ -221,7 +221,7 @@ final class ChatListContainerItemNode: ASDisplayNode { self.updateLayout(size: size, insets: insets, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .animated(duration: 0.4, curve: .spring)) } } - }) + }).strict() } if case let .forum(peerId) = location { @@ -242,7 +242,7 @@ final class ChatListContainerItemNode: ASDisplayNode { self.updateLayout(size: size, insets: insets, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .animated(duration: 0.4, curve: .spring)) } } - }) + }).strict() } } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index c9e9055c8d..87eb202ef9 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -401,7 +401,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController strongSelf.tabBarItem.badgeValue = compactNumericCountString(Int(count.0), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) } } - }) + }).strict() self.presentationDataDisposable = (context.sharedContext.presentationData |> deliverOnMainQueue).start(next: { [weak self] presentationData in @@ -416,7 +416,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController strongSelf.updateThemeAndStrings() } } - }) + }).strict() if !previewing { /* diff --git a/submodules/ChatListUI/Sources/ChatListEmptyNode.swift b/submodules/ChatListUI/Sources/ChatListEmptyNode.swift index 3f303e42e9..89244fcee2 100644 --- a/submodules/ChatListUI/Sources/ChatListEmptyNode.swift +++ b/submodules/ChatListUI/Sources/ChatListEmptyNode.swift @@ -149,7 +149,7 @@ final class ChatListEmptyNode: ASDisplayNode { if let (size, insets) = self.validLayout { self.updateLayout(size: size, insets: insets, transition: .immediate) } - }) + }).strict() } } diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 8cd0bfa31e..9c3002c1ae 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -501,7 +501,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo strongSelf.updateTheme(theme: presentationData.theme) } } - }) + }).strict() if case let .forum(peerId) = location { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index e8c7b61eb7..4838fde890 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -2656,7 +2656,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } }) } - }) + }).strict() self.recentListNode.beganInteractiveDragging = { _ in interaction.dismissInput() @@ -2733,7 +2733,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } strongSelf.playlistLocation = playlistStateAndType?.1.playlistLocation - }) + }).strict() } self.deletedMessagesDisposable = (context.account.stateManager.deletedMessages @@ -2758,7 +2758,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { return state } } - }) + }).strict() } deinit { @@ -3056,7 +3056,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } }, completed: { - }) + }).strict() cancelImpl = { self?.playlistPreloadDisposable?.dispose() } diff --git a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift index d8b21b9fd6..dd0ea13f4e 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift @@ -683,7 +683,7 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate { for (_, itemNode) in strongSelf.visibleMediaItems { itemNode.updateHiddenMedia() } - }) + }).strict() } deinit { diff --git a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift index 504608aaf2..d045de8389 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift @@ -134,7 +134,7 @@ private final class ChatListSearchPendingPane { |> deliverOnMainQueue).start(next: { [weak self] _ in self?.isReady = true hasBecomeReady(key) - }) + }).strict() } deinit { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 72a595d0ba..b61603b2b5 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1832,7 +1832,7 @@ public final class ChatListNode: ListView { |> distinctUntilChanged self.suggestedChatListNotice.set(suggestedChatListNoticeSignal) - }) + }).strict() let storageInfo: Signal if !"".isEmpty, case .chatList(groupId: .root) = location, chatListFilter == nil { @@ -2723,7 +2723,7 @@ public final class ChatListNode: ListView { return state } } - }) + }).strict() self.reorderItem = { [weak self] fromIndex, toIndex, transactionOpaqueState -> Signal in guard let strongSelf = self, let filteredEntries = (transactionOpaqueState as? ChatListOpaqueTransactionState)?.chatListView.filteredEntries else { diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift index ac5f097cc3..21e5be95e1 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift @@ -75,7 +75,7 @@ public final class ChatSendMessageActionSheetController: ViewController { strongSelf.controllerNode.updatePresentationData(presentationData) } } - }) + }).strict() self.statusBar.statusBarStyle = .Hide self.statusBar.ignoreInCall = true diff --git a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift index be84d84abf..6cf3f9b459 100644 --- a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift +++ b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift @@ -93,7 +93,7 @@ public final class ReactionIconView: PortalSourceView { } strongSelf.file = files[fileId] strongSelf.reloadFile() - }) + }).strict() } } diff --git a/submodules/Components/ReactionImageComponent/Sources/ReactionImageComponent.swift b/submodules/Components/ReactionImageComponent/Sources/ReactionImageComponent.swift index e55c7738e7..659bc02ddf 100644 --- a/submodules/Components/ReactionImageComponent/Sources/ReactionImageComponent.swift +++ b/submodules/Components/ReactionImageComponent/Sources/ReactionImageComponent.swift @@ -151,7 +151,7 @@ public final class ReactionImageNode: ASDisplayNode { strongSelf.iconNode.image = image } } - }) + }).strict() } else if let file = file { self.size = file.dimensions?.cgSize ?? displayPixelSize self.isAnimation = false @@ -169,7 +169,7 @@ public final class ReactionImageNode: ASDisplayNode { strongSelf.iconNode.image = image } } - }) + }).strict() } else { self.size = displayPixelSize self.isAnimation = false diff --git a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift index 754b885bca..777a849ebc 100644 --- a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift +++ b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift @@ -163,7 +163,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent } strongSelf.file = file strongSelf.updateReactionLayer() - }) + }).strict() } } else { let iconNode = ASImageNode() @@ -507,7 +507,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent } strongSelf.file = file strongSelf.updateReactionLayer() - }) + }).strict() } } else { self.file = nil @@ -825,7 +825,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } - }) + }).strict() } deinit { diff --git a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift index 36b6ad5601..f5bc7187af 100644 --- a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift +++ b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift @@ -267,7 +267,7 @@ open class ViewControllerComponentContainer: ViewController { strongSelf.containerLayoutUpdated(layout, transition: .immediate) } } - }) + }).strict() switch statusBarStyle { case .none: diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 9d8e81aa1d..9bc2728322 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -1385,7 +1385,7 @@ public final class ContactListNode: ASDisplayNode { } } } - }) + }).strict() self.listNode.didEndScrolling = { [weak self] _ in if let strongSelf = self { diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index e40623fe5a..6a57501471 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -184,7 +184,7 @@ public class ContactsController: ViewController { strongSelf.updateThemeAndStrings() } } - }) + }).strict() if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { self.authorizationDisposable = (combineLatest(DeviceAccess.authorizationStatus(subject: .contacts), combineLatest(context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.permissionWarningKey(permission: .contacts)!), context.account.postbox.preferencesView(keys: [PreferencesKeys.contactsSettings]), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.contactSynchronizationSettings])) @@ -211,7 +211,7 @@ public class ContactsController: ViewController { strongSelf.tabBarItem.badgeValue = status != .allowed && !suppressed ? "!" : nil strongSelf.sortOrderPromise.set(.single(sortOrder)) } - }) + }).strict() } else { self.sortOrderPromise.set(context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.contactSynchronizationSettings]) |> map { sharedData -> ContactsSortOrder in diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 39a1df45f2..ade1b4a403 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -143,7 +143,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { strongSelf.updateThemeAndStrings() } } - }) + }).strict() addNearbyImpl = { [weak self] in if let strongSelf = self { @@ -243,7 +243,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { self.contactListNode.storySubscriptions.set(.single(storySubscriptions)) self.storiesReady.set(.single(true)) - })*/ + }).strict()*/ self.contactListNode.openStories = { [weak self] peer, sourceNode in guard let self else { diff --git a/submodules/ContactListUI/Sources/InviteContactsController.swift b/submodules/ContactListUI/Sources/InviteContactsController.swift index 24e19d6efd..00c58c2de9 100644 --- a/submodules/ContactListUI/Sources/InviteContactsController.swift +++ b/submodules/ContactListUI/Sources/InviteContactsController.swift @@ -67,7 +67,7 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr strongSelf.updateThemeAndStrings() } } - }) + }).strict() self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search, activate: { [weak self] in self?.activateSearch() diff --git a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift index 089090e508..572a01cba4 100644 --- a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift @@ -325,7 +325,7 @@ final class InviteContactsControllerNode: ASDisplayNode { strongSelf.updateThemeAndStrings() } } - }) + }).strict() let selectionStateSignal = self.selectionStatePromise.get() let transition: Signal @@ -428,7 +428,7 @@ final class InviteContactsControllerNode: ASDisplayNode { self.disposable = transition.start(next: { [weak self] transition in self?.enqueueTransition(transition) - }) + }).strict() shareImpl = { [weak self] in if let strongSelf = self { diff --git a/submodules/ContextUI/Sources/ContextActionNode.swift b/submodules/ContextUI/Sources/ContextActionNode.swift index 83918597a5..4aa8dd42fe 100644 --- a/submodules/ContextUI/Sources/ContextActionNode.swift +++ b/submodules/ContextUI/Sources/ContextActionNode.swift @@ -210,7 +210,7 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol { return } strongSelf.iconNode.image = image - }) + }).strict() } } diff --git a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift index 7b85202d98..aa8489a6e1 100644 --- a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift +++ b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift @@ -804,7 +804,7 @@ final class ContextActionsContainerNode: ASDisplayNode { strongSelf.tip = tip requestLayout() - }) + }).strict() } } diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index f3d8aeca84..be14401569 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -2540,7 +2540,7 @@ public final class ContextController: ViewController, StandalonePresentableContr return } strongSelf.dismiss(result: .default, completion: {}) - }) + }).strict() case let .reference(referenceSource): self.statusBar.statusBarStyle = .Ignore @@ -2552,7 +2552,7 @@ public final class ContextController: ViewController, StandalonePresentableContr return } strongSelf.dismiss(result: .default, completion: {}) - }) + }).strict() case let .extracted(extractedSource): if extractedSource.blurBackground { self.statusBar.statusBarStyle = .Hide @@ -2567,7 +2567,7 @@ public final class ContextController: ViewController, StandalonePresentableContr return } strongSelf.dismiss(result: .default, completion: {}) - }) + }).strict() case .controller: self.statusBar.statusBarStyle = .Hide } diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index 413322b028..fe479f20d6 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -296,7 +296,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin return } strongSelf.iconNode.image = image - }) + }).strict() } } else if let image = self.iconNode.image { iconSize = image.size @@ -1122,7 +1122,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { } strongSelf.tip = tip requestUpdate(.immediate) - }) + }).strict() } } diff --git a/submodules/DateSelectionUI/Sources/DateSelectionActionSheetController.swift b/submodules/DateSelectionUI/Sources/DateSelectionActionSheetController.swift index d8fe0af380..ef88e03178 100644 --- a/submodules/DateSelectionUI/Sources/DateSelectionActionSheetController.swift +++ b/submodules/DateSelectionUI/Sources/DateSelectionActionSheetController.swift @@ -26,7 +26,7 @@ public final class DateSelectionActionSheetController: ActionSheetController { if let strongSelf = self { strongSelf.theme = ActionSheetControllerTheme(presentationData: presentationData) } - }) + }).strict() self._ready.set(.single(true)) diff --git a/submodules/Display/Source/Navigation/NavigationOverlayContainer.swift b/submodules/Display/Source/Navigation/NavigationOverlayContainer.swift index 68401608a0..a8c358a5f9 100644 --- a/submodules/Display/Source/Navigation/NavigationOverlayContainer.swift +++ b/submodules/Display/Source/Navigation/NavigationOverlayContainer.swift @@ -56,7 +56,7 @@ final class NavigationOverlayContainer: ASDisplayNode { strongSelf.isReadyUpdated?() } } - }) + }).strict() } deinit { diff --git a/submodules/DrawingUI/Sources/StickerPickerScreen.swift b/submodules/DrawingUI/Sources/StickerPickerScreen.swift index 15ae4c16f7..5d22cd40e1 100644 --- a/submodules/DrawingUI/Sources/StickerPickerScreen.swift +++ b/submodules/DrawingUI/Sources/StickerPickerScreen.swift @@ -559,7 +559,7 @@ public class StickerPickerScreen: ViewController { } else { strongSelf.gifMode = hasRecentGifs ? .recent : .trending } - }) + }).strict() self.trendingGifsPromise.set(.single(nil)) self.trendingGifsPromise.set(paneGifSearchForQuery(context: context, query: "", offset: nil, incompleteResults: true, delayRequest: false, updateActivity: nil) diff --git a/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift b/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift index 047eb744f5..79991c26d8 100644 --- a/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift +++ b/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift @@ -402,7 +402,7 @@ public final class ChatMediaInputTrendingPane: ChatMediaInputPane { strongSelf.didSetReady = true strongSelf._ready.set(.single(Void())) } - }) + }).strict() } public override func updateLayout(size: CGSize, topInset: CGFloat, bottomInset: CGFloat, isExpanded: Bool, isVisible: Bool, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition) { diff --git a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift index 6c76dfbc00..39b428ba5e 100644 --- a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift +++ b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift @@ -422,7 +422,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { strongSelf.didSetReady = true strongSelf._ready.set(.single(true)) } - }) + }).strict() self.controller?.searchNavigationNode?.setQueryUpdated({ [weak self] query, languageCode in guard let strongSelf = self else { diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index b08a7983b7..95db9e40ee 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -1120,7 +1120,7 @@ public class GalleryController: ViewController, StandalonePresentableController, if let strongSelf = self, strongSelf.traceVisibility() { let _ = strongSelf.context.engine.messages.addSecretChatMessageScreenshot(peerId: id.peerId).start() } - }) + }).strict() } default: break diff --git a/submodules/GalleryUI/Sources/GalleryPagerNode.swift b/submodules/GalleryUI/Sources/GalleryPagerNode.swift index fbb87ce7c9..aafe2f0409 100644 --- a/submodules/GalleryUI/Sources/GalleryPagerNode.swift +++ b/submodules/GalleryUI/Sources/GalleryPagerNode.swift @@ -156,7 +156,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest if let strongSelf = self { strongSelf.pagingEnabled = pagingEnabled } - }) + }).strict() } deinit { diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 411c2b64a3..a8b05c2d62 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -516,7 +516,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte strongSelf.pictureInPictureController?.invalidatePlaybackState() } } - }) + }).strict() } deinit { @@ -938,7 +938,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } else { strongSelf.footerContentNode.setFramePreviewImage(image: nil) } - }) + }).strict() self.alternativeDismiss = { [weak self] in guard let strongSelf = self, strongSelf.hasPictureInPicture else { @@ -973,7 +973,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if let strongSelf = self { strongSelf.updateControlsVisibility(false) } - }) + }).strict() } deinit { diff --git a/submodules/SSignalKit/SwiftSignalKit/Source/Disposable.swift b/submodules/SSignalKit/SwiftSignalKit/Source/Disposable.swift index 28fa733282..e1ec9fe473 100644 --- a/submodules/SSignalKit/SwiftSignalKit/Source/Disposable.swift +++ b/submodules/SSignalKit/SwiftSignalKit/Source/Disposable.swift @@ -4,6 +4,32 @@ public protocol Disposable: AnyObject { func dispose() } +public final class StrictDisposable: Disposable { + private let disposable: Disposable + private let isDisposed = Atomic(value: false) + + public init(_ disposable: Disposable) { + self.disposable = disposable + } + + deinit { + #if DEBUG + assert(self.isDisposed.with({ $0 })) + #endif + } + + public func dispose() { + let _ = self.isDisposed.swap(true) + self.disposable.dispose() + } +} + +public extension Disposable { + func strict() -> Disposable { + return StrictDisposable(self) + } +} + final class _EmptyDisposable: Disposable { func dispose() { } diff --git a/submodules/SSignalKit/SwiftSignalKit/Source/Signal.swift b/submodules/SSignalKit/SwiftSignalKit/Source/Signal.swift index cdde287573..2078378f73 100644 --- a/submodules/SSignalKit/SwiftSignalKit/Source/Signal.swift +++ b/submodules/SSignalKit/SwiftSignalKit/Source/Signal.swift @@ -23,18 +23,37 @@ public func |> (value: T, function: ((T) -> U)) -> U { return function(value) } -private final class SubscriberDisposable : Disposable { +private final class SubscriberDisposable: Disposable, CustomStringConvertible { private let subscriber: Subscriber - private let disposable: Disposable - init(subscriber: Subscriber, disposable: Disposable) { + private var lock = pthread_mutex_t() + private var disposable: Disposable? + + init(subscriber: Subscriber, disposable: Disposable?) { self.subscriber = subscriber self.disposable = disposable + + pthread_mutex_init(&self.lock, nil) + } + + deinit { + pthread_mutex_destroy(&self.lock) } func dispose() { - subscriber.markTerminatedWithoutDisposal() - disposable.dispose() + self.subscriber.markTerminatedWithoutDisposal() + + var disposeItem: Disposable? + pthread_mutex_lock(&self.lock) + disposeItem = self.disposable + self.disposable = nil + pthread_mutex_unlock(&self.lock) + + disposeItem?.dispose() + } + + public var description: String { + return "SubscriberDisposable { disposable: \(self.disposable == nil ? "nil" : "hasValue") }" } } diff --git a/submodules/SSignalKit/SwiftSignalKit/Source/Subscriber.swift b/submodules/SSignalKit/SwiftSignalKit/Source/Subscriber.swift index bb5a68a08f..8f6041d973 100644 --- a/submodules/SSignalKit/SwiftSignalKit/Source/Subscriber.swift +++ b/submodules/SSignalKit/SwiftSignalKit/Source/Subscriber.swift @@ -1,13 +1,21 @@ import Foundation -public final class Subscriber { +#if DEBUG +// Signals keep themselves in memory until terminated (dispose, putError, putCompletion) +private final class LiveSubscribers { + var dict: [ObjectIdentifier: AnyObject] = [:] +} +private let liveSubscribers = Atomic(value: LiveSubscribers()) +#endif + +public final class Subscriber: CustomStringConvertible { private var next: ((T) -> Void)! private var error: ((E) -> Void)! private var completed: (() -> Void)! private var lock = pthread_mutex_t() private var terminated = false - internal var disposable: Disposable! + internal var disposable: Disposable? public init(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) { self.next = next @@ -16,6 +24,10 @@ public final class Subscriber { pthread_mutex_init(&self.lock, nil) } + public var description: String { + return "Subscriber { next: \(self.next == nil ? "nil" : "hasValue"), error: \(self.error == nil ? "nil" : "hasValue"), completed: \(self.completed == nil ? "nil" : "hasValue"), disposable: \(self.disposable == nil ? "nil" : "hasValue"), terminated: \(self.terminated) }" + } + deinit { var freeDisposable: Disposable? pthread_mutex_lock(&self.lock) @@ -34,6 +46,12 @@ public final class Subscriber { } internal func assignDisposable(_ disposable: Disposable) { + #if DEBUG + liveSubscribers.with { impl in + //let _ = impl.dict[ObjectIdentifier(self)] = self + } + #endif + var dispose = false pthread_mutex_lock(&self.lock) if self.terminated { @@ -49,14 +67,33 @@ public final class Subscriber { } internal func markTerminatedWithoutDisposal() { + var freeDisposable: Disposable? + pthread_mutex_lock(&self.lock) if !self.terminated { self.terminated = true self.next = nil self.error = nil self.completed = nil + + if let disposable = self.disposable { + freeDisposable = disposable + self.disposable = nil + } } pthread_mutex_unlock(&self.lock) + + if let freeDisposableValue = freeDisposable { + withExtendedLifetime(freeDisposableValue, { + }) + freeDisposable = nil + } + + #if DEBUG + liveSubscribers.with { impl in + let _ = impl.dict.removeValue(forKey: ObjectIdentifier(self)) + } + #endif } public func putNext(_ next: T) { @@ -86,7 +123,6 @@ public final class Subscriber { self.terminated = true disposeDisposable = self.disposable self.disposable = nil - } pthread_mutex_unlock(&self.lock) @@ -97,6 +133,12 @@ public final class Subscriber { if let disposeDisposable = disposeDisposable { disposeDisposable.dispose() } + + #if DEBUG + liveSubscribers.with { impl in + let _ = impl.dict.removeValue(forKey: ObjectIdentifier(self)) + } + #endif } public func putCompletion() { @@ -141,5 +183,11 @@ public final class Subscriber { if let disposeDisposable = disposeDisposable { disposeDisposable.dispose() } + + #if DEBUG + liveSubscribers.with { impl in + let _ = impl.dict.removeValue(forKey: ObjectIdentifier(self)) + } + #endif } }