Tags

Month: April 2016

NSOperation and NSOperationQueue are built on top of GCD and provide additional features built-in that are very hard to achieve using just GCD. This post will provide basic introduction of NSOperation and how to create a simple custom NSOperation and execute it. In my next post on this series, I will delve into NSOperationQueue and how to use your custom NSOperation in conjunction with NSOperationQueue to satisfy your different scheduling requirements.

NSOperation

NSOperation is an abstract class that provides a way to consolidate the data required and the code associated with a task. In order to create a custom operation, we should derive from the NSOperation class and add the data and code associated with our custom operation. Alternatively we can use one of the 2 Apple-provided subclasses of NSOperation: NSBlockOperation and NSInvocationOperation. I will not go into details of these specific subclasses of NSOperation here.

Features of NSOperation

NSOperation, in spite of being an abstract class, provides wealth of features associated with executing a task. The following are the two most important features which , though not impossible, are very messy to implement with GCD:

Tracking state of an task

The states that a task wrapped in NSOperation can take are “Ready”, “Executing” and “Finished”

Ability to cancel an operation.

NSOperation provides the ability to cancel an operation by cancel() method.

At the core, the cancel() method just sets a boolean cancelled = true. For any of the custom subclasses of NSOperation, the onus is on us to ensure that we account for the value of the cancelled boolean and gracefully exit our task.

Will go into more details further in the blog

Below I will list the steps that go into encompassing a task into an NSOperation. For simplicity, the example I am going to describe below will execute synchronously ( i.e When the code execution finishes the task finishes (or) task does not launch any asynchronous code as part of its logic)

custom NSOperation Example

Let’s say our gazelle(or deer) is in the forest grazing. A single unit of task that the gazelle needs to perform is checkForPredators(). In short, the gazelle looks up, checks its surrounding and will be done with the task.

In this scenario,

the data associated with the task would be the gazelle itself and

the logic associated with the task would be the call to “checkForPredators()”

In this most straightforward scenario, the new task only needs to override the main() method of the NSOperation abstract class. Below are the 3 things to remember when overriding main():

Ensure you are setting self.executing = true before starting task logic in main()

checkForPredators(gazelle)// Check for predators done when this func returns

self.executing=false

}

}

The simplest way to start the execution of a custom subclass of NSOperation is to call the start() method. When we look at the open source code for NSOperation ( in Swift Yay!!) here, we notice that the start() method is as below

NSOperation start()

Swift

1

2

3

4

public funcstart(){

main()

finish()// ensures the state is updated to finished = true

}

Hence, when we call GazelleCheckPredatorsOperation.start(), our custom implementation of the main() is called and then finish(), which cleans up the state of the Operation as soon as the main() function call returns.

CONGRATULATIONS !!!! you now have successfully created the basic NSOperation and started it to perform a pre-defined task!!!

I have been working on Swift for little over a year now and I am thoroughly enjoying this foray into iOS . With the experience I have gained working on Bing iOS app, the 5th best iOS app of 2016 as per PCMag, I have learned some iOS goodness along the way. I submitted the proposal hoping to share my learning on “Asynchronous NSOperation and how I integrated this into our app”

I am really honored and thankful to the Forward Swift curators for giving me this chance. I will be working on some blog posts on related topics in the upcoming week to get a jump start on what topics to present and how. Hoping to create some example playground similar to what was done at RWDevCon

Core Data is a framework the provides us with the ability to represent the model layer of iOS app along with persistence features.

The 2 essential CoreData framework classes to know are:

NSManagedObjectContext

Apple describes this as a scratchpad in-memory for app to perform core data operations. Users can later decide if they want to save ( commit) these changes to the disk

We are allowed to create multiple NSManagedObjectContext and specify child-parent relationship among them. So any changes made to parent are automatically propagated to the child. Any changes made to the child are propagated to the parent when child.save() is called

NSManagedObject

All the data stored in the Core Data database , when retrieved in memory is represented by Core Data framework in form of NSManagedObjects

In the most standard scenarios, we can get away with single NSManagedContextObject created with MainQueueConcurrencyType. In this case, all the Core Data operations happen on the main queue along with UI related actions. This setup is recommended only if the core data actions support the user actions ( Eg: A new TODO item has been created, an existing work item is marked completed etc).

But for certain apps, the core data operations do not have direct correlation with user’s scenario. Eg: Import operations, storing network data in background so that user’s can access it later, if necessary. As for any multi-threaded system operations that do not directly correlate with user scenario should be performed on a background thread/queue. Core Data framework hence provides us with the option of creating a NSManagedContext with PrivateQueueConcurrencyType. It goes a step further and also creates a private queue that is exclusively reserved for this NSManagedContext object.

Below are some of the rules that must be followed if you do not want your app that uses CoreData to crash (or) corrupt the database:

A NSManagedObjectContext should be used only on the queue that is associated with it.

If initialized with .PrivateQueueConcurrencyType, a private, internal queue is created that is associated with the object. This queue can be accessed by instance methods .performBlockAndWait (for sync ops) and .performBlock (for async ops)

If initialized with .MainQueueConcurrencyType, the object can be used only on the main queue. The same instance methods ( performBlock and performBlockAndQueue) can be used here as well.

An NSManagedObject should not be used outside the thread in which it is initialized

In order to stress the importance of these rules, Apple has associated a scary name when these rules are not followed – “Core Data Violations”. In fact, Apple considered these violations so important, that they decided to start crashing when a CoreData violation is present, starting iOS 6. But there were already so many apps in App Store and most of them would start crashing and so Apple to turn it OFF.

Today it is still possible to get your app into AppStore with Core Data Violations in it. Refer to this blog post to understand how to enable this detection when developing your app. When this Core Data Violations detection is enabled, all violations of Core Data threading rules will cause debugger to stop with stack trace ending at

iOS developers seem to be split about Core Data framework. I have seen people who feel like Core Data “just makes sense” for them to those who consider Core Data is very hard to comprehend. I definitely belonged to those in the latter.

Due to a crash in Core Data code at work, I had to take a closer look at Core Data framework ( Original author the core data code left team and I am the one who drew the short straw for ownership). This code has been working , sans crashes, for a few releases now, but since iOS 9.3 it has started randomly crashing in production ( with of course no reliable repro steps). The exact code that has crashed has not been touched for a few releases.

Due to apparent lack of explanation, I dove deep into how to debug Core data code and found this debugging nugget.

Core data debugging nugget

Create a new Xcode scheme by selecting the tab in top left corner right before the simulator/device type and selecting “New Scheme”. Name it something eay to understand like “TestCoreData” (Using Ray Wenderlich video tutorial project as example here)

This new scheme will not be the default selected scheme. Click on the “Scheme” tab again and this time select “Edit scheme” as shown below

Select the “Run” option from the left menu and select the “Arguments” tab from the right side wizard. Here click on the “+” sign under the “Arguments passed on launch” tab. Here enter the text “-com.apple.CoreData.ConcurrencyDebug 1” and hit “Close” at the right bottom corner of the wizard.

The next time you launch you app with this scheme selected and if you are using your CoreData layer incorrectly, you will get a crash that is similar to the below with the entry at end of your stack trace pointing to “NSManagedObjectContext_Multithreading_Violation_AllThatIsLeftToUsIsHonor__ ” providing you the indication that you are creating your NSManagedObjectContext object (or) NSManagedObject object in one thread and using it in an another ( which is CoreData violation).

An interesting side note is that, as per Marcus Zarra, this was enabled by default in iOS 6 but then was removed because this would too many production apps to crash.

I personally think that this is a very vital part of Core Data development as it would avoid random crashes due to CoreData threading violations that are virtually impossible to root cause.

I have redesigned our app’s Core data infrastructure to take advantage of specifying parent-child relationship among NSManagedObjectContext objects while also fixing the CoreData violations across our app. I have also created a new scheme that will be shared across the team for Core Data debugging and which will also be enabled by default in Jenkins when buiding “dogfood” version of our app.