我遇到了一个小问题:
如何听取另一个(“不可编辑的 !!”)课程中声明的
struct-instance variable
的变化?
我添加了一些小代码片段,以澄清我的想法。
说明:
FixedClass
是一个不可编辑的课程:我不想更改/我无法更改“FixedClass
”的任何代码
EditableClass
可以编辑 - 我相信你会看到代码(^ _ ^)/
代码:
let fixedClass: FixedClass = FixedClass()
class FixedClass {
struct MyObject {
var abc = 0
}
var instance = MyObject()
public init() { Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.updateVar), userInfo: nil, repeats: true) }
@objc func updateVar() {
instance.abc+=1
}
func getVar() -> Int {
return instance.abc
}
}
let editableClass: EditableClass = EditableClass()
class EditableClass {
public init() { }
func listenForChange() {
// listen for change event of FixedClass.getVar()
print("Variable: \(fixedClass.getVar())")
}
}
致电:editableClass.listenForChange()
总结一下,我想听取FixedClass.getVar()
结果的更改 - 最好避免使用loops
或timers
。然而,最重要的是让它至少起作用。
非常感谢任何帮助,谢谢!
答案 0 :(得分:1)
这完全取决于如何定义'真正'的FixedClass。 I.E.它是NSObject的子类,它是一个ObjectiveC类,如何定义要观察的属性。
就你的实际例子而言,你可以通过这样的子类化来实现:
var newFixedClass: NewFixedClass = NewFixedClass()
var editableClass: EditableClass = EditableClass()
protocol NewFixedClassProtocol: class {
func changed(newFixedClass: NewFixedClass)
}
class NewFixedClass: FixedClass {
public weak var delegate: NewFixedClassProtocol?
override var instance: FixedClass.MyObject {
didSet {
self.delegate?.changed(newFixedClass: self)
}
}
}
class EditableClass: NewFixedClassProtocol {
public init() {
newFixedClass.delegate = self
}
func changed(newFixedClass: NewFixedClass) {
print ("Value = \(newFixedClass.instance.abc)")
}
}
因此,您基本上创建了一个协议,该类执行观察支持,创建一个FixedClass的子类,它具有协议类型的委托,并覆盖您想要使用didSet观察者观察的FixedClass的属性,然后调用委托方法。在某些时候,你必须将观察类指定为子类的委托(我在init方法中将它作为测试)。
这样做我可以观察结构何时发生变化,但我还没有触及过FixedClass。
但请注意,此方法在很大程度上依赖于了解原始的FixedClass,因此可能不适合您的“真实世界”案例。
(另外,我无法让它与全局定义的类实例一起使用,并且必须将它们设置在我的初始视图控制器中,但这可能与我的测试方式有关并且不会改变所涉及的方法)
答案 1 :(得分:0)
有几件事:
如果原始类是Objective-C或以其他方式参与KVO(例如dynamic
子类的Swift NSObject
属性等),那么您可以观察到更改。但是,这是一个相当狭窄的用例。但这是使一个人的属性可被其他对象观察的一般模式。有关详细信息,请参阅Key-Value Observing Programming Guide。
如果您无法编辑该类,在某些狭隘的情况下,理论上您可以将其子类化并添加您想要的任何观察系统。这显然只有在您手动实例化FixedClass
时才有效,并且它取决于FixedClass
的实现方式,但在某些狭窄的情况下,您可以通过子类化实现所需的功能。
你问:
你能和我们分享一些代码片段吗?
当然,请考虑您的FixedClass
:
class FixedClass {
struct MyObject {
var abc = 0
}
var instance = MyObject()
public init() { Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.updateVar), userInfo: nil, repeats: true) }
@objc func updateVar() {
instance.abc+=1
}
func getVar() -> Int {
return instance.abc
}
}
然后你可以定义一个子类:
class FixedClassSubclass: FixedClass {
static let changeNotification = NSNotification.Name(rawValue: Bundle.main.bundleIdentifier! + ".FixedClassSubclassNotification")
override func updateVar() {
super.updateVar()
NotificationCenter.default.post(name: FixedClassSubclass.changeNotification, object: self)
}
}
然后你可以这样做:
let fixed = FixedClassSubclass()
和
NotificationCenter.default.addObserver(forName: FixedClassSubclass.changeNotification, object: nil, queue: nil) { _ in
print("changed")
}
您可以使用所需的任何通知流程。 NotificationCenter
。 KVN。委托协议模式。随你。详细信息将完全根据FixedClass
的详细信息而有所不同,您给我们提供了一个在许多情况下不太可扩展的人为例子。
我必须承认对试图挂钩不可编辑类的内部实现细节的想法有些普遍的疑虑。我们通常会寻求仅依赖于已发布的支持接口的松散耦合对象。这项努力违反了这两个目标。但我会假设你有充分的理由去做你想做的事。
答案 2 :(得分:-1)
执行此操作的一种方法是使用NotificationCenter广播更改,并让您的EditableClass
侦听该更改并对其做出反应。您的实现可能如下所示:
class FixedClass { //Class names should start with capital letters
struct MyObject { //Struct names should also start with capital letters
var abc = 0
}
var instance = myObject()
public init() { Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.updateVar), userInfo: nil, repeats: true) }
@objc func updateVar() {
instance.abc+=1
//Broadcast the change, and pass this FixedClass instance
NotificationCenter.default.post(name: Notification.Name("fixedClassChanged"), object: self)
}
func getVar() -> Int {
return instance.abc
}
}
然后你可以像EditableClass
一样对这个广播作出反应:
class EditableClass {
public init() { }
func listenForChange() {
//Observe same notification being broadcast by the other class
NotificationCenter.default.addObserver(self, selector: #selector(processChange(_:)), name: Notification.Name("fixedClassChanged"), object: nil)
}
@objc func processChange(_ sender: Notification) {
//When the notification comes in, check to see if the object passed in was a FixedClass, and if so process whatever needs to be processed
if let fixed = sender.object as? FixedClass {
print("Class changed to \(fixed.getVar())")
}
}
}