对符合协议的所有对象调用方法

Mih*_*atu 2 ios swift

更新

正如maddy在下面的评论中提到的,很明显你需要一个对象的引用才能调用它的方法。这可能实际上是我的问题:您如何跟踪实现协议的所有对象?

回顾 Objective-C,我想过使用类似于+load或 的+initialize方法并将对象添加为特定NSNotification. 但这行不通,因为这些方法是类方法而不是实例方法。

所以,试着说得更具体一点:有没有一种方法可以在所有对象创建后调用它们?一种允许我将该对象添加到我管理的集合或作为特定对象的观察者的方法NSNotification

PS:我没有尝试为问题添加太多细节,因为我不想用我糟糕的、无意义的想法“玷污”你。

原来的

所以......想象一下这段代码:

protocol MyProtocol: class {

    func myMethod()

}

public class MyClass: MyProtocol {

    func myMethod() {
        print("myMethod called on MyClass")
    }    

}

extension UIView: MyProtocol {

    func myMethod() {
        print("myMethod called on UIView")
    }

}

let myObject = MyClass()
let myView = UIView()
Run Code Online (Sandbox Code Playgroud)

现在......我试图找出一种方法来myMethod从不知道它们的第三个对象调用这两个对象 - 这是第三个对象的简化示例:

class MyManager {

    func callMyMethodOnAllObjecs() {
        // Do something here so that ALL objects present in memory that conform to MyProtocol get their myMethod called
    }        

}
Run Code Online (Sandbox Code Playgroud)

任何人?

Cri*_*tik 6

请注意,您需要的特性需要属于动态类型语言,这意味着使用 Objective-C 运行时。这不会带来很多技术挑战,但会限制您可以使用的 Swift 实体的范围——基本上只有NSObject派生类。

简而言之,这是您需要的:

  1. 支持MyManager注册新创建的对象
  2. 一段代码,需要由所有MyProtocol注册自己的实例执行MyManager
  3. 最重要的是,不会产生内存泄漏的代码,因为在其中注册对象会MyManager带来管理器无限期保留该对象的风险。

您可以在下面找到解决问题的代码:

// Since we need ObjectiveC specific runtime features, we need to
// restrict the protocol to the NSObject protocol
protocol MyProtocol: NSObjectProtocol {
    func myMethod()
}

public class MyClass: NSObject, MyProtocol {
    func myMethod() {
        print("myMethod called on MyClass")
    }
    
}

extension UIView: MyProtocol {
    
    func myMethod() {
        print("myMethod called on UIView")
    }   
}

extension NSObject {
    // this is an alternative to the original init method, that besides the
    // original edit it registers the object within MyManager
    @objc func swizzledInit() -> NSObject {
        // this is not recursive, as init() in exchanged with swizzledInit()
        let `self` = swizzledInit()
        if let `self` = self as? MyProtocol {
            // the object is MyProtocol
            MyManager.shared.register(self)
        }
        return self
    }
}

class MyManager {
    
    static let shared = MyManager()
    private var objecters = [() -> MyProtocol?]()
    
    private init() {
        // let's swizzle init() with our custom init
        if let m1 = class_getInstanceMethod(NSObject.self, #selector(NSObject.init)),
           let m2 = class_getInstanceMethod(NSObject.self, #selector(NSObject.swizzledInit)) {
            method_exchangeImplementations(m1, m2)
        }
    }
    
    public func register(_ object: MyProtocol) {
        // registering a block in order to be able to keep a weak reference to
        // the registered object
        objecters.append({ [weak object] in return object })
    }
    
    
    func callMyMethodOnAllObjecs() {
        var newList =  [() -> MyProtocol?]()
        // go through the list of registered blocks, execute the method,
        // and retain only the ones for wich the object is still alive
        for object in objecters {
            if let o = object() {
                newList.append(object)
                o.myMethod()
            }
        }
        objecters = newList
    }
    
}

// This is to make sure the manager is instantiated first,
// and thus it swizzles the NSObject initializer
_ = MyManager.shared

let myObject = MyClass()
let myView = UIView()

// an instance of MyClass and one of UIView will print stuff
MyManager.shared.callMyMethodOnAllObjecs()
Run Code Online (Sandbox Code Playgroud)

总结一下,上面的代码:

  1. swizzles initofNSObject以便除了原始init对象之外,它还将对象注册到您的经理。
  2. 保留一个返回对象而不是对象本身的闭包列表,以便能够弱引用这些对象,从而避免使对象保持活跃
  3. 清理callMyMethodOnAllObjecs被调用时对象被释放的闭包。