user1834305
user1834305

Reputation:

Properly observing object using KVO

I have a view which is observing the properties of a single object using KVO. I have observed all the properties of the object in the view.

[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
 [person addObserver:self forKeyPath:@"photo" options:NSKeyValueObservingOptionNew context:NULL];
 [person addObserver:self forKeyPath:@"address" options:NSKeyValueObservingOptionNew context:NULL];

Now, when there is change in just one property, it seems to be fine but when the whole object change, the notification gets triggered 3/4 times in just fraction of seconds. I need to load the data from the network based on the changes. While a single property change creates a single network request, but if multiple properties change at the same time. It creates a queue of request for the same object. This leads to some problem. How can I observe multiple properties at the same time and load only once even if all the properties changes. Please do help me. This is a serious trouble, I have got into.

Upvotes: 4

Views: 704

Answers (1)

Fruity Geek
Fruity Geek

Reputation: 7381

You can use a dispatch source in Grand Central Dispatch to coalesce the property change observations so they aren't happening more often than you can handle them.

@implementation Controller
{
    dispatch_source_t source;
}
- (id)init
{
    self = [super init];
    if (self)
    {
        //We are using data add source type, but not actually using the added data.
        source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
        dispatch_source_set_event_handler(source, ^{
            //Insert your network call to load data from the network.
            //The event handler will only be called when another event handler is not being processed. So you won't attempt to do another network call until the last call was completed.

        });
        //Dispatch sources always start out suspended so you can add the event handler. You must resume them after creating them if you want events to be delivered)
        dispatch_resume(source);
    }
    return self;
}
- (void)dealloc
{
    dispatch_release(source);
}
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    //Tell dispatch source some data changed. It will only call the event handler if an event is not currently being handled.
    //You could add a custom timer based coalesce here for when no events are currently being processed if you want to delay all initial events to potentially wait for more changes 
    dispatch_source_merge_data(source, 1);
}
@end

So the first property change notification triggers the dispatch source event handler. Subsequent property changes that happen while an existing event is running are queued to run as soon as the last one is complete. This means if 5 properties change in quick succession, you will get 2 network calls (instead of 5 network calls). You can add a custom timer based coalesce when no events are being processed if you would prefer to sacrifice instant responsiveness to notifications to eliminate the second network call.

Upvotes: 6

Related Questions