Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ios (Memory Management, Block and GCD)

ios (Memory Management, Block and GCD)

ios - Memory Management, Block and GCD

Oursky Limited

November 19, 2012
Tweet

More Decks by Oursky Limited

Other Decks in Programming

Transcript

  1. “MyClass.h” @interface MyClass : NSObject @property float noDeclaredIvar; @end “MyClass.m”

    @implementation MyClass @synthesize noDeclaredIvar = _noDeclaredIvar; @end “MyClass.h” @interface MyClass : NSObject { float _declaredIvar; } @property float declaredIvar; @end “MyClass.m” @implementation MyClass @synthesize declaredIvar = _declaredIvar; @end Monday, 19 November, 12
  2. “MyClass.h” @interface MyClass : NSObject @property float noDeclaredIvar; @end “MyClass.m”

    @implementation MyClass @synthesize noDeclaredIvar = _noDeclaredIvar; @end “MyClass.h” @interface MyClass : NSObject { float _declaredIvar; } @property float declaredIvar; @end “MyClass.m” @implementation MyClass @synthesize declaredIvar = _declaredIvar; @end legacy runtime Monday, 19 November, 12
  3. “MyClass.h” @interface MyClass : NSObject @property float noDeclaredIvar; @end “MyClass.m”

    @implementation MyClass @synthesize noDeclaredIvar = _noDeclaredIvar; @end “MyClass.h” @interface MyClass : NSObject { float _declaredIvar; } @property float declaredIvar; @end “MyClass.m” @implementation MyClass @synthesize declaredIvar = _declaredIvar; @end legacy runtime modern runtime Monday, 19 November, 12
  4. “MyClass.h” @interface MyClass : NSObject @property float noDeclaredIvar; @end “MyClass.m”

    @implementation MyClass @synthesize noDeclaredIvar = _noDeclaredIvar; @end “MyClass.h” @interface MyClass : NSObject { float _declaredIvar; } @property float declaredIvar; @end “MyClass.m” @implementation MyClass @synthesize declaredIvar = _declaredIvar; @end legacy runtime modern runtime • iPhone applications and 64-bit programs on OS X v10.5 and later use the modern version of the runtime. • Other programs (32-bit programs on OS X desktop) use the legacy version of the runtime. Monday, 19 November, 12
  5. Runtime Difference @synthesize With legacy runtime, you must either provide

    an instance variable with the same name and compatible type of the property or specify another existing instance variable in the @synthesize statement With the modern runtime, if you do not provide an instance variable, the compiler adds one for you Monday, 19 November, 12
  6. At a Glance Manual Retain-Release (MRR) explicitly manage memory by

    keeping track of objects you own using reference counting Automatic Reference Counting (ARC) uses the same reference counting system as MRR inserts the appropriate memory management method calls at compile-time strongly encourage to use ARC for new projects. Monday, 19 November, 12
  7. Memory-Related Problems Freeing or overwriting data that is still in

    use This causes memory corruption, and typically results in your application crashing, or worse, corrupted user data Not freeing data that is no longer in use causes memory leaks Leaks cause your application to use ever- increasing amounts of memory, which in turn may result in poor system performance or your application being terminated Monday, 19 November, 12
  8. Basic Memory Management Rules You own any object you create

    Three important words “alloc”, “new”, “copy”. You can take ownership of an object using retain When you no longer need it, you must relinquish ownership of an object using release You must not relinquish ownership of an object you do not own Monday, 19 November, 12
  9. Retain Counts When you create an object, it has a

    retain count of 1. When you send an object a retain message, its retain count is incremented by 1. When you send an object a release message, its retain count is decremented by 1. If an object’s retain count is reduced to zero, it is deallocated. Monday, 19 November, 12
  10. strong vs weak strong => retain “keep this in the

    heap until I don’t point to it anymore” I won’t point to it anymore if I set my pointer to it to nil. Or if I myself am removed from the heap because no one strongly points to me! weak “keep this as long as someone else points to it strongly” Monday, 19 November, 12
  11. From MRR to ARC Instead of you having to remember

    when to use retain, release, and autorelease, ARC evaluates the lifetime requirements of your objects and automatically inserts appropriate memory management calls for you at compile time retain => strong assign => weak Monday, 19 November, 12
  12. Retain Cycles (Circular Reference) Retaining an object creates a strong

    reference to that object An object cannot be deallocated until all of its strong references are released Retain cycle arise if two objects have a strong reference to each other Monday, 19 November, 12
  13. If the Document object has a strong reference to the

    Page object and the Page object has a strong reference to the Document object, neither object can ever be deallocated The Document’s reference count cannot become zero until the Page object is released, and the Page object won’t be released until the Document object is deallocated Monday, 19 November, 12
  14. Solution The solution to the problem of retain cycles is

    to use weak references. A weak reference is a non-owning relationship where the source object does not retain the object to which it has a reference there must be strong references somewhere if there were only weak references, then the pages and paragraphs might not have any owners and so would be deallocated Monday, 19 November, 12
  15. Cocoa establishes a convention: a “parent” object should maintain strong

    references to its “children,” and the children should have weak references to their parents the document object has a strong reference to (retains) its page objects but the page object has a weak reference to (does not retain) the document object Monday, 19 November, 12
  16. Discussion - Delegate Examples of weak references in Cocoa include,

    but are not restricted to, table data sources, outline view items, notification observers, and miscellaneous targets and delegates when a delegate object is deallocated, you need to remove the delegate link by sending a setDelegate: message with a nil argument to the other object. These messages are normally sent from the object’s dealloc method Monday, 19 November, 12
  17. Discussion - View @interface PrintConfirmationViewController() { UILabel *_canvasPriceLabel; } @end

    @implementation PrintConfirmationViewController - (id)init { self = [super init]; if (self) { _canvasPriceLabel = [[UILabel alloc] init]; ... [self.view addSubview:_canvasPriceLabel]; [_canvasPriceLabel release]; } } @interface PrintConfirmationViewController() { UILabel *_canvasPriceLabel; } @end @implementation PrintConfirmationViewController - (id)init { self = [super init]; if (self) { _canvasPriceLabel = [[UILabel alloc] init]; ... [self.view addSubview:_canvasPriceLabel]; } } - (void)dealloc { [_canvasPriceLabel release]; [super dealloc]; } Version 1 Version 2 Monday, 19 November, 12
  18. Discussion - Singleton #import <foundation/Foundation.h> @interface Singleton : NSObject {

    NSString *someProperty; } @property (nonatomic, retain) NSString *someProperty; + (id)instance; @end #import "Singleton.h" static Singleton *singleton = nil; @implementation Singleton @synthesize someProperty; #pragma mark Singleton Methods + (id)instance { @synchronized(self) { if (singleton == nil) singleton = [[self alloc] init]; } return singleton; } - (id)init { if (self = [super init]) { someProperty = [[NSString alloc] initWithString:@"Default Property Value"]; } return self; } @end Singleton.h Singleton.m Monday, 19 November, 12
  19. Discussion - Singleton (MRR) + (id)allocWithZone:(NSZone *)zone { return [[self

    sharedManager] retain]; } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)retain { return self; } - (unsigned)retainCount { return UINT_MAX; / /denotes an object that cannot be released } - (oneway void)release { / / never release } - (id)autorelease { return self; } - (void)dealloc { / / Should never be called [someProperty release]; [super dealloc]; } @end #import "Singleton.h" static Singleton *singleton = nil; @implementation Singleton @synthesize someProperty; #pragma mark Singleton Methods + (id)instance { @synchronized(self) { if (singleton == nil) singleton = [[self alloc] init]; } return singleton; } - (id)init { if (self = [super init]) { someProperty = [[NSString alloc] initWithString:@"Default Property Value"]; } return self; } Monday, 19 November, 12
  20. Blocks What is a block? a sequence of statements inside

    {} What does it look like? Here’s an example of calling a method that takes a block as an argument. [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSLog(@“value for key %@ is %@”, key, value); if ([@“ENOUGH” isEqualToString:key]) { *stop = YES; } }]; This NSLog()s every key and value in aDictionary (but stops if the key is ENOUGH). Monday, 19 November, 12
  21. If you declare a block as a variable, you can

    then use it just as you would a function: int multiplier = 7; int (^myBlock)(int) = ^(int num) { return num * multiplier; }; printf("%d", myBlock(3)); / / prints "21" Monday, 19 November, 12
  22. Why Use Blocks? An advantage of blocks as function and

    method parameters is that they enable the caller to provide the callback code at the point of invocation Because this code does not have to be implemented in a separate method or function, your implementation code can be simpler and easier to understand Monday, 19 November, 12
  23. Example - NSNotification - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter]

    addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; } - (void)keyboardWillShow:(NSNotification *)notification { // Notification-handling code goes here. } Monday, 19 November, 12
  24. Example - NSNotification - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter]

    addObserverForName:UIKeyboardWillShowNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { // Notification-handling code goes here. }]; } Monday, 19 November, 12
  25. Blocks Can use local variables declared before the block inside

    the block (closure) double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSLog(@“value for key %@ is %@”, key, value); if ([@“ENOUGH” isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; } }]; Monday, 19 November, 12
  26. Block But they are read only! BOOL stoppedEarly = NO;

    double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSLog(@“value for key %@ is %@”, key, value); if ([@“ENOUGH” isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; stoppedEarly = YES; // ILLEGAL } }]; Monday, 19 November, 12
  27. Block But they are read only! __block BOOL stoppedEarly =

    NO; double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSLog(@“value for key %@ is %@”, key, value); if ([@“ENOUGH” isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; stoppedEarly = YES; // OK Now } }]; Monday, 19 November, 12
  28. Block So what about objects which are messaged inside the

    block? NSString *stopKey = [@“Enough” uppercaseString]; double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSLog(@“value for key %@ is %@”, key, value); if ([stopKey isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; } }]; stopKey will essentially have a strong pointer to it until the block goes out of scope or the block itself leaves the heap (i.e. no one points strongly to the block anymore). Monday, 19 November, 12
  29. Block Creating a “type” for a variable that can hold

    a block Blocks are kind of like “objects” with an unusual syntax for declaring variables that hold them. Usually if we are going to store a block in a variable, we typedef a type for that variable, e.g., typedef double (^unary_operation_t)(double op); This declares a type called “unary_operation_t” for variables which can store a block. Specifically, a block which takes a double as its only argument and returns a double Monday, 19 November, 12
  30. Then we could declare a variable, square, of this type

    and give it a value ... unary_operation_t square; square = ^(double operand) { return operand * operand; } And then use the variable square like this ... double squareOfFive = square(5.0); We could then use the unary_operation_t to define a method typedef double (^unary_operation_t)(double op); - (void)addUnaryOperation:(NSString *)op whichExecutesBlock:(unary_operation_t)opBlock { [self.unaryOperations setObject:opBlock forKey:op]; } Monday, 19 November, 12
  31. don’t always typedef - (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL

    *stop))block; Same as typedef void (^enumeratingBlock)(id key, id obj, BOOL *stop); - (void)enumerateKeysAndObjectsUsingBlock: (enumeratingBlock)block; No Argument Block [UIView animateWithDuration:1.0 animations:^{ view.opacity = 1; }]; Monday, 19 November, 12
  32. Block - Memory Cycles What if you had the following

    property in a class? @property (nonatomic, strong) NSArray *myBlocks; / / array of blocks And then tried to do the following in one of that class’s methods? [self.myBlocks addObject:^() { [self doSomething]; }]; We said that all objects referenced inside a block will stay in the heap as long as the block does. (in other words, blocks keep a strong pointer to all objects referenced inside of them) In this case, self is an object reference in this block. Thus the block will have a strong pointer to self. But notice that self also has a strong pointer to the block (through its myBlocks property)! (Circular reference) Monday, 19 November, 12
  33. Block - Memory Cycles Local variables are always strong. There’s

    a way to declare that a local variable is weak: __weak MyClass *weakSelf = self; [self.myBlocks addObject:^() { [weakSelf doSomething]; }]; This solves the problem because now the block only has a weak pointer to self. (self still has a strong pointer to the block, but that’s okay) As long as someone in the universe has a strong pointer to this self, the block’s pointer is good. And since the block will not exist if self does not exist (since myBlocks won’t exist) Monday, 19 November, 12
  34. When use Block? Enumeration View Animations Sorting (sort this thing

    using a block as the comparison method) Notification (when something happens, execute this block) Error handlers (if an error happens while doing this, execute this block) Completion handlers (when you are done doing this, execute this block) Multithreading With Grand Central Dispatch (GCD) API Monday, 19 November, 12
  35. Grand Central Dispatch GCD is a C API The basic

    idea is that you have queues of operations The operations are specified using blocks. The system runs operations from queues in separate threads The good thing is that if your operation blocks, only that queue will block Monday, 19 November, 12
  36. Important functions Creating and releasing queues dispatch_queue_t dispatch_queue_create(const char *label,

    NULL); / / serial queue void dispatch_release(dispatch_queue_t); Putting blocks in the queue typedef void (^dispatch_block_t)(void); void dispatch_async(dispatch_queue_t queue, dispatch_block_t block); Monday, 19 November, 12
  37. Important functions Getting the current or main queue dispatch_queue_t dispatch_get_current_queue();

    void dispatch_queue_retain(dispatch_queue_t); / / keep it in the heap until dispatch_release dispatch_queue_t dispatch_get_main_queue(); Monday, 19 November, 12
  38. Example Assume we fetched an image from the network -

    (void)viewWillAppear:(BOOL)animated { NSData *imageData = [NSData dataWithContentsOfURL:networkURL]; UIImage *image = [UIImage imageWithData:imageData]; self.imageView.image = image; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); self.scrollView.contentSize = image.size; } Monday, 19 November, 12
  39. Example Assume we fetched an image from the network -

    (void)viewWillAppear:(BOOL)animated { dispatch_queue_t downloadQueue = dispatch_queue_create(“image downloader”, NULL); dispatch_async(downloadQueue, ^{ NSData *imageData = [NSData dataWithContentsOfURL:networkURL]; UIImage *image = [UIImage imageWithData:imageData]; self.imageView.image = image; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); self.scrollView.contentSize = image.size; }); } Slow network blocks the main thread Monday, 19 November, 12
  40. Example Assume we fetched an image from the network -

    (void)viewWillAppear:(BOOL)animated { dispatch_queue_t downloadQueue = dispatch_queue_create(“image downloader”, NULL); dispatch_async(downloadQueue, ^{ NSData *imageData = [NSData dataWithContentsOfURL:networkURL]; dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithData:imageData]; self.imageView.image = image; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); self.scrollView.contentSize = image.size; }); }); } UIKit calls can only happen in the main thread Monday, 19 November, 12
  41. Example Assume we fetched an image from the network -

    (void)viewWillAppear:(BOOL)animated { dispatch_queue_t downloadQueue = dispatch_queue_create(“image downloader”, NULL); dispatch_async(downloadQueue, ^{ NSData *imageData = [NSData dataWithContentsOfURL:networkURL]; dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithData:imageData]; self.imageView.image = image; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); self.scrollView.contentSize = image.size; }); }); dispatch_release(downloadQueue); } This “leaks” the downloadQueue in the heap it won’t remove the queue from the heap until all blocks have been processed Monday, 19 November, 12
  42. Singleton Revisit #import <foundation/Foundation.h> @interface Singleton : NSObject { NSString

    *someProperty; } @property (nonatomic, retain) NSString *someProperty; + (id)instance; @end #import "Singleton.h" static Singleton *singleton = nil; @implementation Singleton @synthesize someProperty; #pragma mark Singleton Methods + (id) instance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ singleton = [[self alloc] init]; }); return singleton; } - (id)init { if (self = [super init]) { someProperty = [[NSString alloc] initWithString:@"Default Property Value"]; } return self; } @end Singleton.h Singleton.m Monday, 19 November, 12