У меня есть некоторое представление о том, что использовать, когда, но точное использование мне все еще не ясно. Может кто-нибудь объяснить на примере?
KVO против NSNotification против протокола/делегатов?
Ответы (6)
Используйте делегата, если вы хотите общаться только с одним объектом. Например, у tableView есть делегат — за работу с ним должен отвечать только один объект.
Используйте уведомления, если хотите сообщить всем, что что-то произошло. Например, в ситуациях нехватки памяти отправляется уведомление, сообщающее вашему приложению, что было предупреждение о нехватке памяти. Поскольку многие объекты в вашем приложении могут захотеть снизить использование памяти, это уведомление.
Я не думаю, что KVO вообще хорошая идея, и стараюсь не использовать его, но если вы хотите узнать, изменилось ли свойство, вы можете прослушать изменения.
Надеюсь, это поможет.
PS Это резюмирует, почему я думаю, что KVO не работает
Используйте делегат, когда существует отношение «главный/подчиненный» (делегат знает о классе, а класс знает о делегате), когда один класс находится выше в иерархии управления, и когда ясно, что не будет ситуаций, когда другие элементы (в основном пользовательский интерфейс) будут заинтересованы в том, чтобы узнать, что должен сказать класс.
Используйте уведомления, когда класс не заинтересован в том, чтобы знать, кто слушает и сколько их, кто угодно и сколько угодно может зарегистрироваться для получения уведомлений.
КВО полезно слушать "без ведома класса", хотя конечно это не так, класс на котором применяется КВО менять не надо.
Делегирование — это шаблон проектирования, который вы используете, когда хотите, чтобы какой-то другой объект изменил поведение отправителя. Пример: окна терминала избегают отображения любых строк или символов, обрезанных краями окна, потому что делегат окна терминала изменяет размер окна, чтобы гарантировать это.
Уведомление — это шаблон, который можно использовать, когда вам не нужен ответ. Пример: вы получаете уведомление о том, что система собирается перейти в спящий режим. Отправителю этого уведомления все равно, что вы с ним сделаете.
Даже если все три будут служить вашим потребностям в ситуации, делегирование по-прежнему будет предпочтительным вариантом:
- Возможность повторного использования.
- Самодокументированный. Изучив заголовочный файл класса, можно было бы сразу узнать, что/как происходит обмен данными.
На мой взгляд, KVO лучше из-за его преимуществ с нулевыми накладными расходами. Уведомления имеют накладные расходы, даже если вы их не используете/не наблюдаете. Чтобы улучшить это, вы можете использовать разные Центры уведомлений, но даже при этом будут некоторые накладные расходы (поправьте меня, если я ошибаюсь). KVO немного сложен, но он того стоит, когда вам нужно наблюдать за многими вещами.
Шаблон делегирования, NotificationCenter, KVO
Шаблон delegate
— это шаблон проектирования, который может относиться к шаблону Structural Decorator OR Wrapper от GoF, который добавляет поведение и обязанности к объекту без изменения его кода. Вы можете переместить некоторую логику в другой вспомогательный класс или использовать его в качестве скелета. Это альтернатива наследованию. Технически он использует association
[О программе]. Язык Kotlin поддерживает шаблон delegate
на языковом уровне. Что касается iOS, обычно он используется для Loose coupling
для связи между классами Class1 <-> Class2
без Retain cycle
[О программе] где SomeClass1 -> SomeClass2
и 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 or NSNotificationCenter(Objective-C)
(не удаленные (Push) или локальные уведомления) является разновидностью publish/subscribe event bus
. У вас есть NotificationCenter
одноэлементный объект, который является единственной точкой для отправки или получения события. Вы можете использовать его для отправки событий через все приложение, и любой может прервать его. Такая система быстро развивается, но тяжело поддерживается. Это тоже своего рода Loose coupling
система.
Вы можете использовать следующий API NotificationCenter:
post(name: object: userInfo:)
addObserver(_ observer: selector: name: object:)
removeObserver(_ observer: selector: object:)
KVO
— Наблюдение за ключом-значением. Наблюдение за изменениями значения поддерживаемого свойства Objective-C. Вы можете использовать его, когда вам нужно знать о каких-то изменениях на объекте без какого-либо запроса.
Objective-C -@property
[О программе], который использует willChangeValueForKey
и didChangeValueForKey
за KVO
*Примечания
- Если вы переопределяете
willChangeValueForKey
,didChangeValueForKey
,observeValueForKeyPath
не срабатывает - Если вы используете установщик iVar[About], вы несете ответственность за вызов
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
[О программе]
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 против KVO]
[Swift KVC]