【问题标题】:KVO vs NSNotification vs protocol/delegates?KVO vs NSNotification vs 协议/委托?
【发布时间】:2011-12-13 11:18:02
【问题描述】:

我对何时使用哪个有一些想法,但我仍然不清楚确切的用法。有人可以举例说明吗?

【问题讨论】:

标签: iphone ios protocols key-value-observing nsnotifications


【解决方案1】:

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

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

我认为 KVO 根本不是一个好主意,尽量不要使用它,但是,如果你想知道某个属性是否发生了变化,你可以监听变化。

希望对您有所帮助。

PSThis sums up why I think KVO is broken

【讨论】:

  • 我知道这是旧的,但我仍然想不同意它:) 那篇文章提出了很好的观点。为什么 KVO API 被破坏了,但它也准确地指出它仍然是一个强大的工具。在很多情况下,它可以节省很多丑陋的代码。如果您愿意,可以将它与他提供的包装器一起使用,但请务必使用它。
  • 当观察者需要立即响应时使用 KVO。当观察者可以等待事件循环时,我们可以使用 NSNotifications。
  • @MANN 我不认为我理解 - 通知不是异步的,它们也会立即触发(尽管您没有获得使用 KVO 获得的 willChange 选项)
  • @deanWombourne 从来没有说过 NSNotifications 不是异步的。它只是下一个事件循环。 Link--developer.apple.com/library/ios/documentation/General/… --> ...当属性值发生变化时,KVO 通知不是向所有已注册为观察者的对象广播通知的中心对象,而是直接发送给观察对象。
  • @Shinigami 能否请您概述一些 KVO 的简单好示例?
【解决方案2】:

当存在“主/从”关系时使用委托(委托知道类,类知道委托),一个类在控制层次结构中更高,并且很明显不会有其他元素(主要是 UI)有兴趣知道类要说什么的情况。

当班级不想知道谁在听以及他们有多少人不感兴趣时​​,使用通知,任何人和任何号码都可以注册通知。

KVO 在“不知道类”的情况下听很有用,虽然当然不是这样,应用 KVO 的类不需要更改。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      即使在某种情况下这三个都可以满足您的需求,委托仍然是首选:

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

        • 实际开销是多少?
        【解决方案6】:

        委托模式、通知中心、KVO

        委托

        delegate 模式是一种与结构(GoF 的装饰器或包装器模式)相关的设计模式,它在不更改代码的情况下向对象添加行为和职责。你可以将一些逻辑移动到另一个辅助类中或将其用作骨架。它是继承的替代方案。从技术上讲,它使用association[About]。 Kotlin 语言在语言层支持delegate 模式。至于iOS,它通常用于Loose coupling 用于类之间的通信Class1 <-> Class2 没有Retain cycle[About] 其中SomeClass1 -> SomeClass2SomeClass2 weak-> SomeClass1

        protocol SomeProtocol {
            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 or NSNotificationCenter(Objective-C)(不是远程(推送)或本地通知)是publish/subscribe event bus 的一种。您有 NotificationCenter 单例对象,它是任何人发送或接收事件的单点。您可以使用它通过所有应用程序发送事件,任何人都可以中断它。这样的系统开发速度快,但支持难度大。也是Loose coupling系统的一种。

        您可以使用 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 - 键值观察。观察 Objective-C 支持的属性值的变化。当您需要了解对象的某些更改而无需任何请求时,您可以使用它

        Objective-C -@property[About] 使用willChangeValueForKeydidChangeValueForKey 表示KVO

        *备注

        • 如果您覆盖 willChangeValueForKeydidChangeValueForKey,则不会触发 observeValueForKeyPath
        • 如果你使用 iVar[About] setter 你负责调用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@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()
            }
        }
        

        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)
        }
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-10
          • 2011-02-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多