IOS development in some of the common parallel processing

By Bruce Butler,2015-04-01 23:46
17 views 0
IOS development in some of the common parallel processing

    IOS development in some of the common parallel


    This paper mainly discusses some commonly used multiple tasks of best

    practices.Including the Core Data of multithreaded access, parallel rendering UI, asynchronous network request and some in the running state under the condition of tight memory processing scheme of large file, etc.

Actually write asynchronous processing

    applications have a lot of pit!Therefore, this paper involved the sample are used as far as possible concise intuitive approach.Because of the more simple logical structure, the more can show the clear lines of code, the more easy to understand.For example, if the program is used in multi-level nested callbacks, basically that it will have a lot of space. Operation Queues vs. Grand Central Dispatch

    At present, in the iOS and OS X, the SDK provides two main classes of multitasking API:operation queuesandGrand Central DispatchGCD.The communist party is the more the

    underlying API based on C, and the operation the queues is widely considered to be based on the GCD and encapsulation of object-oriented (objective - C) multitasking API.About concurrent processing API level comparison, there are a lot ofRelated articlesIf interested to read.

    Compared to the communist party, the operation is the advantage of the queues: provides some very useful and convenient processing.One of the most important is that you can cancel the task in the task processing queue (for instance) later.In addition operation the queues in dealing with the dependencies between tasks also easier.While the GCD specialty is: can access and manipulate the operation the queues can use lower function.Details refer to the lower concurrent processing APIRelated articles

    Read on:

    ; StackOverflow: NSOperation vs. Grand Central Dispatch

    ; Blog: When to use NSOperation vs. GCD

    Core Data in the Background

    Before we set out to the Core Data of multithreading, we suggest read apple's first official document"Concurrency with Core Data guide". This document lists the rules, such as: don't directly transfer between different threads managed objects. Note that this means that not only can't to do not belong to own between threads managed object modification operations, even can't read the attributes. The correct approach is through the object ID and access to the object from the other thread context information to achieve the result of transfer

    object. In fact, as long as the following documents in various guidelines, then deal with the Core Data parallel programming problems is much easier.

    Xcode provides a way to create the Core Data of the templates, the working principle is through the main thread as a persistent store coordinator (persistent coordinator) to manipulate the managed object context, thus realize object persistence.Although this way is very convenient and applicable basic routine scenario, but if you want to operation Data is large, it is very necessary to the operation of the Core Data distribution to other threads (note: the operation of the large amount of Data may be blocking the main thread, blocking the main thread for a long time the user experience is very poor and is likely to lead to the application feign death or crash).

    The sample: to the Core Data import a large amount of Data:

    1. Create a separate operation for incoming data

    2. Create a and a main object context persistent store coordinator of the same object context 3. Introduce the context of the operation to save completed, inform the main managed object context to merge the data.

    In the sample app, you want to import a large group of transportation data in Berlin.In the process of import will show the progress bar and the user can cancel the current import operation at any time.Below the article wait for again with a table view to display has the import data import and refresh the interface at the same time.The sample's data signature Creative Commons license, you canIn the download.Use of open standardsGeneral Transit FeedFormat.

    Next create a subclass of NSOperation ImportOperation by autotype the main method to handle all import work.To create a private queue concurrency types of independent managed object context, this context need to manage their own queue, all operations on its must use performBlock or performBlockAndWait to trigger.This is quite important, this is ensure that these actions will be implemented in the correct thread on the key.

1 NSManagedObjectContext* context = [[NSManag

     edObjectContext alloc] initWithConcurrencyT

    2 ype:NSPrivateQueueConcurrencyType];

3 context.persistentStoreCoordinator = self.p



     context.undoManager = nil;


     [self.context performBlockAndWait:^




    [self import];


    Note: reuse the persistent store coordinator in the sample.Under normal circumstances,

    need to initialize the managed object contexts and specify its type, such as

    NSPrivateQueueConcurrencyType NSMainQueueConcurrencyType or NSConfinementConcurrencyType, including NSConfinementConcurrencyType is not

    recommended, as it is for some old legacy code to use.

    Before import, according to the iterative transportation lines the content of the data file,

    give each line can parse the data to create a managed object:

1 [lines enumerateObjectsUsingBlock:

    2 ^(NSString* line, NSUInteger idx, BOOL * sh





     NSArray* components = [line csvComponents];


     if (components.count < 5) {


     NSLog(@ "couldn't parse: %@" , components);


     return ;




     [Stop importCSVComponents:components intoCo

    10 ntext:context];


    Through the view controller to trigger the operation:

1 ImportOperation* operation = [[ImportOperat

     ion alloc]

    2 fileName:fileNam

    3 e];

    [self.operationQueue addOperation:operatio


    So far, multithreading import Data to the Core Data part has been completed.Next, is to

    cancel the import part, very simple only need to add a judgment in a set of quick enumeration

    block can be:

1 if (self.isCancelled) {

2 *shouldStop = YES;

3 return ;

4 }

    The last is to increase the progress bar, create a progressCallback attribute block in the

    operation.Pay attention to update the progress bar must be completed in the main thread,

    otherwise it will lead to UIKit collapse.

1 operation.progressCallback = ^( float progr





     [[NSOperationQueue mainQueue] addOperationW

4 ithBlock:^

5 {

6 self.progressIndicator.progress = progress;

7 }];


    Add the following line in fast enumeration to calls to update the progress bar block:

1 self.progressCallback(idx / ( float ) coun


    However, if you execute the sample app will find everything is particularly slow and cancel the operation also has a hysteresis.This is because the main opertation queue with the block to update the progress bar.By reducing frequency for update the progress bar can solve this problem,

    For example, with one percent of the rhythm update the progress bar:

1 NSInteger progressGranularity = lines.count

     / 100;


     if (idx % progressGranularity == 0) {


     self.progressCallback(idx / ( float ) coun

    4 t);

5 }

    Updating the Main Context

    We sample app in the table behind the view to hook up a special performs the task of taking data controller on the main thread.As described earlier, the same period in the process of importing data table view will display the data.To achieve this mission, in the process of Data import, need to send a broadcast to main context, to Store the init method of a class registered in the Core Data radio listening:

1 [[NSNotificationCenter defaultCenter]

2 addObserverForName:NSManagedObjectContextDi







     usingBlock:^(NSNotification* note)




     NSManagedObjectContext *moc = self.mainMana

    8 gedObjectContext;

9 if (note.object != moc)

10 [moc performBlock:^(){

11 [moc mergeChangesFromContextDidSaveNotifica







    Note: if the block in main queue passed as a parameter, the block will in the main execution in the queue.Run the sample, and the table view is at the end of the import will show the import results.For a few seconds, the user's operation will be blocked off.Therefore, we need to by batch operation to solve this problem.Because all import a larger data, should adopt the way of gradually import, otherwise it will soon be run out of memory, the efficiency

    will be low.At the same time, the gradual import will disperse the main thread to update the table view.

    As for the number of reasonable save basically have to rely on.Too frequently, defect is repeatedly I/O operation.Any number of too little, the application will become often no response.Through many experiments, we think this sample stored in 250 times more appropriate.After the improvement, the import process very smooth, update the table view, the process did not block the main context for too long.

    Other considerations

    At the time of import file, the sample code after the entire file into memory directly into a String object, then the branch.This way is very suitable for operating the smaller file, but for large files should adopt the way of lazy loading line by line.StackOverflowDave on DeLong provides a very good sample code to implement read line by line.The end of this article will provide a way to read the file samples.

    Note: in the app to run for the first time, also can through the sqlite to replace a large number of Data import Core Data, this process.Sqlite can put inside the bundle, also can download or dynamically generated from the server.In some cases, the real machine use sqlite stored procedure will be very fast.

    Finally, the way, the recent debate about child contexts many, do not recommend the use of it in a multithreaded.If in the main thread creates a context as the main context of child context, performs the save operation in the main thread of these products will beBlocking the main thread.If, in turn, will be the main context is set to other than the main thread context of child context, its effect with the traditional create two dependent contexts are similar, or the need to manually to do the other thread context changes and main context.

    Facts have proven that unless there is a better choice, or set a persistent store coordinator and two independent contexts is the reasonable way of the Core Data multithreaded operations.

    Read on:

    ; Core Data Programming Guide: Efficiently importing data

    ; Core Data Programming Guide: Concurrency with Core Data

    ; StackOverflow: Rules for working with Core Data

    ; WWDC 2012 Video: Core Data Best Practices

    ; Book: Core Data by Marcus Zarra

    UI Code in the Background

    First of all, emphasize: UIKit only executed on the main thread.In other words, in order not to block the UI, those and UIKit unrelated but time-consuming tasks are best performed

    on other threads.Also can't blind to the task assigned to the other threads the queue, that really needs to be optimized is the bottleneck task.

    Independent, time-consuming operation is most suited to put in operation in the queue:

1 __weak id weakSelf = self;

2 [self.operationQueue addOperationWithBlock:



     NSNumber* result = findLargestMersennePrime

    4 ();

5 [[NSOperationQueue mainQueue] addOperationW



     MyClass* strongSelf = weakSelf;


     strongSelf.textLabel.text = [result stringV

    8 alue];



    Seen as the sample, the reference of the inside is not simple.First will never be a self declaration of weak weak references, or it will form retain cycle circular references (block for self retain, private operation queue and retain the block, and then the self retain the operation queue).To avoid access during operation block has been automatically release object, and the weak of the self must be a weak reference is transformed into the strong strong reference. Drawing in the Background

    If the drawRect: really is application performance bottlenecks, can consider to use the core animation the layers or pretender pre-rendered images to replace the original plain core Graphics rendering.See Florian for real machine graphics performance analysispost, or you can see from the UIKit engineer Andy Matuschak the merits of itcomments.If really can't find other good way, it is necessary to put the drawing work related to other threads.Multithreaded rendering approach is more simple, the drawRect: directly to the code into the other operation to perform.Originally need to draw the view in the image view placeholder to wait, wait until

    the operation has been completed to inform and update the original view.The implementation level, with UIGraphicsGetCurrentContext UIGraphicsBeginImageContextWithOpertions used to replace the original drawing in the code:

1 UIGraphicsBeginImageContextWithOptions(siz

     e, NO, 0);


     // drawing code here


     UIImage *i = UIGraphicsGetImageFromCurrentI

    4 mageContext();

5 UIGraphicsEndImageContext();

    return i;

    The third parameter of the above code UIGraphicsBeginImageContextWithOpertion said to equipment main screen scale range, if the 0, so said automatic filling, so deal with whether equipment for the retina screen, will look very good.

    If it is in the mapping table view or collection view of the cell, it is best to all of them put into operation, then add the operation to the main queue queue, so that once the user slide triggered didEndDisplayingCell agent method, drawing operation can be cancelled at any time in the queue.The above content, are in placeWWDC2012Session211Building

    Concurrenct User Interfaces on iOSThere are covered.Except, of course, still can consider to try CALayer multithreaded rendering drawsAsynchronously attributes.But the need to assess the effect of using it, because sometimes it fast against the slow performance. Asynchronous request processing network

    Remember, all the requests to adopt the way of asynchronous processing! But some people use the GCD to deal with network request, the code is like this:

1 // Warning: please don't use this code.

2 dispatch_async(backgroundQueue, ^{

3 NSData* contents = [NSData dataWithContents



     dispatch_async(dispatch_get_main_queue(), ^

    5 {

6 // do something with the data.

7 });


    Zha seems ok, but have a lot of problems, this is a can't cancel the synchronous network request!Unless the request is completed, or you will put the thread stuck.If the request hasn't been response as a result, it can only do wait for timeout (such as dataWithContentsOfURL timeout time is 30 seconds).

    If the queue queue is linear, the queue network request thread by thread will be blocked.If the queue queue is executed in parallel, because the network request thread is blocked, the GCD need to issue a new thread to do things.Both the result is not good, it is best not to hinder any threads.

    How to solve these problems?Should use NSURLConnection way of asynchronous requests, and put all and request related things packaged in an operation to deal with.So can always control these parallel operations, such as the dependent relationships between processing operation, at any time to cancel the operation, etc., it will play to the operation queue convenient advantage.Here also it is important to note that the URL connections through the run loop to send events, because the event data transmission is generally not how time consuming, so use your main run the loop to be easy to handle.Then we use the other threads to deal with the data returned.Of course there are other ways, such as the popular third-party libraryAFNetworkingTreatment is: to create a separate thread, based on this thread to set the run loop, and then through the thread to handle the url connection.But I don't recommend readers themselves in this way.

    Copy the samplestart method to trigger the request of the operation:

1 - ( void )start

2 {

3 NSURLRequest* request = [NSURLRequest reque

Report this document

For any questions or suggestions please email