Example of ARC, Blocks, and GCD
Total Page:16
File Type:pdf, Size:1020Kb
Appendix A Example of ARC, Blocks, and GCD Let’s see an example using ARC, Blocks, and GCD. The following source code reads data from a URL and displays the result on the main thread. That is a very typical use case in iOS applications so you can paste this as a part of your application. This shows you how ARC, Block, and GCD can be used for an application such as a twitter or Tumblr client. The source code is explained with its comments. NSString *url = @"http://images.apple.com/" "jp/iphone/features/includes/camera-gallery/03-20100607.jpg" /* * On the main thread, downloading data from the specified URL starts asynchronously. */ [ASyncURLConnection request:url completeBlock:^(NSData *data) { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ /* * On a global dispatch queue, processing the downloaded data * It doesn't prevent the main thread, which means this task can take long. */ dispatch_async(dispatch_get_main_queue(), ^{ /* * Here, it uses the result on the main dispatch queue. * Displays the result on the user interface. */ }); }); } errorBlock:^(NSError *error) { 181 182 APPENDIX A: Example of ARC, Blocks, and GCD /* * Error occurred */ NSLog(@"error %@", error); }]; Processing the downloaded data is executed on the other thread to avoid preventing the main thread. The dispatch_get_global_queue function obtains a global dispatch queue with default priority, and the dispatch_async function processes the data on the global dispatch queue. When the processing is finished, the user interface has to be updated on the main thread to show the result. This is done by the dispatch_get_main_queue function to obtain the main dispatch queue and by the dispatch_async function to execute the task. By the way, should the downloading itself be implemented with GCD to run on the other threads to avoid preventing the main thread? The answer is no. For network programming, an asynchronous API is strongly recommended. Please see the following WWDC 2010 sessions. WWDC 2010 Session 207—Network Apps for iPhone OS, Part 1 WWDC 2010 Session 208—Network Apps for iPhone OS, Part 2 These are the sessions about network programming. It definitely says “Threads Are Evil™” for network programming. When threads are used for network programming, it tends to use too many threads. For example, if a thread is created for each connection, stacks for each thread will consume too much memory instantly. There are asynchronous network APIs in the Cocoa Framework. Please make sure to use an asynchronous network API. You must not use threads for networks. An ASyncURLConnection class in the source code is a class for network programming and has a base class NSURLConnection in the Foundation Framework, which is to load data asynchronously via a network. Let’s see how the class is implemented. ASyncURLConnection.h #import <Foundation/Foundation.h> /* * By using typedef for a Block type variables, * Source code will have better readability. */ typedef void (^completeBlock_t)(NSData *data); typedef void (^errorBlock_t)(NSError *error); @interface ASyncURLConnection : NSURLConnection { /* * Because ARC is enabled, all the variables below are * qualified with __strong when it doesn't have an explicit qualifier. */ APPENDIX A: Example of ARC, Blocks, and GCD 183 NSMutableData *data_; completeBlock_t completeBlock_; errorBlock_t errorBlock_; } /* * To give the source code better readability, * The typedefined Block type variable is used for the argument. */ + (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock; - (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock; @end ASyncURLConnection.m #import "ASyncURLConnection.h" @implementation ASyncURLConnection + (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock { /* * If ARC is disabled, * This method should return an object after autorelease is called: * * id obj = [[[self alloc] initWithRequest:requestUrl * completeBlock:completeBlock errorBlock:errorBlock]; * return [obj autorelease]; * * Because this method name doesn't begin with alloc/new/copy/mutableCopy, * the returned object has been automatically registered in autoreleasepool. */ return [[self alloc] initWithRequest:requestUrl completeBlock:completeBlock errorBlock:errorBlock]; } - (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock { NSURL *url = [NSURL URLWithString:requestUrl]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; if ((self = [super initWithRequest:request delegate:self startImmediately:NO])) { data_ = [[NSMutableData alloc] init]; /* * To make sure that you can use the passed Block safely, * the instance method 'copy' is called to put the Block on the heap. 184 APPENDIX A: Example of ARC, Blocks, and GCD */ completeBlock_ = [completeBlock copy]; errorBlock_ = [errorBlock copy]; [self start]; } /* * Member variables that have a __strong qualifier * have ownership of the created NSMutableData class object * * When the object is discarded, the strong references * of the member variables with the __strong qualifier disappear. * The NSMutableData class object and the Block will be released automatically. * * So, you don't need to implement the dealloc instance method explicitly. */ return self; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { [data_ setLength:0]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [data_ appendData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { /* * Execute the Block assigned as callback for downloading success. * The legacy delegate callback can be replaced by Block. */ completeBlock_(data_); } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { /* * Execute the Block that is assigned for error. */ errorBlock_(error); } @end When the downloading is finished or an error occurs, the NSURLConnection class calls a method on an object that is assigned to its delegate. The ASyncURLConnection class APPENDIX A: Example of ARC, Blocks, and GCD 185 inherits the NSURLConnection class and Blocks can be set as callback for download finished and an error occurred. With this class, an application source code can be simpler. ARC manages the object of the NSMutableData class that is to store the downloaded data and the Blocks for callbacks properly. Because the member variables are qualified with __strong, you don’t need to call the retain or release method explicitly, Actually, you can’t call them in an ARC-enabled environment. And, you don’t need to implement the dealloc instance method. You must realize that ARC, Blocks, and Grand Central Dispatch are powerful even in such a simple source code. Appendix B References This appendix provides a list of references that you should find helpful. They are listed in three categories for ARC, Blocks, and Grand Central Dispatch. References for ARC Transitioning to ARC Release Notes http://developer.apple.com/library/mac/#releasenotes/ObjectiveC /RN-TransitioningToARC/_index.html ARC Programming Guide by Apple When you want to know something about ARC, you should read this document first. If you’re reading this book before it, it is ok :-), but you should read it later. LLVM Document—Automatic Reference Counting http://clang.llvm.org/docs/AutomaticReferenceCounting.html This document is something like the specification of ARC. When you want to make sure of its specification, please check this out. Advanced Memory Management Programming Guide http://developer.apple.com/library/ios/documentation/Cocoa/ Conceptual/MemoryMgmt/Articles/MemoryMgmt.html This is a document by Apple that explains memory management with reference counting in detail. 187 188 APPENDIX B: References Getting Started: Building and Running Clang http://clang.llvm.org/get_started.html Subversion repository http://llvm.org/svn/llvm-project/cfe/trunk This explains how to get the source code of clang (LLVM compiler 3.0), which is the compiler supporting ARC. The repository of the source code is there, too. For example, the following source codes in the repository have the implementation to achieve ARC. llvm/tools/clang/lib/CodeGen/CGObjC.cpp llvm/tools/clang/lib/CodeGen/CGObjCMac.cpp objc4 version 493.9 http://www.opensource.apple.com/source/objc4/objc4-493.9/ This is an implementation of the Objective-C runtime library by Apple. The source code is available. The Objective-C runtime APIs that are explained in the above document “LLVM Document—Automatic Reference Counting” are implemented in this library. ARC-related APIs are mostly in the following file. runtime/objc-arr.mm APIs that are related to the __weak qualifier are in: runtime/objc-weak.mm GNUstep libobjc2 version 1.5 http://gnustep.blogspot.com/2011/07/gnustep-objective-c- runtime-15-released.html http://thread.gmane.org/gmane.comp.lib.gnustep.general/36358 http://svn.gna.org/viewcvs/gnustep/libs/libobjc2/1.5/ libobjc2 is an implementation of the Objective-C runtime library supporting ARC by the GNUstep project. It is equivalent to objc4, that is, the Objective-C runtime library by Apple. References for Blocks APPLE’S EXTENSIONS TO C http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1370.pdf APPENDIX B: References 189 This is an overview document about C language extensions