Correct object observation using KVO

I have a view that observes the properties of a single object using KVO. I noticed 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 only one property is changed, it seems like everything is fine, but when the whole object changes, the notification fires 3/4 times in just a few seconds. I need to download data from the web based on changes. Although one property change creates a single network request, but if multiple properties are changed at the same time. It creates a request queue for the same object. This leads to some problem. How can I observe multiple properties at the same time and only load once even if all properties change. Please help me. This is a serious problem, I got it.

+3


source to share


1 answer


You can use a dispatch source in Grand Central Dispatch to combine property change observations so that they don't happen more often than you can handle.

@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 fires the dispatch source event handler. Subsequent property changes that occur while the existing event is running are queued to run as soon as the last one is completed. This means that if 5 properties change quickly, you will get 2 net calls (instead of 5 net calls). You can add your own coalescing-based timer for when no events are being processed if you would rather sacrifice instant notification responsiveness to eliminate a second network call.

+6


source







All Articles