4

Im trying to subclass a NSOperation, and read some sample from, they say: when the task finished, using KVO of NSOperation, to finish the operation, code here:

[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"]
finished = YES;
executing = NO;
[self didChangeValueForKey:@"isFinished"];
[self didChangeValueForKey:@"isExecuting"];

then isFinished get called

- (BOOL) isFinished{
    return(finished);
}

anyone could explain this to me? why isFinished gets called, will the isFinished finish the operation? as I understanded, do KVO manually need [self didChangeValueForKey:@"isExecuting"]; and I didnt see code like addobserver: and observeValueForKeyPath:

I write

 -(void)call
{
     [self willChangeValueForKey:@"isVip"];
     [self didChangeValueForKey:@"isVip"];
}

-(void)isVip
{
    NSLog(@"Im vip");
}

isVip is not called when do [self call];

nickyu
  • 141
  • 1
  • 11

3 Answers3

2

The NSOperationQueue implementation will observe the "isFinished" property of your operation (using KVO) so it knows when to remove it from the queue. isFinished is most likely being called by internal Apple code after it has been told of the change to its value.

Mike Weller
  • 45,401
  • 15
  • 131
  • 151
  • how and when does the dealloc func of operation be called? – nickyu Aug 27 '13 at 09:12
  • `dealloc` will be called when the operation's reference count hits 0, i.e. when nobody has an owning reference to it. – Mike Weller Aug 27 '13 at 09:15
  • cool, man, I give operation a observer and when isFinished, I call release and dealloc occurs. many thanks, – nickyu Aug 27 '13 at 09:20
  • BTW, you mentioned NSOperationQueue observe isFinished, and apple code call isFinished, is it a chain that, queue observes the op finished, then it tells apple code to call isFinished, if yes, I simply run the op by [op start] without a queue, isFinished gets called as normal. and if remove didChangeValueForKey, isFinished not called, and op cant be released – nickyu Aug 27 '13 at 09:27
1

Adding to quellish answer, this is how you would override executing, finished, cancelled.

//.m
@interface MyOperation ()  //class extension, make these otherwise read-only properties read-write, we must synthesize
@property(atomic, assign, readwrite, getter=isExecuting) BOOL executing;
@property(atomic, assign, readwrite, getter=isFinished) BOOL finished;
@property(atomic, assign, readwrite, getter=isCancelled) BOOL cancelled;
@end

@implementation CoreLocationOperation
@synthesize executing, finished, cancelled;

+ (BOOL)automaticallyNotifiesObserversForKey {
  return YES;
}

+ (NSSet *)keyPathsForValuesAffectingIsCancelled {
  NSSet *result = [NSSet setWithObject:@"cancelled"];
  return result;
}

+ (NSSet *)keyPathsForValuesAffectingIsExecuting {
  NSSet *result = [NSSet setWithObject:@"executing"];
      return result;
}

 + (NSSet *)keyPathsForValuesAffectingIsFinished {
    NSSet *result = [NSSet setWithObject:@"finished"];
   return result;
 }


- (void)start {
 //..
  //You can use self.executing = YES; (note we can change executing which would otherwise be read-only because we synthesized our own ivar.
  [self setExecuting:YES];
...
}

- (void)cancel {
//..
  //super will change the properties executing/finished for us or we can do it manually
  [super cancel];
...

}
    @end

I think this is clearer than having

[self willChangeValueForKey:_NSURLOperationIsFinished];
    [self setIsFinished:YES];
    [self didChangeValueForKey:_NSURLOperationIsFinished];
user3754239
  • 31
  • 1
  • 3
-2

First, you should not need to do manual KVO notifications. For an NSOperation subclass KVO notifications should be sent automatically unless your class has opted out of automatic KVO notifications by implementing +automaticallyNotifiesObserversForKey or +automaticallyNotifiesObserversOf<Key> to return NO.

NSOperation subclasses are not very useful unless they are added to an NSOperationQueue. When an operation is added to a queue, the queue uses KVO to observe the properties that indicate state changes, such as finished, executing, cancelled, etc. Note that these are not isFinished, isExecuting, or isCancelled - those are the names of the synthesized get accessors for those properties.

In your question you include this code:

[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"]
finished = YES;
executing = NO;
[self didChangeValueForKey:@"isFinished"];
[self didChangeValueForKey:@"isExecuting"];

What you're doing here is sending manual KVO notifications for the get accessors, not the properties being observed. Instead, this would accomplish what you seem to be trying to do:

[self setFinished:YES];
[self setExecuting:NO];

Rather than accessing instance variables directly, use the accessor methods. This will correctly send automatic change notifications for these properties.

If you are truly paranoid about KVO and want to send notifications for the get accessor key paths such as isFinished, register your property as a dependency of the key path:

+ (NSSet *) keyPathsForValuesAffectingIsFinished {
    NSSet   *result = [NSSet setWithObject:@"finished"];
    return result;
}

Registering dependencies is part of KVO compliance.

quellish
  • 21,123
  • 4
  • 76
  • 83
  • This is not correct. I'd refer readers our discussion in the comments of http://stackoverflow.com/a/25953779/1271826. – Rob Sep 23 '14 at 15:49
  • The author's original approach of manually invoking the KVO notification methods is attempting to manage dependencies between key paths. KVO has a mechanism for doing this, that should be used as a first resort. – quellish Sep 23 '14 at 16:32
  • Good addition of the key path dependencies pattern to get the required `isFinished` (and other) notifications. – Rob Sep 23 '14 at 19:01