KVO vs NSNotification vs protocol / delegates?

时间:2011-10-23 07:06:06

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

虽然我有一些想法可以使用,但确切用法仍然不清楚。有人能解释一下......?感谢。

6 个答案:

答案 0 :(得分:38)

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

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

我认为KVO根本不是一个好主意,并且尽量不使用它但是,如果你想知道一个属性是否已经改变,你可以听取改变。

希望有所帮助。

PS This sums up why I think KVO is broken

答案 1 :(得分:13)

当存在“主/从”关系时(委托知道类和类知道委托),使用委托,一个类在控制层次结构的上方,当很明显不存在时其他元素(主要是UI)将有兴趣知道类必须说什么的情况。当班级不知道谁听谁以及他们有多少时,使用通知,任何人和任何号码都可以注册通知。 KVO对于“没有课堂知道”是有用的,虽然当然不是这种情况,但是应用KVO的课程不需要改变。

答案 2 :(得分:2)

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

通知是您不需要回复时使用的模式。示例:您会收到系统即将进入睡眠状态的通知。该通知的发件人并不关心您对此采取的措施。

答案 3 :(得分:2)

即使所有三个都满足您的需求,代表仍然是一个更喜欢的选择:

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

答案 4 :(得分:1)

在我看来,KVO更好,因为它具有零开销优势。 即使您没有使用/观察它们,通知也会有开销。为了改善它你可以使用不同的NotificationCenters,但即使有一些开销将在那里(纠正我,如果我错了)。 KVO有点复杂,但是当你需要观察很多东西时它是值得的。

答案 5 :(得分:0)

委托模式、通知中心、KVO

delegate 模式是一种设计模式,它可以与 GoF 的 Structural Decorator OR Wrapper 模式相关联,它可以在不更改对象代码的情况下向对象添加行为和责任。你可以将一些逻辑移到另一个辅助类中或将其用作骨架。它是继承的替代方案。从技术上讲,它使用 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:)

KVO - 键值观察。观察 Objective-C 支持的属性值的变化。无需任何请求即可在需要了解对象的某些更改时使用它

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

*备注

  • 如果您覆盖 willChangeValueForKeydidChangeValueForKey 不会触发 observeValueForKeyPath
  • 如果您使用 iVar[About] setter,则您有责任调用 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 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]