diff --git a/Riot/Managers/Analytics/DecryptionFailureTracker.h b/Riot/Managers/Analytics/DecryptionFailureTracker.h index ffefe0d994..b2dbbfc774 100644 --- a/Riot/Managers/Analytics/DecryptionFailureTracker.h +++ b/Riot/Managers/Analytics/DecryptionFailureTracker.h @@ -32,7 +32,7 @@ /** The delegate object to receive analytics events. */ -@property (nonatomic) id delegate; +@property (nonatomic, weak) id delegate; /** Report an event unable to decrypt. diff --git a/Riot/Modules/Common/Recents/RecentsViewController.h b/Riot/Modules/Common/Recents/RecentsViewController.h index b0c993f1e8..6a475073a8 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.h +++ b/Riot/Modules/Common/Recents/RecentsViewController.h @@ -40,7 +40,7 @@ FOUNDATION_EXPORT NSString *const RecentsViewControllerDataReadyNotification; /** Current alert (if any). */ - UIAlertController *currentAlert; + __weak UIAlertController *currentAlert; /** The list of the section headers currently displayed in the recents table. diff --git a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m index 8480205af8..56b3caf917 100644 --- a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m +++ b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m @@ -50,7 +50,7 @@ @interface SegmentedViewController () NSLayoutConstraint *leftMarkerViewConstraint; // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. - id kThemeServiceDidChangeThemeNotificationObserver; + __weak id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -183,9 +183,13 @@ - (void)viewDidLoad [self createSegmentedViews]; + MXWeakify(self); + // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + MXStrongifyAndReturnIfNil(self); + [self userInterfaceThemeDidChange]; }]; diff --git a/Riot/Modules/Communities/GroupsViewController.m b/Riot/Modules/Communities/GroupsViewController.m index 916c344ee8..d58d78c2f3 100644 --- a/Riot/Modules/Communities/GroupsViewController.m +++ b/Riot/Modules/Communities/GroupsViewController.m @@ -27,10 +27,10 @@ @interface GroupsViewController () BOOL isRefreshPending; // Observe UIApplicationDidEnterBackgroundNotification to cancel editing mode when app leaves the foreground state. - id UIApplicationDidEnterBackgroundNotificationObserver; + __weak id UIApplicationDidEnterBackgroundNotificationObserver; // Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar. - id kAppDelegateDidTapStatusBarNotificationObserver; + __weak id kAppDelegateDidTapStatusBarNotificationObserver; MXHTTPOperation *currentRequest; @@ -39,7 +39,7 @@ @interface GroupsViewController () UISearchBar *tableSearchBar; // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. - id kThemeServiceDidChangeThemeNotificationObserver; + __weak id kThemeServiceDidChangeThemeNotificationObserver; } @end @@ -98,11 +98,15 @@ - (void)viewDidLoad self.groupsTableView.estimatedSectionHeaderHeight = 30; self.groupsTableView.estimatedSectionFooterHeight = 0; + MXWeakify(self); + // Observe UIApplicationDidEnterBackgroundNotification to refresh bubbles when app leaves the foreground state. UIApplicationDidEnterBackgroundNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + MXStrongifyAndReturnIfNil(self); + // Leave potential editing mode - [self cancelEditionMode:isRefreshPending]; + [self cancelEditionMode:self->isRefreshPending]; }]; @@ -117,6 +121,8 @@ - (void)viewDidLoad // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + MXStrongifyAndReturnIfNil(self); + [self userInterfaceThemeDidChange]; }]; @@ -207,9 +213,13 @@ - (void)viewWillAppear:(BOOL)animated [self.groupsTableView deselectRowAtIndexPath:indexPath animated:NO]; } + MXWeakify(self); + // Observe kAppDelegateDidTapStatusBarNotificationObserver. kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + MXStrongifyAndReturnIfNil(self); + [self scrollToTop:YES]; }]; diff --git a/Riot/Modules/Communities/TabDetail/GroupDetailsCoordinator.swift b/Riot/Modules/Communities/TabDetail/GroupDetailsCoordinator.swift index 27c3d7df02..976c188efd 100644 --- a/Riot/Modules/Communities/TabDetail/GroupDetailsCoordinator.swift +++ b/Riot/Modules/Communities/TabDetail/GroupDetailsCoordinator.swift @@ -43,6 +43,10 @@ final class GroupDetailsCoordinator: GroupDetailsCoordinatorProtocol { self.groupDetailsViewController = groupDetailsViewController } + deinit { + groupDetailsViewController.destroy() + } + // MARK: - Public func start() { diff --git a/Riot/Modules/Contacts/ContactsTableViewController.h b/Riot/Modules/Contacts/ContactsTableViewController.h index 8b535495b5..f1b3effaf4 100644 --- a/Riot/Modules/Contacts/ContactsTableViewController.h +++ b/Riot/Modules/Contacts/ContactsTableViewController.h @@ -122,7 +122,7 @@ /** The delegate for the view controller. */ -@property (nonatomic) id contactsTableViewControllerDelegate; +@property (nonatomic, weak) id contactsTableViewControllerDelegate; @end diff --git a/Riot/Modules/Contacts/ContactsTableViewController.m b/Riot/Modules/Contacts/ContactsTableViewController.m index adbc1f9e98..f208f6f671 100644 --- a/Riot/Modules/Contacts/ContactsTableViewController.m +++ b/Riot/Modules/Contacts/ContactsTableViewController.m @@ -33,12 +33,12 @@ @interface ContactsTableViewController () /** Observe UIApplicationWillChangeStatusBarOrientationNotification to hide/show bubbles bg. */ - id UIApplicationWillChangeStatusBarOrientationNotificationObserver; + __weak id UIApplicationWillChangeStatusBarOrientationNotificationObserver; /** The observer of the presence for matrix user. */ - id mxPresenceObserver; + __weak id mxPresenceObserver; /** List of the basic actions on this contact. @@ -79,7 +79,7 @@ Current alert (if any). /** Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. */ - id kThemeServiceDidChangeThemeNotificationObserver; + __weak id kThemeServiceDidChangeThemeNotificationObserver; /** The current visibility of the status bar in this view controller. @@ -182,9 +182,13 @@ - (void)viewDidLoad self.bottomImageView.hidden = (orientation.integerValue == UIInterfaceOrientationLandscapeLeft || orientation.integerValue == UIInterfaceOrientationLandscapeRight); }]; + MXWeakify(self); + // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + MXStrongifyAndReturnIfNil(self); + [self userInterfaceThemeDidChange]; }]; @@ -379,9 +383,13 @@ - (void)registerOnContactChangeNotifications // Be warned when the thumbnail is updated [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThumbnailUpdate:) name:kMXKContactThumbnailUpdateNotification object:nil]; + MXWeakify(self); + // Observe contact presence change mxPresenceObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKContactManagerMatrixUserPresenceChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + MXStrongifyAndReturnIfNil(self); + NSString* matrixId = self.firstMatrixId; if (matrixId && [matrixId isEqualToString:notif.object]) diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift index e68aedb514..d89e606356 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift @@ -105,8 +105,8 @@ final class EnterNewRoomDetailsViewController: UIViewController { var section3: Section? if RiotSettings.shared.roomCreationScreenAllowEncryptionConfiguration { - let row_3_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.isEncrypted, onValueChanged: { (theSwitch) in - self.viewModel.roomCreationParameters.isEncrypted = theSwitch.isOn + let row_3_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.isEncrypted, onValueChanged: { [weak self] (theSwitch) in + self?.viewModel.roomCreationParameters.isEncrypted = theSwitch.isOn }), text: VectorL10n.createRoomEnableEncryption, accessoryType: .none) { // no-op } @@ -117,11 +117,20 @@ final class EnterNewRoomDetailsViewController: UIViewController { var section4: Section? if RiotSettings.shared.roomCreationScreenAllowRoomTypeConfiguration { - let row_4_0 = Row(type: .default, text: VectorL10n.createRoomTypePrivate, accessoryType: viewModel.roomCreationParameters.isPublic ? .none : .checkmark) { + let row_4_0 = Row(type: .default, text: VectorL10n.createRoomTypePrivate, accessoryType: viewModel.roomCreationParameters.isPublic ? .none : .checkmark) { [weak self] in + guard let self = self else { + return + } + self.viewModel.roomCreationParameters.isPublic = false self.updateSections() } - let row_4_1 = Row(type: .default, text: VectorL10n.createRoomTypePublic, accessoryType: viewModel.roomCreationParameters.isPublic ? .checkmark : .none) { + let row_4_1 = Row(type: .default, text: VectorL10n.createRoomTypePublic, accessoryType: viewModel.roomCreationParameters.isPublic ? .checkmark : .none) { [weak self] in + + guard let self = self else { + return + } + self.viewModel.roomCreationParameters.isPublic = true self.updateSections() // scroll bottom to show user new fields @@ -149,8 +158,8 @@ final class EnterNewRoomDetailsViewController: UIViewController { } if viewModel.roomCreationParameters.isPublic { - let row_5_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.showInDirectory, onValueChanged: { (theSwitch) in - self.viewModel.roomCreationParameters.showInDirectory = theSwitch.isOn + let row_5_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.showInDirectory, onValueChanged: { [weak self] (theSwitch) in + self?.viewModel.roomCreationParameters.showInDirectory = theSwitch.isOn }), text: VectorL10n.createRoomShowInDirectory, accessoryType: .none) { // no-op } @@ -389,7 +398,10 @@ extension EnterNewRoomDetailsViewController: UITableViewDataSource { cell.mxkLabel.text = row.text cell.mxkSwitch.isOn = isOn cell.mxkSwitch.removeTarget(nil, action: nil, for: .valueChanged) - cell.mxkSwitch.vc_addAction(for: .valueChanged) { + cell.mxkSwitch.vc_addAction(for: .valueChanged) { [weak cell] in + guard let cell = cell else { + return + } onValueChanged?(cell.mxkSwitch) } cell.mxkLabelLeadingConstraint.constant = cell.vc_separatorInset.left diff --git a/Riot/Modules/Integrations/Widgets/Jitsi/JitsiViewController.h b/Riot/Modules/Integrations/Widgets/Jitsi/JitsiViewController.h index 653608c477..2fb2b368ff 100644 --- a/Riot/Modules/Integrations/Widgets/Jitsi/JitsiViewController.h +++ b/Riot/Modules/Integrations/Widgets/Jitsi/JitsiViewController.h @@ -81,7 +81,7 @@ /** The delegate for the view controller. */ -@property (nonatomic) id delegate; +@property (nonatomic, weak) id delegate; @end diff --git a/Riot/Modules/Room/Members/Detail/Views/RoomMemberTitleView.h b/Riot/Modules/Room/Members/Detail/Views/RoomMemberTitleView.h index 009b77e229..6111b69391 100644 --- a/Riot/Modules/Room/Members/Detail/Views/RoomMemberTitleView.h +++ b/Riot/Modules/Room/Members/Detail/Views/RoomMemberTitleView.h @@ -54,6 +54,6 @@ /** The delegate. */ -@property (nonatomic) id delegate; +@property (nonatomic, weak) id delegate; @end diff --git a/Riot/Modules/Room/Members/RoomParticipantsViewController.h b/Riot/Modules/Room/Members/RoomParticipantsViewController.h index c0b8dc3f8e..757bb2fca3 100644 --- a/Riot/Modules/Room/Members/RoomParticipantsViewController.h +++ b/Riot/Modules/Room/Members/RoomParticipantsViewController.h @@ -90,7 +90,7 @@ /** The delegate for the view controller. */ -@property (nonatomic) id delegate; +@property (nonatomic, weak) id delegate; /** Returns the `UINib` object initialized for a `RoomParticipantsViewController`. diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift index f65c06071c..ccfe76a7e8 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift @@ -152,11 +152,10 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType { case .search: MXKRoomDataSourceManager.sharedManager(forMatrixSession: session)?.roomDataSource(forRoom: self.room.roomId, create: false, onComplete: { (roomDataSource) in guard let dataSource = roomDataSource else { return } - let storyboard = UIStoryboard(name: "Main", bundle: nil) - if let search = storyboard.instantiateViewController(withIdentifier: "RoomSearch") as? RoomSearchViewController { - search.roomDataSource = dataSource - self.navigationRouter.push(search, animated: animated, popCompletion: nil) - } + let roomSearchViewController: RoomSearchViewController = RoomSearchViewController.instantiate() + roomSearchViewController.loadViewIfNeeded() + roomSearchViewController.roomDataSource = dataSource + self.navigationRouter.push(roomSearchViewController, animated: animated, popCompletion: nil) }) case .notifications: let coordinator = createRoomNotificationSettingsCoordinator() diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift index daa9b29479..3590b5526a 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift @@ -50,8 +50,8 @@ final class RoomInfoListViewController: UIViewController { private lazy var basicInfoView: RoomInfoBasicView = { let view = RoomInfoBasicView.loadFromNib() - view.onTopicSizeChange = { _ in - self.view.setNeedsLayout() + view.onTopicSizeChange = { [weak self] _ in + self?.view.setNeedsLayout() } return view }() diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 063f37ff76..6d989d669b 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2257,6 +2257,12 @@ - (void)setupUserSuggestionView } UIViewController *suggestionsViewController = self.userSuggestionCoordinator.toPresentable; + + if (!suggestionsViewController) + { + return; + } + [suggestionsViewController.view setTranslatesAutoresizingMaskIntoConstraints:NO]; [self addChildViewController:suggestionsViewController]; @@ -2353,11 +2359,14 @@ - (void)showPreviewHeader:(BOOL)isVisible if (isVisible) { - previewHeader = [PreviewRoomTitleView roomTitleView]; + PreviewRoomTitleView *previewHeader = [PreviewRoomTitleView roomTitleView]; previewHeader.delegate = self; previewHeader.tapGestureDelegate = self; previewHeader.translatesAutoresizingMaskIntoConstraints = NO; [self.previewHeaderContainer addSubview:previewHeader]; + + self->previewHeader = previewHeader; + // Force preview header in full width NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:previewHeader attribute:NSLayoutAttributeLeading @@ -5398,7 +5407,7 @@ - (void)showEncryptionInformation:(MXEvent *)event // Remove potential existing subviews [self dismissTemporarySubViews]; - encryptionInfoView = [[EncryptionInfoView alloc] initWithEvent:event andMatrixSession:self.roomDataSource.mxSession]; + EncryptionInfoView *encryptionInfoView = [[EncryptionInfoView alloc] initWithEvent:event andMatrixSession:self.roomDataSource.mxSession]; // Add shadow on added view encryptionInfoView.layer.cornerRadius = 5; @@ -5408,6 +5417,8 @@ - (void)showEncryptionInformation:(MXEvent *)event // Add the view and define edge constraints [self.view addSubview:encryptionInfoView]; + self->encryptionInfoView = encryptionInfoView; + [self.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual diff --git a/Riot/Modules/Room/Search/RoomSearchViewController.h b/Riot/Modules/Room/Search/RoomSearchViewController.h index b1168107bb..14c1460b5b 100644 --- a/Riot/Modules/Room/Search/RoomSearchViewController.h +++ b/Riot/Modules/Room/Search/RoomSearchViewController.h @@ -25,4 +25,6 @@ */ @property (nonatomic) MXKRoomDataSource *roomDataSource; ++ (instancetype)instantiate; + @end diff --git a/Riot/Modules/Room/Search/RoomSearchViewController.m b/Riot/Modules/Room/Search/RoomSearchViewController.m index b6a17df468..08374baea9 100644 --- a/Riot/Modules/Room/Search/RoomSearchViewController.m +++ b/Riot/Modules/Room/Search/RoomSearchViewController.m @@ -37,6 +37,13 @@ @interface RoomSearchViewController () @implementation RoomSearchViewController ++ (instancetype)instantiate +{ + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]]; + RoomSearchViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"RoomSearch"]; + return viewController; +} + - (void)finalizeInit { [super finalizeInit]; diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 0a5acf57c3..7ecfcb5331 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -178,16 +178,16 @@ @interface SettingsViewController () { // Current alert (if any). - UIAlertController *currentAlert; + __weak UIAlertController *currentAlert; // listener - id removedAccountObserver; - id accountUserInfoObserver; - id pushInfoUpdateObserver; + __weak id removedAccountObserver; + __weak id accountUserInfoObserver; + __weak id pushInfoUpdateObserver; - id notificationCenterWillUpdateObserver; - id notificationCenterDidUpdateObserver; - id notificationCenterDidFailObserver; + __weak id notificationCenterWillUpdateObserver; + __weak id notificationCenterDidUpdateObserver; + __weak id notificationCenterDidFailObserver; // profile updates // avatar @@ -216,10 +216,10 @@ @interface SettingsViewController () UIViewController { + private func createVersionCheckCoordinator(withRootViewController rootViewController: UIViewController, bannerPresentrer: BannerPresentationProtocol) -> VersionCheckCoordinator { + let versionCheckCoordinator = VersionCheckCoordinator(rootViewController: rootViewController, + bannerPresenter: bannerPresentrer, + themeService: ThemeService.shared()) + return versionCheckCoordinator + } + + private func createHomeViewController() -> HomeViewControllerWithBannerWrapperViewController { let homeViewController: HomeViewController = HomeViewController.instantiate() homeViewController.tabBarItem.tag = Int(TABBAR_HOME_INDEX) homeViewController.tabBarItem.image = homeViewController.tabBarItem.image homeViewController.accessibilityLabel = VectorL10n.titleHome - let wrapperViewController = HomeViewControllerWithBannerWrapperViewController(viewController: homeViewController) - homeViewControllerWrapperViewController = wrapperViewController + let wrapperViewController = HomeViewControllerWithBannerWrapperViewController(viewController: homeViewController) return wrapperViewController } @@ -291,12 +288,27 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { self.masterTabBarController.filterRooms(withParentId: spaceId, inMatrixSession: self.currentMatrixSession) } + // TODO: Avoid to reinstantiate controllers everytime private func updateTabControllers(for tabBarController: MasterTabBarController, showCommunities: Bool) { var viewControllers: [UIViewController] = [] - + let homeViewController = self.createHomeViewController() + viewControllers.append(homeViewController) + if let existingVersionCheckCoordinator = self.versionCheckCoordinator { + self.remove(childCoordinator: existingVersionCheckCoordinator) + } + + if let masterTabBarController = self.masterTabBarController { + + let versionCheckCoordinator = self.createVersionCheckCoordinator(withRootViewController: masterTabBarController, bannerPresentrer: homeViewController) + versionCheckCoordinator.start() + self.add(childCoordinator: versionCheckCoordinator) + + self.versionCheckCoordinator = versionCheckCoordinator + } + if RiotSettings.shared.homeScreenShowFavouritesTab { let favouritesViewController = self.createFavouritesViewController() viewControllers.append(favouritesViewController) diff --git a/changelog.d/5058.bugfix b/changelog.d/5058.bugfix new file mode 100644 index 0000000000..1e8112a369 --- /dev/null +++ b/changelog.d/5058.bugfix @@ -0,0 +1 @@ +Fix retain cycles that prevents deallocation in several classes. \ No newline at end of file