KVO vs NSNotification vs protocol/delegates?

23,594

Solution 1

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

Solution 2

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.

Solution 3

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.

Solution 4

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.

Solution 5

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

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

Share:
23,594
Ankit Srivastava
Author by

Ankit Srivastava

Updated on July 09, 2022

Comments

  • Ankit Srivastava
    Ankit Srivastava almost 2 years

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

  • Liron
    Liron over 11 years
    How much, practically speaking, is the overhead?
  • Shinigami
    Shinigami over 10 years
    I know this is old, but I still wanna disagree with it :) That article makes excellent points re. why the KVO API is broken, but it also accurately states that it's still a powerful tool. There are lots of situations where it can save a lot of ugly code. Use it with a wrapper like the one he provided if you like, but do use it.
  • MANN
    MANN over 9 years
    Use KVO when observers need immediate response. And one can us NSNotifications when the observers can wait for the event loop.
  • deanWombourne
    deanWombourne over 9 years
    @MANN I don't think I understand - notifications aren't asynchronous, they are also fired immediately (although you don't get the willChange options you get with KVO)
  • MANN
    MANN over 9 years
    @deanWombourne Never said NSNotifications aren't async. Its just the next event loop. Link--developer.apple.com/library/ios/documentation/General/‌​… --> ...Instead of a central object that broadcasts notifications to all objects that have registered as observers, KVO notifications go directly to observing objects when changes in property values occur.
  • mfaani
    mfaani about 8 years
    @Shinigami may you please outline some simple good examples for KVO?
  • adib
    adib over 7 years
    KVO is a good idea if you need to move over the code to the Mac and then can take advantage of bindings.