3

We created an operation framework to add some functionality not found in the base classes (e.g. tracking success/failure). Parent operations are usually non-concurrent, and may only exist to manage child operations. Child operations which are typically concurrent (downloading xml and media asynchronously).

When we run our application on iOS 7, add we a number of operations to the operation queue, ~3/4 of the operations complete, and then the app appears to hang.

When I pause the app in the debugger, and I examine the operations in the queue (sOpQueue.operations), many of them are ready to run (isReady returns TRUE), but apparently none of them are executing (isExecuting returns FALSE, and I see no evidence of any operation running on any thread).

This is a new problem as of iOS 7.

The behavior does not seem to change when I increase or decrease the number of concurrent operations.

Does anyone have any suggestions on how to determine why a ready operation is not being started?

Thanks, Chuck

Chuck Doucette
  • 153
  • 2
  • 11
  • I posted the same question in Apple's Developer Forums: https://devforums.apple.com/message/904947#904947 The response from an Apple support professional was that overridding isReady was: "well off the beaten path". Thus, we are re-evaluating our approach. – Chuck Doucette Oct 09 '13 at 21:24

2 Answers2

5

Are you issuing the isReady Key Value Observing notification?

For example, I use a property:

@property (nonatomic, getter = isReady) BOOL ready;

And then have a custom setter:

- (void)setReady:(BOOL)ready
{
    [self willChangeValueForKey:@"isReady"];
    _ready = ready;
    [self didChangeValueForKey:@"isReady"];
}

As well as a custom getter that calls super:

- (BOOL)isReady
{
    return _ready && [super isReady];
}

And, because you implemented both the setter and getter, you have to manually synthesize the property at the beginning of the @implementation (usually you don't have to do this anymore, but if you implement all of the custom accessors, you have to manually @synthesize):

@synthesize ready = _ready;

Then, the operation starts when both of the following conditions are satisfied:

  • The ready property is set to YES (note, use the setter, not the ivar directly);

    self.ready = YES;
    

    or

    [self setReady:YES];
    
  • All other standard NSOperation criteria are satisfied (e.g. dependencies between operations, honoring maxConcurrentOperationCount, factoring in priorities, etc.).

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 1
    We are not maintaining a separate property, but we are issuing the KVO notification calls when the ready state changes. – Chuck Doucette Oct 08 '13 at 21:51
  • @ChuckDoucette Well, I don't know what to say in the absence of code samples, because both of the two above approaches work fine in iOS7 (I tested both before posting). I suspect some simple mistake in your `isReady` method, the associated KVO, or something else (like dependencies, queue suspended, etc.). I'd suggest creating the simplest possible demonstration of the problem you're having, and share that with us (assuming, of course, that in the process of going through this exercise, you don't just figure out what's going wrong). – Rob Oct 09 '13 at 04:02
  • The only things that could make an operation not run are (a) suspended queue; (b) not added to queue; (c) isReady method not returning correct value, and not calling `will`/`didChangeValueForKey` before/after changing `isReady` return value; (d) some weird dependency related issue (some dependent operation not started, not finishing, etc.). Those are the only things that leap to mind. But I haven't seen any iOS 7 issues, and I do a lot of operation stuff. – Rob Oct 09 '13 at 04:23
  • 1
    Just stumbled onto this answer and I would like to add that if you create a property and a custom setter for `isReady`, you should also override the getter and return the result of your own variable + `[super isReady]`. If you don't do so, certain things like dependencies may break (if you have any, that is). from the docs: "If you do so, your custom implementation should invoke super and incorporate its return value into the readiness state of the object." – Dima May 14 '14 at 06:16
2

I'll bet you have concurrent operations that haven't finished properly. Raise you number of concurrent operations and see if you can run longer before it hangs. Then figure out why your concurrent operations aren't correctly setting isFinished.

TomSwift
  • 39,369
  • 12
  • 121
  • 149
  • I have tried to increase (and decrease) the number of concurrent operations. Neither seemed to have any effect. Not finishing a concurrent operation is an excellent guess - but none of the operations left in our operation queue appeared to be executing ([op isExecuting] returned FALSE, and no operation threads appeared to be live. – Chuck Doucette Oct 08 '13 at 22:00
  • Any chance you have dependencies set on those operations? Can you confirm that the dependancies were successfully completed? And that you don't have a circular dependency? – TomSwift Oct 09 '13 at 15:28