{"action":"created","issue":{"url":"https://api.github.com/repos/purrrminator/AdvancedNSOperations/issues/8","labels_url":"https://api.github.com/repos/purrrminator/AdvancedNSOperations/issues/8/labels{/name}","comments_url":"https://api.github.com/repos/purrrminator/AdvancedNSOperations/issues/8/comments","events_url":"https://api.github.com/repos/purrrminator/AdvancedNSOperations/issues/8/events","html_url":"https://github.com/purrrminator/AdvancedNSOperations/issues/8","id":104560007,"number":8,"title":"Cancelled Operations never reach the finished state","user":{"login":"t089","id":846537,"avatar_url":"https://avatars.githubusercontent.com/u/846537?v=3","gravatar_id":"","url":"https://api.github.com/users/t089","html_url":"https://github.com/t089","followers_url":"https://api.github.com/users/t089/followers","following_url":"https://api.github.com/users/t089/following{/other_user}","gists_url":"https://api.github.com/users/t089/gists{/gist_id}","starred_url":"https://api.github.com/users/t089/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/t089/subscriptions","organizations_url":"https://api.github.com/users/t089/orgs","repos_url":"https://api.github.com/users/t089/repos","events_url":"https://api.github.com/users/t089/events{/privacy}","received_events_url":"https://api.github.com/users/t089/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-09-02T19:59:33Z","updated_at":"2015-09-03T06:38:57Z","closed_at":null,"body":"For my own project I also ported the Apple example code from Swift over to Objective-C. I've noticed a fundamental issue however with Apple's implementation of the NSOperation subclass. I wonder, if anyone following this project can confirm my issue:\r\n\r\nI think the `cancel` method is handled incorrectly. The [documentation](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/NSOperation_class/index.html#//apple_ref/doc/uid/TP40004591-RH2-SW18) states, that an operation that has been cancelled, should stop its execution and eventually return `YES` when asked `isFinished` and `NO` when asked `isExecuting`.\r\n\r\nIn Apple's example code the operation's internal state is set to `Cancelled` when `cancel` is invoked. So far so good. However, the method `setState:` defines `Cancelled` as a fixed state that cannot be changed. Moreover `isFinished` will return `YES` **only** if `state == Finished`. This means that a cancelled operation can never report that it is finished.\r\n\r\nThis behavior can be a serious problem when an operation is added to an operation queue and cancelled during execution. Since the operation will never reach the `Finished` state, the queue will not remove the operation. In a serial queue this can cause the queue to stall. Subsequent operations will never start."},"comment":{"url":"https://api.github.com/repos/purrrminator/AdvancedNSOperations/issues/comments/137351240","html_url":"https://github.com/purrrminator/AdvancedNSOperations/issues/8#issuecomment-137351240","issue_url":"https://api.github.com/repos/purrrminator/AdvancedNSOperations/issues/8","id":137351240,"user":{"login":"t089","id":846537,"avatar_url":"https://avatars.githubusercontent.com/u/846537?v=3","gravatar_id":"","url":"https://api.github.com/users/t089","html_url":"https://github.com/t089","followers_url":"https://api.github.com/users/t089/followers","following_url":"https://api.github.com/users/t089/following{/other_user}","gists_url":"https://api.github.com/users/t089/gists{/gist_id}","starred_url":"https://api.github.com/users/t089/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/t089/subscriptions","organizations_url":"https://api.github.com/users/t089/orgs","repos_url":"https://api.github.com/users/t089/repos","events_url":"https://api.github.com/users/t089/events{/privacy}","received_events_url":"https://api.github.com/users/t089/received_events","type":"User","site_admin":false},"created_at":"2015-09-03T06:38:57Z","updated_at":"2015-09-03T06:38:57Z","body":"Hi, well, not quite. Let me explain. When an operation is cancelled it does not necessarily mean that it is also finished. In fact, it might still perform some housekeeping before it can actually reach the finished state. `isFinished` should really only return `YES` if the operation is in a steady state and cannot perform anything anymore.\r\n\r\nIn fact, I just [redownloaded](https://developer.apple.com/videos/wwdc/2015/?id=226) the Apple Swift example code, which has been silently updated. They removed the `Cancelled` state completely and instead rely on NSOperation's implementation of the `cancel` method. This actually makes a lot of sense.\r\n\r\nWhen you call `cancel` on an `NSOperation` it sets an internal flag and subsequently returns `YES` to `isCancelled`. In your subclass you should check `isCancelled` regularly during execution. If it turns out to be `YES` you must stop executing and transition to the `Finished` state.\r\n\r\nSo the normal state mutation is always\r\n`Initialized --> Pending --> EvaluatingConditions --> Ready --> Executing --> Finishing --> Finished`\r\n\r\nIf you cancel the operation it will either skip the `Executing` state or abort the `Executing` state and move to `Finishing` and eventually `Finished`. The cancellation state therefore must be stored in a separate variable because it is superimposed on the internal state.\r\n\r\nThere are actually some more changes in the Swift code. I haven't yet looked at them all."}}