diff --git a/Hidden Bar.xcodeproj/project.pbxproj b/Hidden Bar.xcodeproj/project.pbxproj index 6f9aa2f..be203f6 100644 --- a/Hidden Bar.xcodeproj/project.pbxproj +++ b/Hidden Bar.xcodeproj/project.pbxproj @@ -620,7 +620,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 1.8; PRODUCT_BUNDLE_IDENTIFIER = com.dwarvesv.minimalbar; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -645,7 +645,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 1.8; PRODUCT_BUNDLE_IDENTIFIER = com.dwarvesv.minimalbar; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -669,7 +669,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = com.dwarvesv.LauncherApplication; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -693,7 +693,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = com.dwarvesv.LauncherApplication; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/hidden/Extensions/StackView+Extension.swift b/hidden/Extensions/StackView+Extension.swift index 5cebcc4..69b07d4 100644 --- a/hidden/Extensions/StackView+Extension.swift +++ b/hidden/Extensions/StackView+Extension.swift @@ -11,6 +11,8 @@ import Cocoa extension NSStackView { func removeAllSubViews() { for view in self.views { + // Deactivate all constraints associated with this view to prevent memory leaks + NSLayoutConstraint.deactivate(view.constraints) view.removeFromSuperview() } } diff --git a/hidden/Features/Preferences/PreferencesViewController.swift b/hidden/Features/Preferences/PreferencesViewController.swift index 1703cd2..0e3b6d0 100644 --- a/hidden/Features/Preferences/PreferencesViewController.swift +++ b/hidden/Features/Preferences/PreferencesViewController.swift @@ -55,7 +55,12 @@ class PreferencesViewController: NSViewController { createTutorialView() NotificationCenter.default.addObserver(self, selector: #selector(updateData), name: .prefsChanged, object: nil) } - + + deinit { + // Clean up NotificationCenter observer to prevent memory leaks + NotificationCenter.default.removeObserver(self, name: .prefsChanged, object: nil) + } + static func initWithStoryboard() -> PreferencesViewController { let vc = NSStoryboard(name:"Main", bundle: nil).instantiateController(withIdentifier: "prefVC") as! PreferencesViewController return vc diff --git a/hidden/Features/StatusBar/StatusBarController.swift b/hidden/Features/StatusBar/StatusBarController.swift index 3d67840..5db58d4 100644 --- a/hidden/Features/StatusBar/StatusBarController.swift +++ b/hidden/Features/StatusBar/StatusBarController.swift @@ -63,17 +63,32 @@ class StatusBarController { //MARK: - Methods init() { - + setupUI() setupAlwayHideStatusBar() - DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { - self.collapseMenuBar() - }) - + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in + self?.collapseMenuBar() + } + if Preferences.areSeparatorsHidden {hideSeparators()} autoCollapseIfNeeded() } - + + deinit { + // Clean up NotificationCenter observers to prevent memory leaks + NotificationCenter.default.removeObserver(self, name: .prefsChanged, object: nil) + NotificationCenter.default.removeObserver(self, name: .alwayHideToggle, object: nil) + + // Invalidate timer to prevent retain cycles + timer?.invalidate() + timer = nil + + // Properly remove status items + if let statusItem = self.btnAlwaysHidden { + NSStatusBar.system.removeStatusItem(statusItem) + } + } + private func setupUI() { if let button = btnSeparate.button { button.image = self.imgIconLine @@ -139,8 +154,8 @@ class StatusBarController { if isToggle {return} isToggle = true self.isCollapsed ? self.expandMenubar() : self.collapseMenuBar() - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - self.isToggle = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in + self?.isToggle = false } } @@ -243,14 +258,20 @@ extension StatusBarController { } @objc private func toggleStatusBarIfNeeded() { if Preferences.alwaysHiddenSectionEnabled { - self.btnAlwaysHidden = NSStatusBar.system.statusItem(withLength: 20) - if let button = btnAlwaysHidden?.button { - button.image = self.imgIconLine - button.appearsDisabled = true + // Only create new status item if one doesn't exist + if self.btnAlwaysHidden == nil { + self.btnAlwaysHidden = NSStatusBar.system.statusItem(withLength: 20) + if let button = btnAlwaysHidden?.button { + button.image = self.imgIconLine + button.appearsDisabled = true + } + self.btnAlwaysHidden?.autosaveName = "hiddenbar_terminate" + } + } else { + // Properly remove status item before setting to nil + if let statusItem = self.btnAlwaysHidden { + NSStatusBar.system.removeStatusItem(statusItem) } - self.btnAlwaysHidden?.autosaveName = "hiddenbar_terminate"; - - }else { self.btnAlwaysHidden = nil } }