Slide 1

Slide 1 text

INTERMEDIATE OBJECTIVE-C CocoaShops Curtis Herbert Carl Leiby Kotaro Fujita Mike Zornek @parrots @carlism @wild37 @zorn

Slide 2

Slide 2 text

THE BASICS

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

THE IN THE ROOM Swift is the future, right?

Slide 5

Slide 5 text

THE BASICS CORE LANGUAGE FEATURES

Slide 6

Slide 6 text

C PRIMITIVES FLOAT DOUBLE VOID CHAR BOOL INT

Slide 7

Slide 7 text

int myAge = 6; myAge = myAge + 6; int alsoMyAge = myAge; C PRIMITIVES

Slide 8

Slide 8 text

WHERE THINGS GET FUN: OBJ-C IS A DYNAMIC RUNTIME A quick detour to the deep end

Slide 9

Slide 9 text

[tableView reloadData]; Send the message “reloadData” to the object “tableView” objc_msgsend(tableview, @selector(reloadData)); MESSAGE SENDING

Slide 10

Slide 10 text

“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.”

Slide 11

Slide 11 text

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)

Slide 12

Slide 12 text

-[NSArray objectForKey:]: unrecognized selector sent to instance 0x102301c40 
 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSArray objectForKey:]: unrecognized selector sent to instance 0x102301c40'

Slide 13

Slide 13 text

CLASS OBJ-C PRIMITIVES SEL ID NIL

Slide 14

Slide 14 text

OBJ-C PRIMITIVES SEL CLASS ID id name = @“Curtis”; id date = [NSDate new]; NIL

Slide 15

Slide 15 text

OBJ-C PRIMITIVES SEL CLASS ID - (id) someMethod; NIL

Slide 16

Slide 16 text

OBJ-C PRIMITIVES SEL CLASS ID SEL mySelector = @selector(update); [self performSelector:mySelector]; NIL

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

OBJ-C PRIMITIVES SEL CLASS ID SEL mySelector = @selector(updateName:); [self performSelector:mySelector withObject:@“Joe”]; NIL

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

OBJ-C PRIMITIVES SEL CLASS ID Class targetClass = [NSDate class]; if ([myObject isKindOfClass:targetClass]) … NIL

Slide 21

Slide 21 text

OBJ-C PRIMITIVES SEL CLASS ID myObject = nil; NSString *name = [myObject showName]; NIL

Slide 22

Slide 22 text

OBJ-C DIRECTIVES • Instructions to the compiler • @IBOutlet, @property, @class, etc • @“string”, @[@“string1”], @{@“key” : @“value”}

Slide 23

Slide 23 text

HANDS-ON #1

Slide 24

Slide 24 text

CORE LANGUAGE FEATURES Deep breaths - back to more familiar territory

Slide 25

Slide 25 text

OBJ-C CLASSES UIVIEWCONTROLLER NSSTRING NSNUMBER UITABLEVIEW NSARRAY UIVIEW NSDATE NSDICTIONARY NSURL UILABEL UIIMAGEVIEW NSFORMATTER CLLOCTIONMANAGER UISWITCH UICOLLECTIONVIEW

Slide 26

Slide 26 text

STANDARD OO STUFF NSObject UIView : UIResponder UIResponder : NSObject Inherits from subclass Inherits from subclass

Slide 27

Slide 27 text

UIView : UIResponder Conforms to protocols STANDARD OO STUFF

Slide 28

Slide 28 text

COMPONENTS OF A CLASSES HEADER Properties, method signatures Exposed to the outside world IMPLEMENTATION The executable code

Slide 29

Slide 29 text

@import Foundation; @class PCHActivity; @interface PCHActivitySharingProvider : NSObject - (void)shareActivity:(PCHActivity *)activity; @end

Slide 30

Slide 30 text

#import “PCHActivitySharingProvider.h” #import @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

Slide 31

Slide 31 text

- (void)share; - (void)shareActivity:(PCHActivity *)activity; - (void)shareActivity:(PCHActivity *)activity withName:(NSString *)name forUser:(PCHUser *)user; + (instancetype)sharedInstance; METHODS

Slide 32

Slide 32 text

share shareActivity: shareActivity:withName:forUser: sharedInstance METHODS

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

@implementation PCHActivitySharingProvider PCHActivity *_activity; - (void)setActivity:(PCHActivity *)activity { if (activity != _activity) { _activity = activity; } } - (PCHActivity *)activity { return _activity; } ...

Slide 35

Slide 35 text

@import Foundation; @class CBCActivity; @interface CBCActivitySharingProvider : NSObject @property CBCActivity *activity; - (void)shareActivity:(CBCActivity *)activity; @end PROPERTIES

Slide 36

Slide 36 text

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];

Slide 37

Slide 37 text

CONVENTIONS - ACCESSORS setFont: and font not setFont: and getFont

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

HANDS-ON #2

Slide 42

Slide 42 text

POINTERS Not the tips and tricks kind

Slide 43

Slide 43 text

- (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.

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

Objects don’t work this way in Objective-C. They use pointers.

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

*0 NSNumber *myNumber; The nil value means “this instance reference isn’t pointing to any instance.”

Slide 56

Slide 56 text

*0 NSNumber *myNumber = nil; if (myNumber) { //do something if not nil } nil is equivalent to 0

Slide 57

Slide 57 text

*5 *12 NSNumber NSNumber NSNumber *myNumber = [[NSNumber alloc] init]; NSNumber *myNumber2 = [[NSNumber alloc] init];

Slide 58

Slide 58 text

NSNumber *myNumber = [[NSNumber alloc] init]; NSNumber *myNumber2 = [[NSNumber alloc] init]; myNumber = myNumber2; *5 *5 NSNumber NSNumber

Slide 59

Slide 59 text

Objects in Objective-C are pass-by-reference.

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

*5 NSMutableString *5 NSMutableString *myString = [[NSMutableString alloc] init]; [self appendCompany:myString]; - (void)appendCompany:(NSMutableString *)string { [string appendString:@“Test”]; }

Slide 63

Slide 63 text

HANDS-ON #3 NSLOG(@“%p“, &object) will show an object’s address

Slide 64

Slide 64 text

MEMORY MANAGEMENT Reference Counting: A trip back to the dark ages (pre 2011)

Slide 65

Slide 65 text

C primitives only exist in memory as long as they’re in scope. How do we clean up after objects which work differently?

Slide 66

Slide 66 text

*5 *12 NSNumber NSNumber NSNumber *myNumber = [[NSNumber alloc] init]; NSNumber *myNumber2 = [[NSNumber alloc] init]; myNumber = myNumber2;

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

A BALANCING ACT • Alloc or copy variants: starts with retain count of 1 • Retain: +1 • Release: -1 • Autorelease: -1 (later)

Slide 73

Slide 73 text

https://developer.apple.com/library/ios/documentation/general/conceptual/devpedia-cocoacore/MemoryManagement.html

Slide 74

Slide 74 text

IF YOU CREATE, YOU RELEASE - (void)shareActivity { NSDate *today = [[NSDate alloc] init]; //do some stuff with the date [today release]; }

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

2014-03-30 02:30:36.172 tradeRumors[3997:20b] *** -[GameLayer update]: message sent to deallocated instance 0x59bf670 EXEC_BAD_ACCESS

Slide 79

Slide 79 text

PROFILING FOR LEAKS ⌘I

Slide 80

Slide 80 text

No content

Slide 81

Slide 81 text

HANDS-ON #4 Use NSLog(@"%lu", (unsigned long)[self retainCount]) to print an object’s current reference count

Slide 82

Slide 82 text

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…

Slide 83

Slide 83 text

RETAIN CYCLES CBCActivityMonitor CBCActivity

Slide 84

Slide 84 text

RETAIN CYCLES CBCActivityMonitor CBCActivity “Weak reference”

Slide 85

Slide 85 text

@implementation PCHActivitySharingProvider __weak PCHActivity *_activity; - (void)shareActivity:(PCHActivity *)activity { _activity = activity; } ... WEAK VARIABLES

Slide 86

Slide 86 text

@import Foundation; @class PCHActivityMonitor; @interface PCHActivity : NSObject @property (weak) PCHActivityMonitor *monitor; @end PROPERTY ATTRIBUTES

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

@import Foundation; @class PCHActivityMonitor; @interface PCHActivity : NSObject @property (strong, atomic, readwrite) PCHActivityMonitor *monitor; @property (assign, atomic, readwrite) BOOL isValid; @end PROPERTY DEFAULT ATTRIBUTES

Slide 89

Slide 89 text

@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.

Slide 90

Slide 90 text

HANDS-ON #5 You can use NSLOG(@“%@“, object) again to verify unique copies

Slide 91

Slide 91 text

BLOCKS

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

[UIView animateWithDuration:1 animations:^{ self.view.alpha = 0.0; }]; [myDictionary enumerateKeysAndObjectsUsingBlock:^(id k, id v, BOOL *stop) { NSLog(@"%@ => %@", k, v); }]; PASS THEM AROUND

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

http://f*ckingblocksyntax.com (or http://goshdarnblocksyntax.com)

Slide 102

Slide 102 text

- (void)upload:(NSData *)data failure:(void (^)(NSError *))errorCallback; YOUR OWN BLOCKS AS PARAMS [server upload:myData failure:^(NSError *error) { NSLog(@“Error!?! %@“, error); }];

Slide 103

Slide 103 text

- (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

Slide 104

Slide 104 text

typedef void (^CBCErrorHandler)(NSError *); @interface PCHServerAPI - (void)upload:(NSData *)data failure:(PCHErrorHandler)callback; - (void)synchronizeWithFailure:(PCHErrorHandler)callback; @end REUSING BLOCK DEFINITIONS

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

GOTCHA #2: RETAIN CYCLES @property (nonatomic, copy) void (^errorHandler)(NSError *); ... self.errorHandler = ^(){ [self displayAlert:@“Oh No!”]; };

Slide 108

Slide 108 text

_weak typeof(self) weakSelf = self; self.errorHandler = ^(){ __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf) { [strongSelf displayAlert:@“Oh No!”]; } }; GOTCHA #2: RETAIN CYCLES

Slide 109

Slide 109 text

HANDS-ON #6 You’ll need to make sure reloadData happens on the main thread dispatch_async(dispatch_get_main_queue(), ^{ //main thread code });

Slide 110

Slide 110 text

ARCHITECTURAL PATTERNS

Slide 111

Slide 111 text

These can all be abused.

Slide 112

Slide 112 text

SINGLETON PCHServerAPI UserViewController TimelineViewController AppDelegate [PCHServerAPI sharedInstance];

Slide 113

Slide 113 text

@implementation PCHServerAPI static CBCServerAPI *PCHServerAPISharedInstance = nil; + (instancetype)sharedServer { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ PCHServerAPISharedInstance = [[PCHServerAPI alloc] init]; }); return PCHServerAPISharedInstance; }

Slide 114

Slide 114 text

You know how often you use global variables? Use singletons even less.

Slide 115

Slide 115 text

WITHOUT A SINGLETON? (static) shared downloader queue ServerAPI ServerAPI ServerAPI

Slide 116

Slide 116 text

@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; }

Slide 117

Slide 117 text

HANDS-ON #7

Slide 118

Slide 118 text

DATA SOURCE / DELEGATE (PROTOCOLS) UITableView • What data should be shown? • What happens during key events (like tapping a row)?

Slide 119

Slide 119 text

@interface CBCViewController : UIViewController CBCViewController.h

Slide 120

Slide 120 text

- (void)awakeFromNib { [super awakeFromNib]; self.tableView.dataSource = self; } ... - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 0; } ... CBCViewController.m

Slide 121

Slide 121 text

@protocol UITableViewDataSource @required - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; @optional - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; ... UITableViewController.h

Slide 122

Slide 122 text

@interface UITableView : UIScrollView ... @property (nonatomic, weak) id dataSource; ... UITableViewController.h

Slide 123

Slide 123 text

NSUInteger rows = 0; if ([self.delegate respondsToSelector:@selector(tableView:numberOfRowsInSection:)]) { rows = [self.delegate tableView:self numberOfRowsInSection:section]; } UITableViewController.m

Slide 124

Slide 124 text

@interface UITableView : UIScrollView ... @property (nonatomic, weak) id dataSource; @property (nonatomic, weak) id delegate; ...

Slide 125

Slide 125 text

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.

Slide 126

Slide 126 text

HANDS-ON #8

Slide 127

Slide 127 text

@protocol PCHServerAPIDelegate @required - (void)downloadCountChangedTo:(NSUInteger)count; - (void)isDownloading:(BOOL)downloading; @end @interface PCHServerAPI : NSObject @property (nonatomic, weak) id delegate; @end

Slide 128

Slide 128 text

@interface PCHServerAPI : NSObject @property (nonatomic) NSUInteger downloadCount; @property (nonatomic) BOOL downloading; @end WHAT IF? “Hey, tell me if these change.”

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

KEY-VALUE OBSERVING (KVO) UserViewController TimelineViewController AppDelegate PCHServerAPI .downloadCount .downloading

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

[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 } }

Slide 133

Slide 133 text

NSStringFromSelector(@selector(downloadCount)); A FEW IMPROVEMENTS

Slide 134

Slide 134 text

A FEW IMPROVEMENTS static void * MyContext = &MyContext; ... - (void)observeValueForKeyPath:(NSString *)keyPath... { if (context == MyContext) { } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } }

Slide 135

Slide 135 text

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

Slide 136

Slide 136 text

@interface PCHServerAPI : NSObject @property (nonatomic) NSUInteger downloadCount; @property (nonatomic, readonly) BOOL downloading; @end - (BOOL)downloading { return self.downloadCount > 0; } LETS GET TRICKY

Slide 137

Slide 137 text

+ (NSSet *)keyPathsForValuesAffectingDownloading { return [NSSet setWithObjects: NSStringFromSelector(@selector(downloadCount)), nil]; } DERIVED PROPERTIES KEYPATH RELATIONSHIPS

Slide 138

Slide 138 text

- (void)setDownloadCount:(NSUInteger)downloadCount { BOOL shouldUpdateDownloading = //custom logic if (shouldUpdateDownloading) { [self willChangeValueForKey:@"downloading"]; } _downloadCount = downloadCount; if (shouldUpdateDownloading) { [self didChangeValueForKey:@"downloading"]; } } DERIVED PROPERTIES MANUAL NOTIFICATIONS

Slide 139

Slide 139 text

Use this when you want to allow multiple objects to listen to changes without the baggage of a protocol.

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

KVO OVERLOAD PCHServerAPI .downloading

Slide 142

Slide 142 text

KVO OVERLOAD PCHServerAPI .downloading

Slide 143

Slide 143 text

KVO OVERLOAD ServerAPI .downloading Nothing technically wrong with this. Just gets kinda messy.

Slide 144

Slide 144 text

NSNOTIFICATIONCENTER PCHServerAPI Started downloading Started downloading

Slide 145

Slide 145 text

NSNOTIFICATIONCENTER PCHServerAPI @“PCHDownloadStart” @“PCHDownloadStart”

Slide 146

Slide 146 text

REGISTERING id subscriptionToken; subscriptionToken = [[NSNotificationCenter defaultCenter] removeObserverForName:@“PCHDownloadStart” object:nil queue:nil usingBlock:^(NSNotification *notification) { //do something with the notification }];

Slide 147

Slide 147 text

POSTING [[NSNotificationCenter defaultCenter] postNotificationName:@“PCHDownloadStart” object:nil userInfo:nil];

Slide 148

Slide 148 text

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

Slide 149

Slide 149 text

DEREGISTERING [[NSNotificationCenter defaultCenter] removeObserver:subscriptionToken];

Slide 150

Slide 150 text

extern NSString * const PCHServerDownloadStartNotification; extern NSString * const PCHServerDownloadCountKey; NSString * const PCHServerDownloadStartNotification = @“org.phillycocoa.downloadStartNotification”; NSString * const PCHServerDownloadCountKey = @"PCHServerDownloadCountKey"; CLEANUP - USE CONSTANTS

Slide 151

Slide 151 text

CLEANUP - USE CONSTANTS [[NSNotificationCenter defaultCenter] postNotificationName:PCHServerDownloadStartNotification object:nil userInfo:@{PCHServerDownloadCountKey: @(4)}];

Slide 152

Slide 152 text

Use this when you want to allow multiple objects to be notified when something happens without needing any link between the objects.

Slide 153

Slide 153 text

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

Slide 154

Slide 154 text

ERROR HANDLING

Slide 155

Slide 155 text

NSError *error = nil; BOOL success = [myContext save:&error]; WHAT’S THAT & FOR?

Slide 156

Slide 156 text

• 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).

Slide 157

Slide 157 text

NSError *error = nil; BOOL success = [myContext save:error]; *0

Slide 158

Slide 158 text

*0 *0 NSError *error = nil; BOOL success = [myContext save:error]; -(BOOL)save:(NSError*)error;

Slide 159

Slide 159 text

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

Slide 160

Slide 160 text

*0 NSError *error = nil; BOOL success = [myContext save:error];

Slide 161

Slide 161 text

** pointer to a pointer & location in memory A POINTER TO A POINTER? * pointer to an object

Slide 162

Slide 162 text

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];

Slide 163

Slide 163 text

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.

Slide 164

Slide 164 text

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

Slide 165

Slide 165 text

HANDS-ON #11

Slide 166

Slide 166 text

A FEW MORE LANGUAGE FEATURES

Slide 167

Slide 167 text

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.

Slide 168

Slide 168 text

@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

Slide 169

Slide 169 text

@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

Slide 170

Slide 170 text

- (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

Slide 171

Slide 171 text

@interface PCHServerAPI: NSObject @property (nonatomic) BOOL downloading; @end EXTENSIONS

Slide 172

Slide 172 text

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

Slide 173

Slide 173 text

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

Slide 174

Slide 174 text

INSTANCETYPE Remember: id means “this could be any type” instancetype lets the compiler infer the type and can only be used for return types

Slide 175

Slide 175 text

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

Slide 176

Slide 176 text

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

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

HANDS-ON #12

Slide 179

Slide 179 text

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