KVO vs NSNotification vs 协议/委托?

发布于 2024-12-11 04:49:37 字数 40 浏览 0 评论 0原文

我知道何时使用哪个,但确切的用法我仍然不清楚。有人可以举例解释吗?

I have some idea of which to use when but the exact usage is still not clear to me. Can someone explain with example?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

错爱 2024-12-18 04:49:37

如果您只想与一个对象对话,请使用委托。例如,tableView 有一个委托 - 只有一个对象应该负责处理它。

如果您想告诉每个人发生了什么事,请使用通知。例如,在内存不足的情况下,会发送一条通知,告诉您的应用程序出现了内存警告。因为应用程序中的许多对象可能希望降低其内存使用量,所以这是一个通知。

我认为 KVO 根本不是一个好主意,并且尽量不要使用它,但是,如果您想了解属性是否已更改,您可以监听更改。

希望有帮助。

PS 这总结了我认为 KVO 被破坏的原因

Use a delegate if you want to talk to only one object. For example, a tableView has a delegate - only one object should be responsible for dealing with it.

Use notifications if you want to tell everyone that something has happened. For example in low memory situations a notification is sent telling your app that there has been a memory warning. Because lots of objects in your app might want to lower their memory usage it's a notification.

I don't think KVO is a good idea at all and try not to use it but, if you want to find out if a property has changed you can listen for changes.

Hope that helps.

PS This sums up why I think KVO is broken

梅窗月明清似水 2024-12-18 04:49:37

当存在“主/从”关系(委托了解该类,而该类了解该委托)、其中一个类处于控制层次结构的较高层,并且很明显不会出现其他类无法使用的情况时,请使用委托。元素(主要是 UI)有兴趣了解该类要说什么。

当班级不想知道谁在听以及有多少人在听时,可以使用通知,任何人、任何人数都可以注册通知。

KVO 对于“在类不知道的情况下”进行监听很有用,当然事实并非如此,应用 KVO 的类不需要更改。

Use a delegate when there is a "master/slave" relationship (delegate knows about the class and class knows about the delegate), with one class higher up the control hierarchy, and when it is clear that there won't be situations where other elements (mostly UI) will be interested in knowing what the class has to say.

Use notifications when the class is not interested in knowing who listens and how many they are, anybody and any number can register for the notifications.

KVO is useful to listen "without the class knowing", although of course that's not the case, the class on which KVO is applied does not need to be changed.

找回味觉 2024-12-18 04:49:37

委派是当您希望其他对象修改发送者的行为时使用的一种设计模式。示例:终端窗口避免显示被窗口边缘剪切的任何行或字符,因为终端窗口的委托会更改窗口的大小以确保这一点。

通知是当您不需要响应时使用的一种模式。示例:您收到系统即将进入睡眠状态的通知。该通知的发送者并不关心您对此做了什么。

Delegation is a design pattern that you use when you want some other object to modify the sender's behavior. Example: terminal windows avoid showing any lines or characters that are clipped by the window's edges, because the terminal window's delegate alters the size of the window to ensure this.

Notification is a pattern to use when you don't need a response. Example: you get a notification that the system is about to go to sleep. The sender of that notification doesn't care what you do about it.

吹泡泡o 2024-12-18 04:49:37

即使这三者都能满足您在某种情况下的需求,委托仍然是一个更好的选择:

  1. 可重用性。
  2. 自我记录。通过检查类的头文件,人们可以立即识别出数据交换的内容/方式。

Even when all three would serve your need in a situation, delegate would still be a prefer option:

  1. Reuseability.
  2. Self documented. By examining the class's header file, one would immediately recognize what / how the data exchanged taking places.
甜扑 2024-12-18 04:49:37

iOS 委托模式、通知中心、KVO

委托

委托 模式是一种与结构(GoF 的装饰器或包装器模式)相关的设计模式,它添加了无需更改对象的代码即可实现对象的行为和职责。您可以将一些逻辑移至另一个辅助类中或将其用作骨架。它是继承的替代方案。从技术上讲,它使用关联[关于]。 Kotlin 语言在语言层支持delegate 模式。对于iOS,通常它用于松散耦合,用于类之间的通信Class1 <-> Class2 没有保留周期[关于] 其中SomeClass1 -> SomeClass2 和 SomeClass2 弱-> SomeClass1

protocol SomeProtocol: AnyObject {
    func foo()
}

class SomeClass1: SomeProtocol {
    let someClass2 = SomeClass2()

    init() {
        someClass2.delegate = self
    }
    
    func foo() {
        print("foo is called")
    }
}

class SomeClass2 {
    
    weak var delegate: SomeProtocol?
    
    func onButtonTap() {
        delegate?.foo()
    }
}

NotificationCenter

NotificationCenter 或 NSNotificationCenter(Objective-C) (不是远程(推送)或本地通知)是一种发布/订阅事件总线。您有 NotificationCenter 单例对象,它是任何人发送或接收事件的单点。您可以使用它通过所有应用程序发送事件,任何人都可以中断它。这种系统开发速度快,但支持难度大。它也是一种松耦合系统。

您可以使用NotificationCenter的下一个API:

post(name: object: userInfo:)
addObserver(_ observer: selector: name: object:)
removeObserver(_ observer: selector: object:)

例如系统的显示、隐藏键盘

NotificationCenter.default.addObserver(self, selector: #selector(MyViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(MyViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

@objc func keyboardWillShow(_ notification:Notification) {
}
    
@objc func keyboardWillHide(_ notification:Notification) {
}

KVO

KVO - Key-Value Observing。观察 Objective-C 支持的属性值的变化。当你需要了解对象的一些变化而不需要任何请求时,可以使用它

Objective-C -@property[关于] 使用 willChangeValueForKeydidChangeValueForKey KVO

*注释

  • 如果您覆盖 willChangeValueForKeydidChangeValueForKey,则不会触发 observeValueForKeyPath
  • 如果您使用 iVar[关于] 您负责调用的设置器willChangeValueForKeydidChangeValueForKey
#import "SomeClass.h"

@interface SomeClass()
@property (nonatomic, strong) NSString *someVariable;
@end

@implementation SomeClass
- (void) foo 
{    
    [self addObserver: self forKeyPath: @"someVariable" options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context: nil];
    
    self.someVariable = @"set someVariable";
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"someVariable"]) {
        NSLog(@"%@", change);
    }
}

@end

Swift - NSObject@objc 动态[关于]

class SomeClass1 : NSObject {
    @objc dynamic var v = 0
}

class SomeClass2 {
    var kvoToken: NSKeyValueObservation?
    
    func subscribe(someClass1: SomeClass1) {
        kvoToken = someClass1.observe(\.v, options: .new) { (object, change) in
            guard let value = change.newValue else { return }
            print("New value: \(value)")
        }
    }
    
    deinit {
        kvoToken?.invalidate()
    }
}

public class SomeClass: NSObject 
    public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    }
}

func foo() {
    someClass1.addObserver(self, forKeyPath: "v", options: .new, context: nil) //forKeyPath: #keyPath(SomeClass.v)
}

[Objective-C KVC 与 KVO]
[Swift KVC]

iOS Delegate pattern, NotificationCenter, KVO

Delegate

delegate pattern is a design pattern which can relate to Structural (Decorator or Wrapper pattern by GoF) which adds behaviors and responsibilities to an object without changing its code. Yo can move some logic into another helper class or use it as a skeleton. It is an alternative for Inheritance. Technically it uses association[About]. Kotlin language supports delegate pattern on language layer. As for iOS usually it is used for Loose coupling for communicate between classes Class1 <-> Class2 without Retain cycle[About] where SomeClass1 -> SomeClass2 and SomeClass2 weak-> SomeClass1

protocol SomeProtocol: AnyObject {
    func foo()
}

class SomeClass1: SomeProtocol {
    let someClass2 = SomeClass2()

    init() {
        someClass2.delegate = self
    }
    
    func foo() {
        print("foo is called")
    }
}

class SomeClass2 {
    
    weak var delegate: SomeProtocol?
    
    func onButtonTap() {
        delegate?.foo()
    }
}

NotificationCenter

NotificationCenter or NSNotificationCenter(Objective-C) (not Remote(Push) or Local Notifications) is a kind of publish/subscribe event bus. You have NotificationCenter singleton object whic is a single point for anybody to send or receive event. You can use it for sending events through the all application and anybody can interrupt it. Such system is fast for developing but hard for supporting. It is also a kind of Loose coupling system.

You can use next API of NotificationCenter:

post(name: object: userInfo:)
addObserver(_ observer: selector: name: object:)
removeObserver(_ observer: selector: object:)

E.g. system's showing, hiding keyboard

NotificationCenter.default.addObserver(self, selector: #selector(MyViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(MyViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

@objc func keyboardWillShow(_ notification:Notification) {
}
    
@objc func keyboardWillHide(_ notification:Notification) {
}

KVO

KVO - Key-Value Observing. Observing changes on Objective-C supported property value. You can use it when you need to know about some changes on object without any request

Objective-C -@property[About] which uses willChangeValueForKey and didChangeValueForKey for KVO

*Notes

  • If you override willChangeValueForKey, didChangeValueForKey the observeValueForKeyPath is not fired
  • If you use iVar[About] setter you are responsible for calling willChangeValueForKey, didChangeValueForKey
#import "SomeClass.h"

@interface SomeClass()
@property (nonatomic, strong) NSString *someVariable;
@end

@implementation SomeClass
- (void) foo 
{    
    [self addObserver: self forKeyPath: @"someVariable" options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context: nil];
    
    self.someVariable = @"set someVariable";
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"someVariable"]) {
        NSLog(@"%@", change);
    }
}

@end

Swift - NSObject and @objc dynamic[About]

class SomeClass1 : NSObject {
    @objc dynamic var v = 0
}

class SomeClass2 {
    var kvoToken: NSKeyValueObservation?
    
    func subscribe(someClass1: SomeClass1) {
        kvoToken = someClass1.observe(\.v, options: .new) { (object, change) in
            guard let value = change.newValue else { return }
            print("New value: \(value)")
        }
    }
    
    deinit {
        kvoToken?.invalidate()
    }
}

or

public class SomeClass: NSObject 
    public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    }
}

func foo() {
    someClass1.addObserver(self, forKeyPath: "v", options: .new, context: nil) //forKeyPath: #keyPath(SomeClass.v)
}

[Objective-C KVC vs KVO]
[Swift KVC]

伪心 2024-12-18 04:49:37

在我看来,KVO 更好,因为它具有零开销的优势。
即使您不使用/观察通知,也会产生开销。为了改进这一点,您可以使用不同的通知中心,但即使如此,也会产生一些开销(如果我错了,请纠正我)。 KVO 并不复杂,但当你必须观察很多东西时它是值得的。

In my opinion KVO is better because of it's zero-overhead advantages.
Notifications has overhead even if you aren't using/observing them. To improve that you can use different NotificationCenters but even with that some overhead will be there (correct me if I'm wrong). KVO is little complex but its worth when you have to observe lots of stuff.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文