As maddy mentioned bellow in the comments it's obvious you need a reference to an object to be able to call a method on it. That is probably in fact my question: how do you keep track of all objects that implement a protocol?
Looking back at Objective-C I thought about using something similar to +load or +initialize methods and add the object as an observer for a specific NSNotification. But that wouldn't work since those methods are class methods and not instance methods.
So, trying to be even a little bit more specific: is there a method that get's called on all objects after they are created? A method that would allow me to add that object to a collection that I manage or as an observer for a specific NSNotification?
P.S: I haven't tried adding to much details to the problem 'cause I didn't want to "polute" you with my bad, non-sense ideas.
So... Imagine this piece of code:
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()
Now... I'm trying to figure out a way to call myMethod on both these objects from a 3rd one which is not aware of them - here's a simplified example of the 3rd one:
class MyManager {
func callMyMethodOnAllObjecs() {
// Do something here so that ALL objects present in memory that conform to MyProtocol get their myMethod called
}
}
Anyone?
A heads up that what you need requires features belonging to dynamically typed languages, this means using the Objective-C runtime. This doesn't pose many technical challenges, but restricts the area of Swift entities you can use - basically only NSObject derived classes.
Briefly, here's what you need:
MyManager for registering newly created objectsMyProtocol instances that register themselves to MyManagerMyManager poses the risk of having the manager indefinitely retain that object.Below you can find a code that solves the problem:
// 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()
In summary, the above code:
init of NSObject so that besides the original init it also registers the object to your manager.callMyMethodOnAllObjecs is invoked.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With