对不起,标题可能没有那么多信息,所以这是我的问题
我想创建ThemeManager并应用于所有屏幕,可以在应用程序中更改主题,这就是为什么我添加了closureList来触发并更新所有相关屏幕的原因
class ThemeManager {
static let shared = ThemeManager()
private(set) var theme: Theme
private var bindedList: [()->Void] = []
private init () {
self.theme = AppGreenTheme()
}
func apply(theme: Theme) {
self.theme = theme
}
func bind(closure: @escaping ()->Void) {
bindedList.append(closure)
}
func bindAndFire(closure: @escaping ()->Void) {
bind(closure: closure)
closure()
}
}
这是我想从任何UIViewController
或任何UIView
ThemeManager.shared.bindAndFire {
// here I will get theme changes and update my screen
}
所以我想知道,在这种情况下,我将为UIViewController
或UIView
创建参考周期,这是在父项UIViewController
之后从列表中删除闭包的最佳方法或UIView
,将从内存中删除。
答案 0 :(得分:2)
只要您通过UIViewController
作为弱引用,就很安全
ThemeManager.shared.bindAndFire { [weak self] in
guard let strongSelf = self else { return }
// here I will get theme changes and update my screen
}
但是NotificationCenter
是更好的依赖方法,这是基本的ThemeManager
示例
class ThemeManager {
static let shared = ThemeManager()
static let NotificationName = NSNotification.Name("Notifacation.ThemeManager")
var theme: Theme!
func switchTheme(_ theme: Theme) {
self.theme = theme
NotificationCenter.default.post(name: ThemeManager.NotificationName, object: self.theme)
}
}
用法:
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(themeDidUpdate(_:)), name: ThemeManager.NotificationName, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
@objc func themeDidUpdate(_ notification: Notification) {
guard let theme = notification.object as? Theme else { return }
// here I will get theme changes and update my screen
}
}
Update-2 带有闭包的NotificationCenter示例
NotificationCenter.default.addObserver(forName: ThemeManager.NotificationName, object: nil, queue: OperationQueue.main) { [weak self] (notification) in
guard let strongSelf = self, let theme = notification.object as? Theme else { return }
// here I will get theme changes and update my screen
}
答案 1 :(得分:1)
您可以使用struct
包装闭包,该闭包还具有一个属性,可以检查是否相等,并在添加闭包时返回该属性的值。如果要删除闭包,则视图控制器可以传递此id。您可以在其余代码中隐藏包装器。如果您不想跟踪某个计数器,也可以使用UUID
。您还可以使用字典来存储以id为键的闭包。
class ThemeManager {
private var counter = 0
private var closures: [ClosureWrapper] = []
private struct ClosureWrapper {
let id: Int
let closure: () -> Void
}
func bind(closure: @escaping () -> Void) -> Int {
counter += 1
let wrapper = ClosureWrapper(id: counter, closure: closure)
closures.append(wrapper)
return wrapper.id
}
func removeClosure(with id: Int) {
guard let index = closures.firstIndex(where: { $0.id == id }) else {
return
}
closures.remove(at: index)
}
}
在此版本中,您无需跟踪闭包的ID。它使用带有弱键的NSMapTable
来存储闭包。您可以将视图控制器作为键传递,并在取消分配时,将自动从映射表中删除传递的闭包。
class ThemeManager {
private let closureTable = NSMapTable<NSObject, ClosureWrapper>(keyOptions: .weakMemory, valueOptions: .strongMemory)
private class ClosureWrapper {
let closure: () -> Void
init(closure: @escaping () -> Void) {
self.closure = closure
}
}
func bind(from source: NSObject, closure: @escaping () -> Void) {
let wrapper = ClosureWrapper(closure: closure)
closureTable.setObject(wrapper, forKey: source)
}
func callClosures() {
for key in closureTable.keyEnumerator().allObjects {
let wrapper = closureTable.object(forKey: key as? NSObject)
wrapper?.closure()
}
}
}