正如maddy在评论中提到的那样,显然你需要一个对象的引用才能在其上调用一个方法。这可能实际上是我的问题:如何跟踪实现协议的所有对象?
回顾Objective-C我考虑使用类似于+load
或+initialize
方法的东西,并将对象添加为特定NSNotification
的观察者。但这不起作用,因为这些方法是类方法而不是实例方法。
因此,尝试更具体一点:是否有一种方法可以在创建后调用所有对象?一种方法,允许我将该对象添加到我管理的集合或作为特定NSNotification
的观察者?
所以...想象一下这段代码:
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()
现在......我正在试图找出一种方法来从第三个对象上调用myMethod
,而第三个对象不知道它们 - 这是第三个对象的简化示例:
class MyManager {
func callMyMethodOnAllObjecs() {
// Do something here so that ALL objects present in memory that conform to MyProtocol get their myMethod called
}
}
任何?
答案 0 :(得分:4)
请注意,您需要的功能需要属于动态类型语言的功能,这意味着使用Objective-C运行时。这不会带来许多技术挑战,但会限制您可以使用的Swift实体的区域 - 基本上只有NSObject
派生类。
简而言之,这就是你需要的:
MyManager
注册新创建的对象MyProtocol
MyManager
个实例执行的一段代码
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
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
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()
总之,上面的代码:
init
的{{1}},这样除了原NSObject
之外,它还会将对象注册到您的经理。init
时取消分配对象的闭包。答案 1 :(得分:0)
您可以让MyProtocol
个对象收听来自NotificationCenter
的通知,并使myMethod()
成为收到通知时触发的方法。
然后,当您将通知发布到NotificationCenter
时,当前存在并遵守该协议的所有对象都将触发该方法。
答案 2 :(得分:0)
我认为你的意思是,如果你不知道该方法是否存在于该对象上。如果是这样,只需在调用方法之前对该方法运行测试:
myObject.respondsToSelector(Selector("myMethod")) // true if it can respond to it
-----更新了最新问题的答案:
if(myObject && myObject.respondsToSelector(Selector("myMethod")))
[myMutableDic setObject:myObject forKey:@"myObject"];
if(myView && myView.respondsToSelector(Selector("myMethod")))
[myMutableDic setObject:myView forKey:@"myView"];
// and call all myMethods on this set ------------------------------
for (id key in [myMutableDic allKeys]) {
id obj = [myMutableDic objectForKey:key];
[obj myMethod]; // since already checked before putting it into dictionary
}
// or use the block enumeration - does the same thing:
[myMutableDic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
[obj myMethod];
}];
// ps didn't test it may need to fix grammar
答案 3 :(得分:0)
这样你可以添加或删除准备使用的观察者方法。您还可以创建自定义init以在创建实例时调用此方法。
protocol MyProtocol: class {
func myMethod()
func addObserverForMyMethod()
func removeMyMethodObserver()
}
extension MyProtocol {
func addObserverForMyMethod() {
NotificationCenter.default.addObserver(self, selector: #selector(self.myMethod), name: Notification.Name("myMethodCall"), object: nil)
}
func removeMyMethodObserver() {
NSNotificationCenter.default.removeObserver(self, name: "myMethodCall", object: nil)
}
}
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()
myObject.addObserverForMyMethod()
myView.addObserverForMyMethod()
答案 4 :(得分:0)
没有。在所有对象上都没有调用可覆盖的initialize方法。实际上,除非从NSObject
继承,否则在swift中根本没有基类继承的东西。这种类型的动力有目的地不存在于swift中。为了做这样的事情,我建议你使用目标C运行时。考虑使用Injection for Xcode的源作为跳出点:https://github.com/johnno1962/injectionforxcode/blob/master/InjectionPluginLite/Classes/BundleSweeper.h
但是这真的非常强烈地反对谷物。如果您愿意考虑类似的东西,这可能适合您,同时更安全类型(并且它具有允许异构swift类型的好处):
protocol X {
static var insts:[X]{ get }
static func create() -> X
}
final class A:X{
static func create() -> X {
let it = A()
insts.append(it)
return it
}
static var insts:[X] = []
}
struct B:X{
static func create() -> X {
let it = B()
insts.append(it)
return it
}
static var insts:[X] = []
}
var protocols:[X.Type] = [A.self, B.self]
for p in protocols {
p.insts.forEach({print($0)})
}