GCD is an extremely important feature introduced in iOS 4.0, OS X 10.6 and later versions.
Its used to distribute the computation across multiple cores dispatching any number of threads needed and it is optimized to work with devices with multi-core processors.
When using GCD you don’t need to handle any multiple threads yourself, GCD technology will take care of that for you, so you can get to focus on the task itself.
GCD works with Blocks, blocks of code that you need to execute in the background,
in low, default or high priority queue, in a serial or concurrent order.
Using GCD in your application will bring a great benefit of all the features mentioned above without the hassle of managing the threads your self and knowing that the GCD technology will be utilizing multi-core processors found in iOS devices today.
Before we dig into GCD itself we’ll need to be comfortable using blocks. Blocks are a C-level syntactic and runtime feature that let you pass a function as an argument, store it, or execute it on multiple threads. Blocks look very similar to functions, instead of the name of the function replace it with ^ and you’re good to go.
NSLog(@"found the first different Object: %@",obj);
*stop = YES;
}
else {
NSLog(@"%@ at index:%d", obj, idx);
}
return;
}];
So the output will be:
1 at index:0
2 at index:1
3 at index:2
4 at index:3
found the first different Object:
Now that we know more about blocks, I’ll show you how you can add them to a serial queue.
Adding blocks to serial queues ensures that these blocks of code will be executed one after the other. If one block is taking too long, the rest of the blocks will have to wait until its done.
Serial blocks are useful in many cases where ensuring the order of execution is critical.
This block is named counterBlock and it takes arguments: int, char, NSUInteger and an object typed GCDMaster.
The arguments in this block are passed in from the method calling the block. As you saw earlier, you can specify any configuration of a block just like you would do with functions.
Now lets talk about Dispatch Queues. Apple’s definition of Dispatch Queues is:
Dispatch queues are a C-based mechanism for executing custom tasks. A dispatch queue executes tasks either serially or concurrently but always in a first-in, first-out order. (In other words, a dispatch queue always dequeues and starts tasks in the same order in which they were added to the queue.) A serial dispatch queue runs only one task at a time, waiting until that task is complete before dequeuing and starting a new one. By contrast, a concurrent dispatch queue starts as many tasks as it can without waiting for already started tasks to finish.
We want to dispatch a few of these blocks on our serial Queue.
What we did now is we added 4 blocks to our queue “serialQueue” with various different arguments. These blocks will be executed one after the other. When block 1 is finished 2 is started and so forth.
So if we have a log message when the block start and end it will look like this:
==block 1 started ==
==block 1 ended ==
==block 2 started ==
==block 2 ended ==
==block 3 started ==
…
But if we would want to execute the blocks concurrently and we don’t have the need to run them sequentially then we’ll need to create a concurrent queue.
Apple provides you with a number of concurrent queues that you can access from you’re application. These queues are global and they have 3 priorities DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_PRIORITY_DEFAULT and DISPATCH_QUEUE_PRIORITY_LOW priority.
So GCD runs these blocks concurrently, there is no way to expect which one executes first.
So the output would be random like this:
==block 2 started ==
==block 3 started ==
==block 3 ended ==
==block 1 started ==
…
So once the Queue is running you can suspend it and resume it by calling the appropriate methods dispatch_suspend and dispatch_resume. Note that the calls to dispatch_suspend and dispatch_resume must be balanced.
Please feel free to run the attached project with the example given in this blog yourself and feel free to experiment with GCD and become familiar with it. Once you start getting comfortable working with blocks and using queues you will be able to introduce many performance improvements to your code and send heavy operations back to a GCD queue while freeing the UI to move freely Download project on GitHub https://github.com/edwardIshaq/GCDDemo
One of my first iOS projects that I've worked on at Thermopylae Science + Technology.
Which is a platform that allows the admin to construct the application on the server reflecting to all the mobile app users. The web platform enables the admin to add many different types of widgets including but not limited to Youtube videos, Flicker photo streams, Twitter feeds, Track information, Map layers, Map Pins internal routing and so on.
UICollectionView is one of the new exciting features introduced in iOS 6. It enables you to organize your views on the screen dynamically. You can implement grids, stacks, circular or any other arrangement you like.
Collection View building blocks
UICollectionView is very similar to UITableView. The UICollectionView has a delegate and dataSource protocols and a UICollectionViewLayout object that provides essential information for the collection's construction and workflow.
1. Class Structure
the dataSource protocol is very similar to the tableView dataSource protocol, it breaks down the structure to sections and items in each section. Although its up to the UICollectionViewLayout object to decide how to present them on the content area.To do so the layout object can choose to extend the UICollectionViewDelegate just like in the case of the flow layout where it extends the UICollectionViewDelegate into a new protocol called UICollectionViewDelegateFlowLayout. If you choose to provide your own layout and you need to customize your delegate then you should also extend the UICollectionViewDelegate to your own needs.The layout's job is as follow:1. prepare the layout (which we'll dig into at a later point) 2. provide the content size3. provide layout attributes objects for the cells that are displayed in some rect on the collection viewThese layout attributes of class UICollectionViewLayoutAttributes provide basic information such as frame, center, alpha, zIndex attributes that are later applied on the cell. Which will result in positioning these cells in the appropriate place in the collection view.
Flow Layout
What is UICollectionFlowLayout ?
Flow layout object provides a customizable way to organize the cells in the collection view.Layout objects in general are designed to calculate and provide specific attributes to cells and decoration views.
2. FlowLayout's arrangement of the view elements in the collectionView
Courtesy of Apple.com
as you can see from the diagram above, the cells are laid out one after the other and they flow from the left to right.
3. notice the count
In the screen shot above you can see how each cell displays its section and index above to demonstrate their position in relation to the index.Flow layout can change the scrolling direction to horizontal as well as vertical.
When to use UICollectionViewFlowLayout ?
Flow layout is useful if you want to display your objects in a grid, single file/line anything that has a linear flow to it could be implemented using the flow layout.
Configuring flow layout
Flow layout allows you to configure:
- scrollDirection: horizontal or vertical
- line spacings, which you can see in the example above that a minimum spacing is maintained.
- item size
- header and footer reference sizes
4. header and footers for each section
So, lets start building this collection example step by step.
Straight forward as you can see, my view controller implements the UICollectionViewDataSource and the UICollectionViewDelegateFlowLayout for the layout and datasource. And we have an outlet for our UICollectionView.
1 - set the collectionView's delegate and dataSource to be my viewController's responsibilities. we declared in the header that we'll be implementing these protocols.
2- registering a class for the cell, in this step we tell the UICollectionView which class will be our cell made of and assign it a reuse identifier. UICollectionView will later instantiate cells of that class when we need them.
3- register header, just to demonstrate the different ways I registered the header using a Nib and not a class. UICollectionView allows you either. You notice that we registered this header for: SupplementaryViewOfKind:UICollectionElementKindSectionHeader. UICollectionView enables you to display some meta data on your cells and FlowLayout specifically comes equipped with header and footer. As you build your own layouts you can define more supplementary views of your own.
Since we never assigned the layout object our selfs, it was assigned for us in the interface builder:
5. layout config in Xib
Layout: Flow layout
Scroll Direction: Vertical
Also you could setup the Header & Footer from Xib to some degree here.
you can see that we configured the layout such that it keeps a minimum 20 px between the lines. We set each item to be of size 100x100, which later you can change on a per item basis in a method called: collectionView:layout:sizeForItemAtIndexPath: where you can give a specific size to each item.
you can also set the insets of each section using: sectionInset property.
Important note about the header/footer reference size, when the flow layout scrolls vertically the layout will use the height value to keep the header/footer in that height and stretch the footer to the collection widths. when scrolling horizontally it will do exactly the opposite, it will take the width and stretch on the height.
So we dequeue a reusable cell with identifier SIMPLE_CELL_ID, same as the cell class we registered before. The cool thing about the UICollectionView is that you dont need to check the if the dequeued cell is null , the collection will instantiate one for you in that case automatically.
Lets provide some final implementation for the supplementary elements:
same logic as with the cells, we dequeue a supplementary element based on the kind of element and configure it as needed.
The result will look like image 4 above.
Customizing your own layout
If the flow layout is not working for you, you can implement your own layout object.
you will need to subclass UICollectionViewLayout and implement the following methods at a minimum:
- (void)prepareLayout
This method is called before the layout is drawn on screen to allow you to prepare any calculations ahead of times.
prepareLayout will also be called when ever the layout is invalidated. Invalidating the layout will throw away all the existing layout information and forces the layout object to start over again. the layout can call this on itself or can trigger this automatically when the bounds change, see shouldInvalidateLayoutForBoundsChange for more details.
- (CGSize)collectionViewContentSize
the second step is to figure out our content size area. here you will tell the UICollectionView what is the size of the content area you will be needing to display all your cells and decorations.
the last step is supplying an array of layout attributed for the displayed cells in the provided rect.
Since UICollectionView is a subclass of a UIScrollView displays only portion of the items and allows you to scroll to see the rest of the elements. so at any time there is only a certain area provided by the rect that is visible and needing the layout attributes.
6. layout process
Curtesy of Apple.com
UICollectionViewLayoutAttributes
The layout uses objects of class UICollectionViewLayoutAttributes (for short layout attributes) to describe the specific layout attributes for a given item in a collection view. the layout object will create layout attributes as needed by the collection view. Layout attributes include attributes such as frame, center, size, transform3D, alpha, zIndex and hidden.
zIndex is especially useful when cells overlap and you need to provide some order on the z-axis, deciding which cell is displayed on top.
Circular layout ExampleOne of the cool demonstrations at Apple's WWDC 2012 was a circular layout. Its a layout where the cells are displayed in a circle in the middle of the collection view.
for this layout to work a few things are required from our new custom layout object.
we need to know the number of items displayed so we can calculate the angle increment between each cell. which should be done in the prepareLayout method:
- (void)prepareLayout {
[superprepareLayout];
//calculate the degree increment
//360 / items_count
if (self.numItems) {
angle = (CGFloat)2*M_PI/self.numItems;
}
else{
angle = 0;
}
}
Next, we need to provide layout attributes for each cell in the visible rect area. Since we want to display all the cells at once for this demo, we'll provide layout attributes for all of them.
So above you will see that I iterate over all the items and calculate the (x,y) coordinates of that item on the circle plus some normalization for the items to be positioned in the collection view and then some zIndex calculation to make sure that the cells overlap in a nice way.
Feel free to download the demo project from GitHub at:
In every software development cycle you will encounter the optimization phase,
where you’re app is now doing what it should be doing but the UI is clunky or
freezes at some points while the app is doing some heavy lifting work like parsing
data, working on database, manipulating images or files etc. These heavy operations
if done on the main thread in an iOS app life cycle will block the UI updates and
responsiveness of your app. This problem is solved using different methods. You
could send some blocks to the background using Grand Central Dispatch (GCD) or
spawn your own NSTheads.
NSOperation represents a single unit of computation. It provides you with a thread-
safe way to model your workload in aspects of state, priority dependencies and
cancelation. So you will be able to subclass NSOperation to customize your own
pieces of work in any order/dependency you need to suite your needs for every app
you build. It gives an OOP perspective on computation which is a great deal of
benefit for today’s complicated requirement and data handling.
Classes
The class structure of the NSOperation is straight forward.
Each Queue type NSOperationQueue will hold a number of operations
(NSOperation) .
NSOperation: is an abstract class which you would subclass or use any of its
subclasses to execute your work in. Although it is an abstract class it already
implements the thread safe features which allows you to focus on the actual work
that needs to be done.
NSBlockOperation: is a concrete subclass of NSOperation which allows you to
execute several blocks at once with out having to create multiple operations objects.
These blocks are dispatched with default priority to an appropriate queue.
NSInvocationOperation: another concrete subclass of NSOperation that allows you
to create an operation from a message call or an invocation.
UIActivityItemProvider: a new subclass of NSOperation introduced in iOS 6. When
using UIActivityViewController you could pass it a UIActivityItemProvider to
provide the item that the activity will be working on. You’re expected to sublclass
this proxy object in order to implement your own item for the activity view
controller.
All operation objects support the following features:
- support graph-based dependencies between operations.
- Completion block
- KVO
- Prioritizing operations
- Cancelling operations
Creating NSInvocationOperation
Creating NSBlockOperation
Object State NSOperation state life cycle would normally be:
isReady -> isExecuting -> isFinished
isReady: return YES to indicate the operation is ready to start executing unless there
is unfinished initialization work that hasn’t been completed yet or a dependant
operation hasn’t finished executing yet (more on dependencies later)
isExecuting: return YES if the operation is currently working on its task other wise
NO.
isFinished: return YES to indicate that the work has been done.
NSOperation class supports KVC & KVO so you could observe the following keys to
control you other parts of your app.
Read-Only properties:
isCancelled, isConcurrent, isExecuting, isFinished, isReady, dependencies
Read-Write properties:
queuePriority, completionBlock
if you choose to provide a custom implementation of any of these properties
remember that you mush implement the Key-Value-Coding and Key-Value-
Observing for these properties.
Cancelation
At some point the user might want to cancel the operation or navigate to some other
view controller which at that points renders the operations unnecessary. To cancel
the operation you could call [myOperation cancel] a built in method provided by the
sdk or you can cancel all the operations on your queue by calling
[myQueue cancelAllOperations] which is also provided by the sdk .
But when the operation starts executing its out of your hands and to enforce the
cancelation of an executing operation you need to check your op’s isCancelled
property and at that point stop the work and any necessary cleaning up after your
code.
Priorities & Dependencies
Another cool feature of NSOperation is dependencies. You should use dependency
when your operation can start only after another operation is finished. For example
lets say you have image download operation and image processing operation. You
want the processing to begin after the image is downloaded. So you can make the
image processing operation dependant on the download operation.
Always set dependencies before adding the operations to the queue or running
them.
This way the imageProcessingOp wont be ready to start until the imageDownloadOp
or any other dependencies before it are finished.
For priority management iOS SDK provides this set of priorities to use:
};
The default priority value is NSOperationQueuePriorityNormal. You can set the priority of an operation in the Queue by calling the setQueuePriority:
method on the op. If you pass a value that isn’t defined the default value will be used
(normal priority).
Priorities help order the operations execution order within the queue by promoting
high priority operations over low priority ones from the currently ready ops. For
example, if you have 2 ops one in Hight priority and the other is Low priority, if the
Low priority op is ready before the High priority op it will be executed first. If you
want to prevent the low priority op to execute after the high priority op you should
make it dependant on the High priority op instead of setting their priority.
Another aspect is the thread priority. Where you can set the thread’s system priority
while the op’s main method is executing. Notice that this doesn’t apply for the
completion block. You can specify a floating-point value to the threadPriority
property before adding it to the queue or executing it manually.
Concurrent Operations
NSOperation object is non-concurrent by default, that means if you run it manually
with out adding it to a NSOperationQueue it will run on the same thread where the
call was made.
If you run the operations from with in a queue, they will run on background threads
synchronously to that thread.
It is possible to make operations run concurrently when ran manually you will have
to implement some code on your own to check if the op is ready to run and check if
its cancelled periodically and spawn the work on a separate thread on your own.
In this case if the op is not ready you will reschedule a timer to check again later in
order to ensure the op actually start executing.
Completion block
In case you want your op to notify listeners or do something at the end that is not
considered part of the main method you could set a completion block to be executed
once the op is finished.
Call the native method on NSOperation object:
- (void)setCompletionBlock:(void (^)(void))block the block you pass should take no arguments and should not return any value.
Completion block is also used to generate final KVO notifications.
The completion block will execute on the same thread that the operation was
running on. So if you want to modify UI make sure to send the call to the main
thread.
NSOperationQueue
NSOperationQueue objects are the concrete objects that manage the NSOperation
objects you add to the queue.
Creating a queue:
Adding operations to the queue is the easiest way to ensure that the operations
start. You do so by adding an op to the queue. [aQueue addOperation:myOperation];
Queues can also be suspended and resumed by your app. Suspending the Queue will
prevent new operations from starting, but operations that are currently running will
continue until they’re done.
Operations are removed from the Queue when they’re finished.
You also can determine the maximum number or concurrent operations you want
your queue to run simultaneously. This helps you better improve your apps
performance, especially since operations could potentially consume a lot of memory
and processing power. So even though your queuing ops on the queue if the ops are
using a lot of CPU and memory your app will suffer from performance issues.
Subclassing NSOperation
When Subclassing NSOperation class you should at least implement the main
method. Its recommended to wrap your code there with try..catch clause to be safe
of exceptions happening while your operation is running in the background.
While your code is executing, you need to check periodically for any cancellation
events in order to stop gracefully.
If your operation must return values to the main thread, it’s a good habit to declare a
delegate protocol in order to pass these values back to the main thread and update
UI if needed.
[(NSObject *)self.delegate
performSelectorOnMainThread:(@selector(delegateMethod:))
withObject:object waitUntilDone:NO]; Note that you will have to cast your operation to NSObject first.
Keep in mind when you change the op’s default properties to execute the
appropriate KVC calls to notify listeners about values being changed. [self willChangeValueForKey:@"isFinished"];
finished = YES;
[self didChangeValueForKey:@"isFinished"];
you should consider using an autorelease pool in the main function to catch any
autoreleased objects that you might have used while executing the op.
Demos
Many demos are provided by Apple showing how to subclass NSOperation and
things you could do with it.
Please checkout the Apple documentation/Demos at:
NSOperation Class Reference
NSOperationQueue Class Reference
Lazy Table Images ( by Apple)
Demo image downloads and filters (by Soheil Moayedi Azarpour)
Conclusion
NSOperations are a powerful tool for you to model your background work. It’s a
little extra overhead than GCD but it comes with the benefits of KVO,
suspend/resume, priorities and dependencies.
I recommend reading Concurrency Programming Guide to learn more details about
NSOperations and NSOperationQueue tweaks and details.