itp 342 mobile app dev - university of southern...
TRANSCRIPT
ITP 342 Mobile App Dev
Grand Central Dispatch
Background Processing
Grand Central Dispatch (GCD) • New API for splitting up the work your app
needs to do into smaller chunks that can be spread across multiple thread and, with the right hardware, multiple CPUs
• Much of the new API is accessed using blocks – In-line function capability to C & Objective-C
2
Threading Basics • Most modern operating systems support the notion of
threads of execution • Each process can contain multiple threads which all
run concurrently – If there's just one processor core, the operating system
will switch between all executing threads – If more than one core is available, the threads will be
distributed among them • All threads in a process share the same executable
program code and the same global data • Each thread can also have some data that is
exclusive to the thread
3
Threading Basics • Mutex (mutual exclusion) or lock
– Special structure that threads can make use of – Ensures that a particular chunk of code can't be
run by multiple threads at once – Use in a critical section of your code – This makes them thread-safe
4
Thread-Safe • In Cocoa Touch, the Foundation framework
(NSString, NSArray, etc.) is thread-safe – The UIKit framework is for the most part not thread-
safe • Main thread is where all the action of your iOS
app occurs – All method calls that deal with any UIKit object should
be executed in the main thread • https://developer.apple.com/library/ios/
documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html
5
Units of Work • It is possible to implement some concurrency
without too much low-level thread-twisting • Split up long-running tasks into units of work
and put those units into queues for execution • The system manages the queues for us,
executing units of work on multiple threads
6
GCD: Low-Level Queueing • GCD made its debut in Mac OS X 10.6 • Available since iOS 4.0 • Works with Objective-C, C, and C++ • Key concepts is the queue • The system provides a number of predefined
queues including one that does work on the main thread – You can also create your own queues – FIFO queues = first-in, first-out
7
Queueing • Units of work added to a GCD queue will always
be started in the order they were placed in the queue – They may not always finish in the same order due to
distributing its work among multiple threads • Each queue has access to a pool of threads that
are reused throughout the lifetime of the app • GCD will try to maintain a pool of threads
dependent on the machine's architecture • Dual-core processors starting with iPad2 and
iPhone 4S – Earlier devices are single-core
8
Blocks • Blocks: known as closures or lambdas in other
languages • Let a particular chunk of code be treated like any
other C-language type • A block can be assigned to a variable, passed as an
argument to a function or method, and executed • A block can take one or more parameters and specify
a return value – To declare a block variable, use the caret (^) symbol
along with some additional parenthesized bits to declare parameters and return types
– To define the block itself, you do roughly the same, but follow it up with the actual code defining the block wrapped in curly braces
9
Blocks - Example
10
// Declare a block variable "loggerBlock" with no parameters !// and no return value. !void (^loggerBlock)(void); !!// Assign a block to the variable declared above. !// A block without parameters and with no return value needs no !// "decorations" like the use of void in the variable declaration. !loggerBlock = ^{ ! NSLog(@"I'm just glad they didn't call it a lambda"); !}; !!// Execute the block, just like calling a function. !loggerBlock(); // this produces some output in the console !
Defining Blocks • Similar to the concept of a function pointer in C,
but a few critical differences • Blocks can be defined in-line in your code
– Right at the point where it's going to be passed to another method or function
• A block can access variables available in the scope where it's created – By default, the block makes a copy of any variable
you access this way – You can make an outside variable "read/write" by
prepending the storage qualifier __block before its declaration (that's 2 underscores)
11
Blocks - Example
12
// define a variable that cannot be changed by a block !int a = 0; !!// define a block that tries to modify a variable in its scope !void (^sillyBlock)(void) = ^{ a = 47; }; !!// check the value of our variable before calling the block !NSLog(@"a == %d", a); // outputs "a == 0" !!// execute the block !sillyBlock(); !!// check the values of our variable again, after calling the block !NSLog(@"a == %d", a); // outputs "a == 0" !
Blocks - Example
13
// define a variable that can be changed by a block !__block int a = 0; !!// define a block that tries to modify a variable in its scope !void (^sillyBlock)(void) = ^{ a = 47; }; !!// check the value of our variable before calling the block !NSLog(@"a == %d", a); // outputs "a == 0" !!// execute the block !sillyBlock(); !!// check the values of our variable again, after calling the block !NSLog(@"a == %d", a); // outputs "a == 47" !
dispatch_async • Make code run entirely in the background by
wrapping all the code in a block and passing it to a GCD function called dispatch_async!– First parameter is a GCD queue
• Using dispatch_get_global_queue() function – DISPATCH_QUEUE_PRIORITY_HIGH !– DISPATCH_QUEUE_PRIORITY_LOW !
– Second parameter is a block to assign to the queue
14
dispatch_async (dispatch_get_global_queue ! (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ ! // code !}); !
The Main Thread • One problem is UIKit thread-safety
– Can't message any GUI object from a background thread
• Solution is to pass the work to the main thread • Call dispatch_async() with the first
parameter of dispatch_get_main_queue() !
15
dispatch_async (dispatch_get_main_queue(), ^{ ! // code !}); !
Concurrent Blocks • Let's do things concurrently • Use a dispatch group • All blocks that are dispatched asynchronously
within the context of a group, via the dispatch_group_async() function, are set loose to execute as fast as they can – Including being distributed to multiple threads for
concurrent execution, if possible • Use dispatch_group_notify() to specify
an additional block that will be executed when all the blocks in the group have been run to completion
16
Worker • Create a single-view app • To the storyboard, add
– Button (round rect button) – Text View – Activity Indicator View
• In the Attributes Inspector, make sure Hides When Stopped is checked
• Create IBOutlets – Button à startButton – TextView à resultsTextView
• Create IBAction – Button à doWork
17
Worker • Add methods in implementation
18
- (NSString *) fetchSomethingFromServer { ! [NSThread sleepForTimeInterval:1]; ! return @"Fetched from Server"; !} !!- (NSString *) processData: (NSString *)data { ! [NSThread sleepForTimeInterval:2]; ! return [data uppercaseString]; !} !!- (NSString *) calculateFirstResult: (NSString *)data { ! [NSThread sleepForTimeInterval:3]; ! return [NSString stringWithFormat:@"Number of chars: %d", [data length]]; !} !!- (NSString *) calculateSecondResult: (NSString *)data { ! [NSThread sleepForTimeInterval:4]; ! return [data stringByReplacingOccurrencesOfString:@"E" withString:@"e"]; !} !
Slow Worker • Implement doWork: method
19
- (IBAction) doWork: (id)sender { ! NSDate *startTime = [NSDate date]; ! ! NSString *fetchedData = [self fetchSomethingFromServer]; ! NSString *processedData = [self processData:fetchedData]; ! ! NSString *firstResult = [self calculateFirstResult:processedData]; ! NSString *secondResult = [self calculateSecondResult:processedData]; ! ! NSString *resultsSummary = [NSString stringWithFormat: ! @"First: [%@]\nSecond: [%@]", firstResult, secondResult]; !! self.resultsTextView.text = resultsSummary; ! ! NSDate *endTime = [NSDate date]; ! NSLog(@"Completed in %f seconds", ! [endTime timeIntervalSinceDate:startTime]); !} !
Faster Worker
20
- (IBAction)doWork:(id)sender { ! NSDate *startTime = [NSDate date]; !! // Disable the startButton! self.startButton.enabled = NO; ! self.startButton.alpha = 0.5f; ! ! // Start animating the spinner ! [self.spinner startAnimating]; ! ! // Grand Central Dispatch ! dispatch_queue_t queue = ! dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); ! dispatch_async(queue, ^{ !! NSString *fetchedData = [self fetchSomethingFromServer]; !! NSString *processedData = [self processData:fetchedData]; !!
Faster Worker
21
__block NSString *firstResult; ! __block NSString *secondResult; ! dispatch_group_t group = dispatch_group_create(); ! dispatch_group_async(group, queue, ^{ ! firstResult = [self calculateFirstResult:processedData]; ! }); ! dispatch_group_async(group, queue, ^{ ! secondResult = [self calculateSecondResult:processedData]; ! }); ! dispatch_group_notify(group, queue, ^{ ! NSString *resultsSummary = [NSString stringWithFormat: ! @"First: [%@]\nSecond: [%@]", firstResult, secondResult]; ! ! // Have to be on the main thread for UIKit objects ! dispatch_async(dispatch_get_main_queue(), ^{ ! self.resultsTextView.text = resultsSummary; ! self.startButton.enabled = YES; ! self.startButton.alpha = 1; ! [self.spinner stopAnimating]; ! }); ! NSDate *endTime = [NSDate date]; ! NSLog(@"Completed in %f seconds", ! [endTime timeIntervalSinceDate:startTime]); ! }); ! }); !} !
Background Processing • Allows your apps to run in the background
– Allows apps that require specific kinds of system functionality to continue to run in a constrained manner
• Since iOS 4, iPhone 3GS & on, all iPads • For example, an app that plays an audio stream
from an Internet radio station even if the user switches to another app
22
Application Life Cycle • Not Running
– This is the state that all apps are in on a freshly rebooted device
– An app that has launched will return to this state only: • In Info.plist, UIApplicatonExitsOnSuspend is set to YES • It was previously Suspended and the system needs to
clear out some memory • It crashes while running
23
Application Life Cycle • Active
– The normal running state of an app when it's displayed on the screen
– Can receive user input & update the display • Background
– An app is given some time to execute some code but can't directly access the screen or get any user input
– All apps enter this state when the user presses the home button • Most of them quickly move on to Suspended • Apps that want to run in the background stay here until
they're made Active again
24
Application Life Cycle • Suspended
– A Suspended app is frozen – All the memory that app was
using while it was active is held just as it was • If the user brings the app back to the Active state, it will
pick up right where it left off • If the system needs more memory for an Active app,
any Suspended apps may be terminated (Not Running state) and their memory freed
25
Application Life Cycle • Inactive
– Temporary rest stop between 2 states – The only ways an app can stay
Inactive for any length of time 1. If the user is dealing with a system
prompt such as those shown for an incoming call or SMS message
2. If the user has locked the screen
– This state is basically a sort of limbo
26
State-Change Notifications • To manage changes between these states,
UIApplication defines a number of methods that its delegate can implement
• UIApplication also defines a matching set of notification names – This allows other objects besides the app
delegate to register for notifications when the app's state changes
27
Delegate Methods • Delegate methods for tracking your
application's execution state and their corresponding notification names
28
Delegate Method" Notification Name"application:
didFinishLaunchingWithOptions: UIApplicationDidFinishLaunchingNotification"
applicationWillResignActive: UIApplicationWillResignActiveNotification"
applicationDidBecomeActive: UIApplicationDidBecomeActiveNotification"
applicationDidEnterBackground: UIApplicationDidEnterBackgroundNotification"
applicationWillEnterForeground: UIApplicationWillEnterForegroundNofication"
applicationWillTerminate: UIApplicationWillTerminateNotification"
Executing State Changes • Active à Inactive
– Use applicationWillResignActive: and/or UIApplicationWillResignActiveNotification
– "Pause" your app's display • Inactive à Background
– Use applicationDidEnterBackground:/UIApplicationDidEnterBackgroundNotification
– Release any resources – Have about 5 seconds
• Background à Inactive – Use applicationWillEnterForeground:/
UIApplicationWillEnterForeground • Inactive à Active
– Use applicationDidBecomeActive:/UIApplicationDidBecomeActive
29
Project – State Lab • Create a single view app project called
StateLab which will log messages to the console
• A selector is simply Objective-C's way of referring to a method
• Objective-C provides a handy built-in variable called _cmd that always contains the selector of the current method
• The NSStringFromSelector function returns an NSString representation of a given selector
• Shortcut for outputting the current method name
30
NSLog(@"%@", NSStringFromSelector(_cmd)); !