Monday, September 14, 2015

My dive into KVO (Key Value Observers)

I've played with KVO for a while but was always afraid of it. Most people are I find. whenever I brought it up, people would say things like "dangerous", "spaghetti code", "it will crash your app and be impossible to debug" or just plain "messy". It is a powerful feature, but it can so easily become more of a problem than it is worth.

KVO (Key Value Observer) lets you listen for changes on an object's property. Whenever that property changes, you will get an observation notification. This can be very helpful when you are waiting for something to get updated but if you aren't careful you can really mess things up.

A few examples of things to watch out for;
-cycling, if you are listening for one property, it changes, you change your data as a result and then you changes trigger another KVO that changes other properties, which changes the properties you were listening to in the first place, bad stuff will happen.

-crashing on removal. If you try and remove the observation of a property when you were not even listening to it or worse yet, if you already stopped listening to it, it will crash your app. (People like to put @try @catch around that to avoid it but that is a lazy way of fixing it, create a bool to let you know if you already stopped listening.)

-crashing on not correct listening. When you start observing properties, it will run the observation method. This will be run on everything. If you don't account for one of the items you are listening to, or even worse, something else tells you to listen to something you are not aware of, when it goes through and doesn't do anything, it will crash your app.

-inconsistency. If you are not aware of other people KVOing your properties, you could be doing something to your properties (which you are entitled to of course because it is yours) and you might be triggering KVOs all over the place for the wrong reasons which is bad on many levels.

I am currently working with someone whom got over the hurtles and loves KVO, Here are some tips he gave me to prevent much of this from happening.

+ Try and only start and stop your KVO on the init and dealloc. If you need to stop or start listening outside of these, create a bool to mark whether or not you are currently listening.

+ Use NSCopying. This will prevent possible cycling on KVOs

 + Don't listen to overly changeable data. Any property that is mutable is probably changing way more than you need to listen for so stick to the immutable properties

+ Don't bother with contexts. keypaths should be unique enough for figuring out what you are listening to. Context add an unnecessary level of complexity to your code. If you are listening to multiple items with the same keypath, you have other issues.

So now that you are hopeful, or at least willing to try something. Here are the basics on how to implement it;

- (id)init {
    if (self = [super init]) {        
        [self addObserver:self forKeyPath:@"activeStation.playbackStatus" options:NSKeyValueObservingOptionNew context:nil];
        [self addObserver:self forKeyPath:@"activeStation" options:NSKeyValueObservingOptionNew context:nil];
    }
    return self;

}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"activeStation.playbackStatus"];
    [self removeObserver:self forKeyPath:@"activeStation"];
}


#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"activeStation.playbackStatus"]) {

        }
    } else if ([keyPath isEqualToString:@"activeStation"]) {

    }
}

and that is about it. When should you use KVO? Here is a link that my friend found that helps let you know. As you can see, it is limited on where to KVO but never handicap yourself just because of fear!