Intermediate Objective-C Workshop

Intermediate Objective-C Workshop

9b54e5324785eb939bcc8f15c724baf9?s=128

Curtis Herbert

August 23, 2014
Tweet

Transcript

  1. INTERMEDIATE OBJECTIVE-C CocoaShops Curtis Herbert Carl Leiby Kotaro Fujita Mike

    Zornek @parrots @carlism @wild37 @zorn
  2. THE BASICS

  3. HISTORY OF OBJ-C • 1983 - C + Smalltalk come

    together as a C pre-compiler • 1988 - NeXT licensed the language • 1996 - Apple buys NeXT as foundation for OS X (“Rhapsody”) • 2006 - Objective-C 2.0 • 2008 - iPhone SDK
  4. THE IN THE ROOM Swift is the future, right?

  5. THE BASICS CORE LANGUAGE FEATURES

  6. C PRIMITIVES FLOAT DOUBLE VOID CHAR BOOL INT

  7. int myAge = 6; myAge = myAge + 6; int

    alsoMyAge = myAge; C PRIMITIVES
  8. WHERE THINGS GET FUN: OBJ-C IS A DYNAMIC RUNTIME A

    quick detour to the deep end
  9. [tableView reloadData]; Send the message “reloadData” to the object “tableView”

    objc_msgsend(tableview, @selector(reloadData)); MESSAGE SENDING
  10. “Objective-C decides which method implementation to call right before doing

    so (during runtime). The idea is that the connection between the name of a method and the implementation is dynamic. C++ for example does this during compile time.”
  11. AN EXAMPLE OF THIS VOODOO UIView *myNewView = [[UIView alloc]

    init]; object_setClass(myNewView, [MyOtherClass class]); (this isn’t a common thing to do, don’t worry)
  12. -[NSArray objectForKey:]: unrecognized selector sent to instance 0x102301c40 
 ***

    Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSArray objectForKey:]: unrecognized selector sent to instance 0x102301c40'
  13. CLASS OBJ-C PRIMITIVES SEL ID NIL

  14. OBJ-C PRIMITIVES SEL CLASS ID id name = @“Curtis”; id

    date = [NSDate new]; NIL
  15. OBJ-C PRIMITIVES SEL CLASS ID - (id) someMethod; NIL

  16. OBJ-C PRIMITIVES SEL CLASS ID SEL mySelector = @selector(update); [self

    performSelector:mySelector]; NIL
  17. OBJ-C PRIMITIVES SEL CLASS ID SEL mySelector = @selector(update); if

    ([self respondsToSelector:mySelector]) { } NIL
  18. OBJ-C PRIMITIVES SEL CLASS ID SEL mySelector = @selector(updateName:); [self

    performSelector:mySelector withObject:@“Joe”]; NIL
  19. OBJ-C PRIMITIVES SEL CLASS ID SEL mySelector = NSSelectorFromString(@“myMethod”); [self

    performSelector:mySelector]; NIL
  20. OBJ-C PRIMITIVES SEL CLASS ID Class targetClass = [NSDate class];

    if ([myObject isKindOfClass:targetClass]) … NIL
  21. OBJ-C PRIMITIVES SEL CLASS ID myObject = nil; NSString *name

    = [myObject showName]; NIL
  22. OBJ-C DIRECTIVES • Instructions to the compiler • @IBOutlet, @property,

    @class, etc • @“string”, @[@“string1”], @{@“key” : @“value”}
  23. HANDS-ON #1

  24. CORE LANGUAGE FEATURES Deep breaths - back to more familiar

    territory
  25. OBJ-C CLASSES UIVIEWCONTROLLER NSSTRING NSNUMBER UITABLEVIEW NSARRAY UIVIEW NSDATE NSDICTIONARY

    NSURL UILABEL UIIMAGEVIEW NSFORMATTER CLLOCTIONMANAGER UISWITCH UICOLLECTIONVIEW
  26. STANDARD OO STUFF NSObject UIView : UIResponder UIResponder : NSObject

    Inherits from subclass Inherits from subclass
  27. UIView : UIResponder <NSCoding, UIAppearance> Conforms to protocols STANDARD OO

    STUFF
  28. COMPONENTS OF A CLASSES HEADER Properties, method signatures Exposed to

    the outside world IMPLEMENTATION The executable code
  29. @import Foundation; @class PCHActivity; @interface PCHActivitySharingProvider : NSObject <NSCoder> -

    (void)shareActivity:(PCHActivity *)activity; @end
  30. #import “PCHActivitySharingProvider.h” #import <AFNetworking/AFNetworking.h> @implementation PCHActivitySharingProvider - (void)shareActivity:(PCHActivity *)activity {

    //pre-process the activity, do weird stuff [self showShareUIWithActivity:activity]; //do something else } - (void)showShareUIWithActivity:(PCHActivity *)activity { //do awesome stuff with activity } @end
  31. - (void)share; - (void)shareActivity:(PCHActivity *)activity; - (void)shareActivity:(PCHActivity *)activity withName:(NSString *)name

    forUser:(PCHUser *)user; + (instancetype)sharedInstance; METHODS
  32. share shareActivity: shareActivity:withName:forUser: sharedInstance METHODS

  33. @implementation PCHActivitySharingProvider PCHActivity *_activity; - (void)shareActivity:(PCHActivity *)activity { _activity =

    activity; [self showShareUIWithActivity:activity]; } ... INTERNAL VARIABLES (IVARS)
  34. @implementation PCHActivitySharingProvider PCHActivity *_activity; - (void)setActivity:(PCHActivity *)activity { if (activity

    != _activity) { _activity = activity; } } - (PCHActivity *)activity { return _activity; } ...
  35. @import Foundation; @class CBCActivity; @interface CBCActivitySharingProvider : NSObject <NSCoder> @property

    CBCActivity *activity; - (void)shareActivity:(CBCActivity *)activity; @end PROPERTIES
  36. myObject.activity myObject.activity = blah; self.activity = blah; return self.activity; “DOT

    SYNTAX” VS “OLD SCHOOL” [myObject activity]; [myObject setActivity:blah]; [self setActivity:blah]; return [self activity];
  37. CONVENTIONS - ACCESSORS setFont: and font not setFont: and getFont

  38. CONVENTIONS - CLARITY Code Commentary insertObject:atIndex: Good. insert:at: Not clear;

    what is being inserted? what does “at” signify? removeObjectAtIndex: Good. removeObject: Good, because it removes object referred to in argument. remove: Not clear; what is being removed? https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/ NamingBasics.html#//apple_ref/doc/uid/20001281-BBCHBFAH
  39. CONVENTIONS - DESCRIPTIVE Code Commentary destinationSelection Good. destSel Not clear.

    setBackgroundColor: Good. setBkgdColor: Not clear. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/ Articles/NamingBasics.html#//apple_ref/doc/uid/20001281-BBCHBFAH
  40. CONVENTIONS - PREFIXING • Class Prefix — UIColor, NSString, CLLocation,

    etc • Apple uses 2-letter prefix • Prefix your classes with your initials Prefix Cocoa Framework NS Foundation NS Application Kit AB Address Book IB Interface Builder UI UIKit
  41. HANDS-ON #2

  42. POINTERS Not the tips and tricks kind

  43. - (void)myMethod { int a = 10; } C primitives

    only exist in memory as long as they’re in scope. The memory for a is gone after the method returns.
  44. int a = 10; int b = a; b =

    20; C primitives are passed by making copies. 10 ➡️
  45. int a = 10; int b = a; b =

    20; C primitives are passed by making copies. 10 10 ➡️
  46. int a = 10; int b = a; b =

    20; C primitives are passed by making copies. 10 20 ➡️
  47. int a = 10; [self myAdder:a]; a = 20; C

    primitives are passed by making copies. 10 - (void)myAddder:(int)c { adder += 40; } ➡️
  48. int a = 10; [self myAdder:a]; a = 20; C

    primitives are passed by making copies. 10 10 - (void)myAddder:(int)c { adder += 40; } ➡️
  49. int a = 10; [self myAdder:a]; a = 20; C

    primitives are passed by making copies. 10 50 - (void)myAddder:(int)c { adder += 40; } ➡️
  50. int a = 10; [self myAdder:a]; a = 20; C

    primitives are passed by making copies. 20 - (void)myAddder:(int)c { adder += 40; } ➡️
  51. Objects don’t work this way in Objective-C. They use pointers.

  52. NSNumber *myNumber = [[NSNumber alloc] init]; NSNumber A pointer is

    a variable whose value is the address of another variable.
  53. NSNumber *myNumber = [[NSNumber alloc] init]; A pointer is a

    variable whose value is the address of another variable. NSNumber *
  54. NSNumber *myNumber = [[NSNumber alloc] init]; NSNumber *5 A pointer

    is a variable whose value is the address of another variable.
  55. *0 NSNumber *myNumber; The nil value means “this instance reference

    isn’t pointing to any instance.”
  56. *0 NSNumber *myNumber = nil; if (myNumber) { //do something

    if not nil } nil is equivalent to 0
  57. *5 *12 NSNumber NSNumber NSNumber *myNumber = [[NSNumber alloc] init];

    NSNumber *myNumber2 = [[NSNumber alloc] init];
  58. NSNumber *myNumber = [[NSNumber alloc] init]; NSNumber *myNumber2 = [[NSNumber

    alloc] init]; myNumber = myNumber2; *5 *5 NSNumber NSNumber
  59. Objects in Objective-C are pass-by-reference.

  60. *5 NSMutableString NSMutableString *myString = [[NSMutableString alloc] init]; - (void)appendCompany:(NSMutableString

    *)string { NSLog(string); }
  61. *5 NSMutableString *5 NSMutableString *myString = [[NSMutableString alloc] init]; [self

    appendCompany:myString]; - (void)appendCompany:(NSMutableString *)string { NSLog(string); }
  62. *5 NSMutableString *5 NSMutableString *myString = [[NSMutableString alloc] init]; [self

    appendCompany:myString]; - (void)appendCompany:(NSMutableString *)string { [string appendString:@“Test”]; }
  63. HANDS-ON #3 NSLOG(@“%p“, &object) will show an object’s address

  64. MEMORY MANAGEMENT Reference Counting: A trip back to the dark

    ages (pre 2011)
  65. C primitives only exist in memory as long as they’re

    in scope. How do we clean up after objects which work differently?
  66. *5 *12 NSNumber NSNumber NSNumber *myNumber = [[NSNumber alloc] init];

    NSNumber *myNumber2 = [[NSNumber alloc] init]; myNumber = myNumber2;
  67. Reference counting is a way to express ownership of an

    object. NSString 1 image: http://cocoadevcentral.com alloc
  68. Reference counting is a way to express ownership of an

    object. NSString 2
  69. Reference counting is a way to express ownership of an

    object. NSString 1
  70. Reference counting is a way to express ownership of an

    object. NSString 0
  71. Reference counting is a way to express ownership of an

    object. NSString dealloc
  72. A BALANCING ACT • Alloc or copy variants: starts with

    retain count of 1 • Retain: +1 • Release: -1 • Autorelease: -1 (later)
  73. https://developer.apple.com/library/ios/documentation/general/conceptual/devpedia-cocoacore/MemoryManagement.html

  74. IF YOU CREATE, YOU RELEASE - (void)shareActivity { NSDate *today

    = [[NSDate alloc] init]; //do some stuff with the date [today release]; }
  75. AUTORELEASE TO TRANSFER OWNERSHIP - (NSDate *)todaysDate { NSDate *today

    = [[NSDate alloc] init]; //do some stuff with the date return [today autorelease]; }
  76. TAKING OWNERSHIP PCHActivity *_activity; - (void)shareActivity:(PCHActivity *)activity { [_activity release];

    _activity = [activity retain]; } - (void)dealloc { [_activity release]; [super dealloc]; }
  77. TAKING OWNERSHIP (COPY) PCHActivity *_activity; - (void)shareActivity:(PCHActivity *)activity { [_activity

    release]; _activity = [activity copy]; } - (void)dealloc { [_activity release]; [super dealloc]; }
  78. 2014-03-30 02:30:36.172 tradeRumors[3997:20b] *** -[GameLayer update]: message sent to deallocated

    instance 0x59bf670 EXEC_BAD_ACCESS
  79. PROFILING FOR LEAKS ⌘I

  80. None
  81. HANDS-ON #4 Use NSLog(@"%lu", (unsigned long)[self retainCount]) to print an

    object’s current reference count
  82. AUTOMATIC REFERENCE COUNTING • Added in 2011 • Compiler automatically

    adds these retain/release calls • Different than garbage collection (which is at runtime) • Didn’t solve all the retain-related issues…
  83. RETAIN CYCLES CBCActivityMonitor CBCActivity

  84. RETAIN CYCLES CBCActivityMonitor CBCActivity “Weak reference”

  85. @implementation PCHActivitySharingProvider __weak PCHActivity *_activity; - (void)shareActivity:(PCHActivity *)activity { _activity

    = activity; } ... WEAK VARIABLES
  86. @import Foundation; @class PCHActivityMonitor; @interface PCHActivity : NSObject @property (weak)

    PCHActivityMonitor *monitor; @end PROPERTY ATTRIBUTES
  87. weak - does not increase the reference count atomic -

    creates a lock in setters/getters readonly - only creates a getter for the property copy - creates a copy instead of retaining original object strong - increases the reference count nonatomic - does not lock, better performance readwrite - creates setter and getter assign - similar to weak, used for primitives
  88. @import Foundation; @class PCHActivityMonitor; @interface PCHActivity : NSObject @property (strong,

    atomic, readwrite) PCHActivityMonitor *monitor; @property (assign, atomic, readwrite) BOOL isValid; @end PROPERTY DEFAULT ATTRIBUTES
  89. @property PCHActivityMonitor *monitor; _monitor @property PCHActivity *activity; _activity SYNTHESIZED IVARS

    Property definition ivar name In general though, avoid accessing ivars, always use self.property. Use ivars in custom setters and gettings.
  90. HANDS-ON #5 You can use NSLOG(@“%@“, object) again to verify

    unique copies
  91. BLOCKS

  92. ^() { //do some awesome stuff };

  93. void (^myBlock)() = ^() { NSLog(@“Inside!”); }; NSLog(@“Outside”); myBlock(); THEY’RE

    OBJECTS
  94. [UIView animateWithDuration:1 animations:^{ self.view.alpha = 0.0; }]; [myDictionary enumerateKeysAndObjectsUsingBlock:^(id k,

    id v, BOOL *stop) { NSLog(@"%@ => %@", k, v); }]; PASS THEM AROUND
  95. int (^myBlock)(int) = ^(int a) { return a * 15;

    }; int result = myBlock(10); COMPONENTS OF A BLOCK
  96. int (^myBlock)(int) = ^(int a) { return a * 15;

    }; int result = myBlock(10); DEFINITION Return type
  97. int (^myBlock)(int) = ^(int a) { return a * 15;

    }; int result = myBlock(10); DEFINITION Name
  98. int (^myBlock)(int) = ^(int a) { return a * 15;

    }; int result = myBlock(10); DEFINITION Parameter types
  99. int (^myBlock)(int) = ^(int a) { return a * 15;

    }; int result = myBlock(10); IMPLEMENTATION Parameters
  100. int (^myBlock)(int) = ^(int a) { return a * 15;

    }; int result = myBlock(10); IMPLEMENTATION Body
  101. http://f*ckingblocksyntax.com (or http://goshdarnblocksyntax.com)

  102. - (void)upload:(NSData *)data failure:(void (^)(NSError *))errorCallback; YOUR OWN BLOCKS AS

    PARAMS [server upload:myData failure:^(NSError *error) { NSLog(@“Error!?! %@“, error); }];
  103. - (void)upload:(NSData *)data failure:(void (^)(NSError *))errorCallback { //perform upload NSError

    *error = //get the upload error if (errorCallback) { errorCallback(error); } } YOUR OWN BLOCKS AS PARAMS
  104. typedef void (^CBCErrorHandler)(NSError *); @interface PCHServerAPI - (void)upload:(NSData *)data failure:(PCHErrorHandler)callback;

    - (void)synchronizeWithFailure:(PCHErrorHandler)callback; @end REUSING BLOCK DEFINITIONS
  105. GOTCHA #1: __BLOCK NSString *test = @“Hello!; void (^myBlock)() =

    ^(){ test = @“Hello 2!”; }; myBlock(); NSLog(test);
  106. GOTCHA #1: __BLOCK __block NSString *test = @“Hello!; void (^myBlock)()

    = ^(){ test = @“Hello 2!”; }; myBlock(); NSLog(test);
  107. GOTCHA #2: RETAIN CYCLES @property (nonatomic, copy) void (^errorHandler)(NSError *);

    ... self.errorHandler = ^(){ [self displayAlert:@“Oh No!”]; };
  108. _weak typeof(self) weakSelf = self; self.errorHandler = ^(){ __strong typeof(weakSelf)

    strongSelf = weakSelf; if (strongSelf) { [strongSelf displayAlert:@“Oh No!”]; } }; GOTCHA #2: RETAIN CYCLES
  109. HANDS-ON #6 You’ll need to make sure reloadData happens on

    the main thread dispatch_async(dispatch_get_main_queue(), ^{ //main thread code });
  110. ARCHITECTURAL PATTERNS

  111. These can all be abused.

  112. SINGLETON PCHServerAPI UserViewController TimelineViewController AppDelegate [PCHServerAPI sharedInstance];

  113. @implementation PCHServerAPI static CBCServerAPI *PCHServerAPISharedInstance = nil; + (instancetype)sharedServer {

    static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ PCHServerAPISharedInstance = [[PCHServerAPI alloc] init]; }); return PCHServerAPISharedInstance; }
  114. You know how often you use global variables? Use singletons

    even less.
  115. WITHOUT A SINGLETON? (static) shared downloader queue ServerAPI ServerAPI ServerAPI

  116. @implementation PCHServerAPI static dispatch_queue_t PCHServerAPISharedQueue = nil; - (instancetype)init {

    if (self = [super init]) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ PCHServerAPISharedQueue = dispatch_queue_create(...); }); } return self; }
  117. HANDS-ON #7

  118. DATA SOURCE / DELEGATE (PROTOCOLS) UITableView • What data should

    be shown? • What happens during key events (like tapping a row)?
  119. @interface CBCViewController : UIViewController <UITableViewDataSource> CBCViewController.h

  120. - (void)awakeFromNib { [super awakeFromNib]; self.tableView.dataSource = self; } ...

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 0; } ... CBCViewController.m
  121. @protocol UITableViewDataSource <NSObject> @required - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; - (UITableViewCell

    *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; @optional - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; ... UITableViewController.h
  122. @interface UITableView : UIScrollView <NSCoding> ... @property (nonatomic, weak) id

    <UITableViewDataSource> dataSource; ... UITableViewController.h
  123. NSUInteger rows = 0; if ([self.delegate respondsToSelector:@selector(tableView:numberOfRowsInSection:)]) { rows =

    [self.delegate tableView:self numberOfRowsInSection:section]; } UITableViewController.m
  124. @interface UITableView : UIScrollView <NSCoding> ... @property (nonatomic, weak) id

    <UITableViewDataSource> dataSource; @property (nonatomic, weak) id <UITableViewDelegate> delegate; ...
  125. Use this when you want two-way communications between classes, but

    you don’t want to have to hard-code references. Great for reusability / being able to swap out implementations.
  126. HANDS-ON #8

  127. @protocol PCHServerAPIDelegate <NSObject> @required - (void)downloadCountChangedTo:(NSUInteger)count; - (void)isDownloading:(BOOL)downloading; @end @interface

    PCHServerAPI : NSObject @property (nonatomic, weak) id <PCHServerAPIDelegate> delegate; @end
  128. @interface PCHServerAPI : NSObject @property (nonatomic) NSUInteger downloadCount; @property (nonatomic)

    BOOL downloading; @end WHAT IF? “Hey, tell me if these change.”
  129. KEY-VALUE OBSERVING (KVO) • Listen for changes for a given

    key-path • One-way: doesn’t need to know about subscribers • Ad-hoc: no formal contract (protocol) • Automatic • Limitless subscribers
  130. KEY-VALUE OBSERVING (KVO) UserViewController TimelineViewController AppDelegate PCHServerAPI .downloadCount .downloading

  131. Step 1 - Register - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options

    context:(void *)context Step 2 - Listen for Callbacks - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  132. [self.serverAPI addObserver:self forKeyPath:@“downloadCount” options:0 context:nil]; ... - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object

    change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"downloadCount"]) { //do something with the change } }
  133. NSStringFromSelector(@selector(downloadCount)); A FEW IMPROVEMENTS

  134. A FEW IMPROVEMENTS static void * MyContext = &MyContext; ...

    - (void)observeValueForKeyPath:(NSString *)keyPath... { if (context == MyContext) { } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } }
  135. UNSUBSCRIBING - A PAIN @try { [self.server removeObserver:self forKeyPath:NSStringFromSelector(@selector(downloadCount)) context:MyContext];

    } @catch (NSException * __unused exception) {} • Can’t check if you’re a subscriber • Will crash if you try to unsubscribe when not already subscribed
  136. @interface PCHServerAPI : NSObject @property (nonatomic) NSUInteger downloadCount; @property (nonatomic,

    readonly) BOOL downloading; @end - (BOOL)downloading { return self.downloadCount > 0; } LETS GET TRICKY
  137. + (NSSet *)keyPathsForValuesAffectingDownloading { return [NSSet setWithObjects: NSStringFromSelector(@selector(downloadCount)), nil]; }

    DERIVED PROPERTIES KEYPATH RELATIONSHIPS
  138. - (void)setDownloadCount:(NSUInteger)downloadCount { BOOL shouldUpdateDownloading = //custom logic if (shouldUpdateDownloading)

    { [self willChangeValueForKey:@"downloading"]; } _downloadCount = downloadCount; if (shouldUpdateDownloading) { [self didChangeValueForKey:@"downloading"]; } } DERIVED PROPERTIES MANUAL NOTIFICATIONS
  139. Use this when you want to allow multiple objects to

    listen to changes without the baggage of a protocol.
  140. HANDS-ON #9 • viewWillAppear is a great spot to hook

    up KVO in this example • it’s opposite, viewDidDisappear, is a great place to unsubscribe
  141. KVO OVERLOAD PCHServerAPI .downloading

  142. KVO OVERLOAD PCHServerAPI .downloading

  143. KVO OVERLOAD ServerAPI .downloading Nothing technically wrong with this. Just

    gets kinda messy.
  144. NSNOTIFICATIONCENTER PCHServerAPI Started downloading Started downloading

  145. NSNOTIFICATIONCENTER PCHServerAPI @“PCHDownloadStart” @“PCHDownloadStart”

  146. REGISTERING id subscriptionToken; subscriptionToken = [[NSNotificationCenter defaultCenter] removeObserverForName:@“PCHDownloadStart” object:nil queue:nil

    usingBlock:^(NSNotification *notification) { //do something with the notification }];
  147. POSTING [[NSNotificationCenter defaultCenter] postNotificationName:@“PCHDownloadStart” object:nil userInfo:nil];

  148. POSTING DATA [[NSNotificationCenter defaultCenter] postNotificationName:@“PCHDownloadStart” object:nil userInfo:@{@“DownloadCount”: @(4)}];

  149. DEREGISTERING [[NSNotificationCenter defaultCenter] removeObserver:subscriptionToken];

  150. extern NSString * const PCHServerDownloadStartNotification; extern NSString * const PCHServerDownloadCountKey;

    NSString * const PCHServerDownloadStartNotification = @“org.phillycocoa.downloadStartNotification”; NSString * const PCHServerDownloadCountKey = @"PCHServerDownloadCountKey"; CLEANUP - USE CONSTANTS
  151. CLEANUP - USE CONSTANTS [[NSNotificationCenter defaultCenter] postNotificationName:PCHServerDownloadStartNotification object:nil userInfo:@{PCHServerDownloadCountKey: @(4)}];

  152. Use this when you want to allow multiple objects to

    be notified when something happens without needing any link between the objects.
  153. HANDS-ON #10 • setDownloads is a great place to send

    a notification (if conditions are met) • you’ll need to use [NSNumber numberForBOOL:] to send bool values in the user info dictionary, and [number boolValue] to get it on the other side
  154. ERROR HANDLING

  155. NSError *error = nil; BOOL success = [myContext save:&error]; WHAT’S

    THAT & FOR?
  156. • Exceptions exist in Objective-C, but are expensive • Instead

    return a successful BOOL if no return type needed, or nil if an object is expected. • Use an NSError parameter with information about the error (same as you would use an exception to bubble up information).
  157. NSError *error = nil; BOOL success = [myContext save:error]; *0

  158. *0 *0 NSError *error = nil; BOOL success = [myContext

    save:error]; -(BOOL)save:(NSError*)error;
  159. *0 *5 NSError NSError *error = nil; BOOL success =

    [myContext save:error]; -(BOOL)save:(NSError*)error { error = [[NSError alloc] init]; }
  160. *0 NSError *error = nil; BOOL success = [myContext save:error];

  161. ** pointer to a pointer & location in memory A

    POINTER TO A POINTER? * pointer to an object
  162. NSError *error = nil; A POINTER TO A POINTER? *5

    NSError if (error) { *error = [NSError errorWithDomain:@“...” code:1 userInfo:nil]; } } -(BOOL)save:(NSError**)error { BOOL success = [myContext save:&error];
  163. A LITTLE MORE ON NSERROR [NSError errorWithDomain:domain code:code userInfo:userInfo]; Domain

    - reverse DNS, most likely the same as your app ID (“com.consumedbycode.slopes”, for example) Code - you pick these and define their meanings. Internal to app logic. UserInfo - can use to pass back a dictionary of values to the handler. Usually a description using NSLocalizedDescriptionKey.
  164. extern NSString * const PCHSlopesErrorDomain; extern NSString * const PCHSlopesErrorUnknownTypeDescription;

    extern int const PCHSlopesErrorUnknownTypeCode; NSString * const PCHSlopesErrorDomain = @“org.phillycocoa.slopes”; NSString * const PCHSlopesErrorUnknownTypeDescription = @"File type for export unknown.”; int const PCHSlopesErrorUnknownTypeCode = 100; CLEANUP - USE CONSTANTS
  165. HANDS-ON #11

  166. A FEW MORE LANGUAGE FEATURES

  167. CATEGORIES • A category allows you to add methods to

    an existing class without subclassing it or needing to know any of the details of how it's implemented. • Effects all instances of that class within the application.
  168. @interface NSArray (Utils) - (id)randomObject; @end @implementation NSArray (Utils) -(id)randomObject

    { if ([self count]) { return [self objectAtIndex:arc4random_uniform([self count])]; } else { return nil; } } @end CATEGORIES NSArray+utils.h NSArray+utils.m
  169. @interface NSArray (CBCUtils) - (id)pch_randomObject; @end @implementation NSArray (CBCUtils) -(id)pch_randomObject

    { if ([self count]) { return [self objectAtIndex:arc4random_uniform([self count])]; } else { return nil; } } @end BEST PRACTICE: NAMESPACE NSArray+utils.h NSArray+utils.m
  170. - (void)build; - (void)destroy; CATEGORIES FOR EXPOSING “PROTECTED” METHODS PCHActivity.h

    @interface CBCActivity - (void)build; @end PCHActivity+Protected.h @interface CBCActivity(Protected) -(void)destroy; @end
  171. @interface PCHServerAPI: NSObject @property (nonatomic) BOOL downloading; @end EXTENSIONS

  172. @interface PCHServerAPI: NSObject @property (nonatomic, readonly) BOOL downloading; @end EXTENSIONS

  173. #import “PCHServerAPI.h” @interface PCHServerAPI() @property (nonatomic, readwrite) BOOL downloading; @end

    @implementaion ... EXTENSIONS
  174. INSTANCETYPE Remember: id means “this could be any type” instancetype

    lets the compiler infer the type and can only be used for return types
  175. INSTANCETYPE - (id)init { self = [super init]; return self;

    } - (bool)testMethod { ... } bool test = [[[AClass alloc] init] testMethod]; //error!
  176. INSTANCETYPE - (id)init { self = [super init]; return self;

    } - (bool)testMethod { ... } bool test = [((AClass *)[[AClass alloc] init]) testMethod]; //yay!
  177. INSTANCETYPE - (instancetype)init { self = [super init]; return self;

    } - (bool)testMethod { ... } bool test = [[[AClass alloc] init] testMethod]; //yay!
  178. HANDS-ON #12

  179. RESOURCES http://www.objc.io http://nshipster.com http://iosdevweekly.com