Slide 1

Slide 1 text

Modernize your Objective-C Massimo Oliviero! Software Developer
 @maxoly

Slide 2

Slide 2 text

About Massimo Oliviero
 Freelance Software Developer & Trainer
 #pragma mark founder ! www.massimooliviero.net
 [email protected]
 @maxoly

Slide 3

Slide 3 text

#pragma mark iOS & OSX Developers Community We organize the #PragmaConference, an event dedicated to iOS and OS X development ! http://www.pragmamark.org http://www.facebook.com/groups/pragmamark @pragmamarkorg

Slide 4

Slide 4 text

Agenda ‣ Syntax ‣ Conventions ‣ Coding ‣ Framework

Slide 5

Slide 5 text

Syntax

Slide 6

Slide 6 text

use new literal syntax you can create NSArray, NSDictionary, NSNumber with a simple @

Slide 7

Slide 7 text

Literals NSArray *names = [NSArray arrayWithObjects:@"Marco", @"Paolo", @"Flavio", nil]; NSDictionary *jobs = [NSDictionary dictionaryWithObjectsAndKeys:@"Marco", @"CTO", @"Paolo", "CEO", nil]; NSNumber *one = [NSNumber numberWithInt:1]; NSNumber *boolNum = [NSNumber numberWithBool:YES]; NSNumber *square = [NSNumber numberWithDouble:sqrt(2)];

Slide 8

Slide 8 text

Literals NSArray *names = [NSArray arrayWithObjects:@"Marco", @"Paolo", @"Flavio", nil]; NSDictionary *jobs = [NSDictionary dictionaryWithObjectsAndKeys:@"Marco", @"CTO", @"Paolo", "CEO", nil]; NSNumber *one = [NSNumber numberWithInt:1]; NSNumber *boolNum = [NSNumber numberWithBool:YES]; NSNumber *square = [NSNumber numberWithDouble:sqrt(2)]; NSArray *names = @[ @"Marco", @"Paolo", @"Giuseppe" ]; ! NSDictionary *jobs = @{ @"CTO" : @"Marco", @"CEO" : @"Paolo" }; ! NSNumber *one = @1; NSNumber *boolNum = @YES; NSNumber *square = @(sqrt(2));

Slide 9

Slide 9 text

Literals ‣ The new object literals significantly reduce the verbosity of code ‣ All objects made via literal (such an array or a dictionary) are immutable ‣ Remember that @(expr) dynamically evaluates the boxed expression and returns the appropriate object literal based on its value

Slide 10

Slide 10 text

use Object Subscripting Object pointer values can now be used with C’s subscripting operator

Slide 11

Slide 11 text

Object Subscripting ! NSArray *people = @[ @"John", @"Steve", @"Bill" ]; NSString *steve = [people objectAtIndex:1]; NSDictionary *books = @{ @"123" : @"Odyssey", @"234" : @"Illiad" }; NSString *illiad = [books objectForKey:@“234"]; ! // Mutable NSMutableArray *mPeople = [people mutableCopy]; [mPeople addObject:@"Ive"]; NSMutableDictionary *mBooks = [books mutableCopy]; [mBooks setObject:@"Othello" forKey:@"876"];

Slide 12

Slide 12 text

Object Subscripting ! NSArray *people = @[ @"John", @"Steve", @"Bill" ]; NSString *steve = [people objectAtIndex:1]; NSDictionary *books = @{ @"123" : @"Odyssey", @"234" : @"Illiad" }; NSString *illiad = [books objectForKey:@“234"]; ! // Mutable NSMutableArray *mPeople = [people mutableCopy]; [mPeople addObject:@"Ive"]; NSMutableDictionary *mBooks = [books mutableCopy]; [mBooks setObject:@"Othello" forKey:@"876"]; ! NSArray *people = @[ @"John", @"Steve", @"Bill" ]; NSString *steve = people[1]; NSDictionary *books = @{ @"123" : @"Odyssey", @"234" : @"Illiad" }; NSString *illiad = books[@"234"]; ! ! // Mutable NSMutableArray *mPeople = [people mutableCopy]; mPeople[3] = @"Ive"; NSMutableDictionary *mBooks = [books mutableCopy]; mBooks[@"876"] = @"Othello";

Slide 13

Slide 13 text

Custom Subscripting @interface MGACustomer : NSObject ! @property (nonatomic, strong, readonly) NSArray *orders; ! // Custom Indexed Subscripting - (id)objectAtIndexedSubscript:(NSUInteger)idx; - (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx; ! // Custom Keyed Subscripting - (id)objectForKeyedSubscript:(id )key; - (void)setObject:(id)obj forKeyedSubscript:(id )key; ! @end ! ! // how to use custom subscripting - (void)foo { MGACustomer *customer = [[MGACustomer alloc] init]; id order67 = customer[67]; id order34 = customer[@"order.34"]; }

Slide 14

Slide 14 text

Object Subscripting ‣ Subscripting syntax can significantly reduce the verbosity of code that deals heavily with arrays and dictionaries ‣ Syntax similar to that found in common scripting languages ‣ You can extend your own classes with subscripting support

Slide 15

Slide 15 text

use new @import declaration @import is the new compiler directive introduced by Modules

Slide 16

Slide 16 text

@import #import #import // you must add framework from Target > Build Phases ! ! @interface MGAMapViewController : UIViewController ! @property (weak, nonatomic) IBOutlet MKMapView *mapView; ! @end

Slide 17

Slide 17 text

@import #import #import // you must add framework from Target > Build Phases ! ! @interface MGAMapViewController : UIViewController ! @property (weak, nonatomic) IBOutlet MKMapView *mapView; ! @end @import UIKit; @import MapKit; // no additional steps required ! ! @interface MGAMapViewController : UIViewController ! @property (weak, nonatomic) IBOutlet MKMapView *mapView; ! @end

Slide 18

Slide 18 text

@import ‣ Modules provide an alternative, simpler way to use software libraries that provides better compile-time scalability and eliminates many of the problems inherent to using the C preprocessor to access the API of a library ‣ Modules link frameworks automatically (no more need to link framework from Build Phases) ‣ At this time, modules are only available for Apple’s frameworks and have been implicitly disabled for C++

Slide 19

Slide 19 text

use new type instancetype instancetype is a contextual keyword that can be used as a result type to signal that a method returns a related result type, you can use it as return type of an init method or a (convenient)constructor

Slide 20

Slide 20 text

instancetype @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; + (id)customerWithName:(NSString *)name; ! @end ! @implementation MGACustomer ! + (id)customerWithName:(NSString *)name { id user = [[self alloc] init]; [user setName:name]; return user; } ! @end ! ! ! ! ! ! ! !

Slide 21

Slide 21 text

instancetype @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; + (id)customerWithName:(NSString *)name; ! @end ! @implementation MGACustomer ! + (id)customerWithName:(NSString *)name { id user = [[self alloc] init]; [user setName:name]; return user; } ! @end ! ! ! ! ! ! ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; + (id)customerWithName:(NSString *)name; ! @end ! @implementation MGACustomer ! + (id)customerWithName:(NSString *)name { id user = [[self alloc] init]; [user setName:name]; return user; } ! @end ! .. ! - (void)foo { // assign an object of type MGACustomer to a string variable // will not generate any error or warning at compile-time NSString *wrong = [MGACustomer customerWithName:@"test"]; }

Slide 22

Slide 22 text

instancetype @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; + (id)customerWithName:(NSString *)name; ! @end ! @implementation MGACustomer ! + (id)customerWithName:(NSString *)name { id user = [[self alloc] init]; [user setName:name]; return user; } ! @end ! ! ! ! ! ! ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; + (id)customerWithName:(NSString *)name; ! @end ! @implementation MGACustomer ! + (id)customerWithName:(NSString *)name { id user = [[self alloc] init]; [user setName:name]; return user; } ! @end ! .. ! - (void)foo { // assign an object of type MGACustomer to a string variable // will not generate any error or warning at compile-time NSString *wrong = [MGACustomer customerWithName:@"test"]; } @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; + (instancetype)customerWithName:(NSString *)name; ! @end ! @implementation MGACustomer ! + (instancetype)customerWithName:(NSString *)name { id user = [[self alloc] init]; [user setName:name]; return user; } ! @end ! .. ! - (void)foo { // With ‘instancetype’ the compiler could infer type, it will generate: Incompatible // pointer types initializing 'NSString *' with an expression of type 'MGACustomer *' NSString *wrong = [MGACustomer customerWithName:@"test"]; }

Slide 23

Slide 23 text

instancetype ‣ With instancetype, the compiler will correctly infer that the result of convenient constructor is an instance of a MGACustomer ‣ Instancetype, unlike id, can only be used as the result type in a method declaration ‣ Init method should also use instancetype instead of id, instancetype is more explicit and therefore better than id

Slide 24

Slide 24 text

use auto property synthesis synthesize not necessary for @property since Xcode 4.4

Slide 25

Slide 25 text

auto property synthesis @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; @property (nonatomic, readwrite, copy) NSString *address; ! @end ! ! ! ! @implementation MGACustomer { NSString *_name; NSString *_address; } ! @synthesize name = _name; @synthesize address = _address; ! - (void)foo { _name = @"Apple Inc."; } ! @end

Slide 26

Slide 26 text

auto property synthesis @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; @property (nonatomic, readwrite, copy) NSString *address; ! @end ! ! ! ! @implementation MGACustomer { NSString *_name; NSString *_address; } ! @synthesize name = _name; @synthesize address = _address; ! - (void)foo { _name = @"Apple Inc."; } ! @end @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; @property (nonatomic, readwrite, copy) NSString *address; ! @end ! ! ! ! @implementation MGACustomer ! ! // compiler auto-synthesizes properties for you // and generate corresponding ivar(s) // with underscore as prefix ! ! ! - (void)foo { _name = @"Apple Inc.”; // you can still access to ivar } ! @end

Slide 27

Slide 27 text

auto property synthesis ‣ Clang provides support for autosynthesis of declared properties. Using this feature, clang provides default synthesis of those properties not declared @dynamic and not having user provided backing getter and setter methods. ‣ Auto-synthesis is not support for properties defined in a protocol ‣ The compiler will add the ivar for you (with underscore prefix) when it adds the required accessor methods

Slide 28

Slide 28 text

use copy for any immutable class for attributes whose type is an immutable value class that conforms to the NSCopying protocol (e.g.. NSDate, NSNumber, NSArray, NSSet), you almost always should specify copy in your @property declaration

Slide 29

Slide 29 text

copy attribute ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, strong) NSString *name; @property (nonatomic, readwrite, strong) NSString *address; @property (nonatomic, readwrite, strong) NSArray *orders; @property (nonatomic, readwrite, strong) NSDate *created; ! @end !

Slide 30

Slide 30 text

copy attribute ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, strong) NSString *name; @property (nonatomic, readwrite, strong) NSString *address; @property (nonatomic, readwrite, strong) NSArray *orders; @property (nonatomic, readwrite, strong) NSDate *created; ! @end ! ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; @property (nonatomic, readwrite, copy) NSString *address; @property (nonatomic, readwrite, copy) NSArray *orders; @property (nonatomic, readwrite, copy) NSDate *created; ! @end !

Slide 31

Slide 31 text

copy attribute ‣ The reason of copy is that it is possible to have a property that is declared as an immutable type (such as NSString) yet pass it a mutable type (such as NSMutableString). In which case it is possible to change the property from outside the class ‣ using copy is recommended, because it behaves sensibly with class clusters, sending copy to a mutable class returns an immutable copy of the object

Slide 32

Slide 32 text

NS_ENUM & NS_OPTIONS macro these macros are the new, preferred way to declare enum types.

Slide 33

Slide 33 text

NS_ENUM typedef enum MGAUserProfile : NSUInteger { MGAUserProfileAdmin, MGAUserProfileGuest, MGAUserProfileOperator } MGAUserProfile; ! ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, assign) MGAUserProfile profile; ! @end

Slide 34

Slide 34 text

NS_ENUM typedef enum MGAUserProfile : NSUInteger { MGAUserProfileAdmin, MGAUserProfileGuest, MGAUserProfileOperator } MGAUserProfile; ! ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, assign) MGAUserProfile profile; ! @end typedef NS_ENUM(NSUInteger, MGAUserProfile) { MGAUserProfileAdmin, MGAUserProfileGuest, MGAUserProfileOperator }; ! ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, assign) MGAUserProfile profile; ! @end

Slide 35

Slide 35 text

NS_OPTIONS typedef enum MGAUserProfile : NSUInteger { MGAUserProfileAdmin = 1 << 0, MGAUserProfileGuest = 1 << 1, MGAUserProfileOperator = 1 << 2 } MGAUserProfile; ! ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, assign) MGAUserProfile profile; ! @end

Slide 36

Slide 36 text

NS_OPTIONS typedef enum MGAUserProfile : NSUInteger { MGAUserProfileAdmin = 1 << 0, MGAUserProfileGuest = 1 << 1, MGAUserProfileOperator = 1 << 2 } MGAUserProfile; ! ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, assign) MGAUserProfile profile; ! @end typedef NS_OPTIONS(NSUInteger, MGAUserProfile) { MGAUserProfileAdmin = 1 << 0, MGAUserProfileGuest = 1 << 1, MGAUserProfileOperator = 1 << 2, }; ! ! ! @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, assign) MGAUserProfile profile; ! @end

Slide 37

Slide 37 text

NS_ENUM & NS_OPTIONS ‣ The new macros combines the best of all ways to declare an enum or an option, and even provide hints to the compiler for type-checking and switch statement completeness. ‣ If the compiler is operating in C++11 or Objective-C+ +11 mode, the macros behave slightly differently in order to make the code compatible with the mixed mode

Slide 38

Slide 38 text

use extern const not #define the best way to define a globally accessible constant is extern const

Slide 39

Slide 39 text

// Constants.h #define kMGAProjectConst @“myString" #define kMGASomeMagicValue 42 ! ! ! ! ! ! ! extern const

Slide 40

Slide 40 text

// Constants.h #define kMGAProjectConst @“myString" #define kMGASomeMagicValue 42 ! ! ! ! ! ! ! extern const // Constants.h extern NSString * const kMGAProjectConst; extern CGFloat const kMGASomeMagicValue; // Constants.m #import “Constants.h" NSString * const kMGAProjectConst = @“server.com”; CGFloat const kMGASomeMagicValue = 42.0f;

Slide 41

Slide 41 text

// Constants.h #define kMGAProjectConst @“myString" #define kMGASomeMagicValue 42 ! ! ! ! ! ! ! extern const // Constants.h extern NSString * const kMGAProjectConst; extern CGFloat const kMGASomeMagicValue; // Constants.m #import “Constants.h" NSString * const kMGAProjectConst = @“server.com”; CGFloat const kMGASomeMagicValue = 42.0f; // Constants.h FOUNDATION_EXPORT NSString * const kMGAProjectConst; FOUNDATION_EXPORT CGFloat const kMGASomeMagicValue; ! ! ! // Constants.m #import “Constants.h" ! NSString * const kMGAProjectConst = @“server.com”; CGFloat const kMGASomeMagicValue = 42.0f;

Slide 42

Slide 42 text

extern const ‣ It ensures that the compiler do a static type checking and emit a warning if you try to use it somewhere where it isn't expecting ‣ One benefit is that changing the value of a constant does not cause a rebuild of your entire program. ‣ Use FOUNDATION_EXPORT macro instead of extern if your code will be used in mixed C/C++ environments or on other platforms

Slide 43

Slide 43 text

use Foundation Data Types if you are unsure, the Foundation Data Types (like NSInterger or CGFloat) are the best choice because they are architecture safe versions of the corresponding C types

Slide 44

Slide 44 text

Foundation Data Types ‣ The Fondation Data Types were introduced to make it easier to write code that works on both 32-bit and 64- bit without modification ‣ You usually want to use Foundation Data Types when you don't know what kind of processor architecture your code might run on ‣ However, if you need to take control on memory footprint you can use native types

Slide 45

Slide 45 text

Use Class Extensions to Hide Private Data Class extensions are often used to extend the public interface with additional private methods, properties or ivars for use within the implementation of the class itself

Slide 46

Slide 46 text

// MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () ! ! ! ! ! ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! ! ! ! ! ! @end Class Extensions

Slide 47

Slide 47 text

// MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () ! ! ! ! ! ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! ! ! ! ! ! @end // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () ! ! ! ! - (void)privateMethod; ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! - (void)privateMethod { } ! @end Class Extensions

Slide 48

Slide 48 text

// MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () ! ! ! ! ! ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! ! ! ! ! ! @end // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () ! ! ! ! - (void)privateMethod; ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! - (void)privateMethod { } ! @end // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () { NSString *privateString; } ! - (void)privateMethod; ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! - (void)privateMethod { privateString = @"hello"; } ! @end Class Extensions

Slide 49

Slide 49 text

// MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () ! ! ! ! ! ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! ! ! ! ! ! @end // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () ! ! ! ! - (void)privateMethod; ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! - (void)privateMethod { } ! @end // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () { NSString *privateString; } ! - (void)privateMethod; ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! - (void)privateMethod { privateString = @"hello"; } ! @end Class Extensions // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) NSArray *points; ! @end ! // MGAMapViewController.m @interface MGAMapViewController () { NSString *privateString; } ! - (void)privateMethod; ! @property (nonatomic, strong, readwrite) NSArray *points; ! @end ! @implementation MGAMapViewController ! ! ! ! - (void)privateMethod { privateString = @"hello"; } ! @end

Slide 50

Slide 50 text

// MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () ! ! ! ! ! ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! ! ! ! ! ! @end // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () ! ! ! ! - (void)privateMethod; ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! - (void)privateMethod { } ! @end // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! ! ! @end ! // MGAMapViewController.m @interface MGAMapViewController () { NSString *privateString; } ! - (void)privateMethod; ! ! ! @end ! @implementation MGAMapViewController ! ! ! ! - (void)privateMethod { privateString = @"hello"; } ! @end Class Extensions // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) NSArray *points; ! @end ! // MGAMapViewController.m @interface MGAMapViewController () { NSString *privateString; } ! - (void)privateMethod; ! @property (nonatomic, strong, readwrite) NSArray *points; ! @end ! @implementation MGAMapViewController ! ! ! ! - (void)privateMethod { privateString = @"hello"; } ! @end // MGAMapViewController.h @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) NSArray *points; ! @end ! // MGAMapViewController.m @interface MGAMapViewController () { NSString *privateString; } ! - (void)privateMethod; ! @property (nonatomic, strong, readwrite) NSArray *points; ! @end ! @implementation MGAMapViewController { NSInteger counter; } ! - (void)privateMethod { privateString = @"hello"; } ! @end

Slide 51

Slide 51 text

Class Extensions ‣ A class extension bears some similarity to a category, but it can only be added to a class for which you have the source code at compile time. The methods declared by a class extension are implemented in the @implementation block for the original class ‣ By declaring the class extension inside the source code file for the class implementation, the information stays private to the class; the header files in theory should only expose public interface for your classes

Slide 52

Slide 52 text

Class Extensions ‣ It’s possible to use a class extension to add custom instance variables. These are declared inside braces in the class extension interface ‣ It’s also common, for example, to define a property as readonly in the interface, but as readwrite in a class extension declared above the implementation, in order that the internal methods of the class can change the property value directly ‣ It’s also possible to declare an ivar in the @implemetation block

Slide 53

Slide 53 text

Conventions

Slide 54

Slide 54 text

use prefixes on all classes classes must be named uniquely in order to avoid name clashes

Slide 55

Slide 55 text

use prefixes on all classes @interface Customer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; @property (nonatomic, readwrite, copy) NSString *address; ! @end ! ! ! ! @interface HomeViewController : UIViewController ! @property (weak, nonatomic) IBOutlet UILabel *userLabel; @property (weak, nonatomic) IBOutlet UILabel *logoLabel; @property (strong, nonatomic) Customer *user; ! @end

Slide 56

Slide 56 text

use prefixes on all classes @interface Customer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; @property (nonatomic, readwrite, copy) NSString *address; ! @end ! ! ! ! @interface HomeViewController : UIViewController ! @property (weak, nonatomic) IBOutlet UILabel *userLabel; @property (weak, nonatomic) IBOutlet UILabel *logoLabel; @property (strong, nonatomic) Customer *user; ! @end @interface MGACustomer : NSObject ! @property (nonatomic, readwrite, copy) NSString *name; @property (nonatomic, readwrite, copy) NSString *address; ! @end ! ! ! ! @interface MGAHomeViewController : UIViewController ! @property (weak, nonatomic) IBOutlet UILabel *userLabel; @property (weak, nonatomic) IBOutlet UILabel *logoLabel; @property (strong, nonatomic) MAGCustomer *user; ! @end

Slide 57

Slide 57 text

use prefixes on all classes ‣ Choose a prefix with at least 3 uppercase chars that are significant for the project ‣ Two-letter prefixes are reserved by Apple for use in framework classes, so you must use 3 (or more) ‣ Objective-C classes must be named uniquely not only within the code that you’re writing in a project, but also across any frameworks or bundles you might be including. As an example, you should avoid using generic class names like ViewController or TextParser because it’s possible a framework you include in your app may fail to follow conventions and create classes with the same names.

Slide 58

Slide 58 text

use a prefix for method names in categories in a category you should include a prefix on the method name to avoid clashes

Slide 59

Slide 59 text

prefix category methods ! @interface NSString (MGAAdditions) ! - (NSString *)reverse; - (NSString *)sha256; ! @end

Slide 60

Slide 60 text

prefix category methods ! @interface NSString (MGAAdditions) ! - (NSString *)reverse; - (NSString *)sha256; ! @end ! @interface NSString (MGAAdditions) ! - (NSString *)mga_reverse; - (NSString *)mga_sha256; ! @end

Slide 61

Slide 61 text

prefix category methods ‣ If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. ‣ In order to avoid undefined behavior, it’s best practice to add a prefix to method names in categories on framework classes, just like you should add a prefix to the names of your own classes. You might choose to use the same three letters you use for your class prefixes, but lowercase to follow the usual convention for method names, then an underscore, before the rest of the method name

Slide 62

Slide 62 text

properly define BOOL property for Boolean properties the getter method should start with is

Slide 63

Slide 63 text

properly define BOOL property ! @interface MGAUser ! @property (nonatomic, assign) BOOL authenticated; ! @end ! @interface MGAUser ! @property (nonatomic, assign, getter = isAuthenticated) BOOL authenticated; ! @end

Slide 64

Slide 64 text

use ~iphone and ~ipad if you’re developing a universal app add tilde to xib name instead of underscore ! !

Slide 65

Slide 65 text

~iphone and ~ipad // MGAHomeViewController_iPhone.xib // MGAHomeViewController_iPad.xib ! @implementation MGAHomeViewController ! - (id)init { NSString *nibName = @"MGAHomeViewController_iPhone"; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { nibName = @"MGAHomeViewController_iPad"; } return [super initWithNibName:nibName bundle:nil]; } ! @end

Slide 66

Slide 66 text

~iphone and ~ipad // MGAHomeViewController_iPhone.xib // MGAHomeViewController_iPad.xib ! @implementation MGAHomeViewController ! - (id)init { NSString *nibName = @"MGAHomeViewController_iPhone"; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { nibName = @"MGAHomeViewController_iPad"; } return [super initWithNibName:nibName bundle:nil]; } ! @end please stop doing that!

Slide 67

Slide 67 text

~iphone and ~ipad // MGAHomeViewController_iPhone.xib // MGAHomeViewController_iPad.xib ! @implementation MGAHomeViewController ! - (id)init { NSString *nibName = @"MGAHomeViewController_iPhone"; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { nibName = @"MGAHomeViewController_iPad"; } return [super initWithNibName:nibName bundle:nil]; } ! @end // MGAHomeViewController~iphone.xib // MGAHomeViewController~ipad.xib ! @implementation MGAHomeViewController ! ! ! ! ! // no custom init required ! ! ! ! ! ! ! @end

Slide 68

Slide 68 text

use ~iphone and ~ipad ‣ Adding ~iphone and ~ipad to xib file name, the runtime will automatically infer the correct xib to load (e.g. MGAHomeViewController~iphone.xib and MGAHomeViewController~ipad.xib) ‣ note, it’s case sensitive so iPhone and iPad, with a capital P, do not work

Slide 69

Slide 69 text

organize #import statements it’s a good practice grouping and commenting #import statements

Slide 70

Slide 70 text

organize #import statements ! #import ! #import "MGAMapViewController.h" #import “MGAPictureView.h" #import “NSString+MGAAdditions.h” ! #import “MGAUser.h" ! #import #import “NSDate+MGAAdditions.h” ! #import “MGAOrderCell.h" #import “MGACustomer.h" #import "UIImage+MGAAdditions.h" ! #import “MGAHomeViewController.h" #import ! #import “MGAUserCell.h"

Slide 71

Slide 71 text

organize #import statements ! #import ! #import "MGAMapViewController.h" #import “MGAPictureView.h" #import “NSString+MGAAdditions.h” ! #import “MGAUser.h" ! #import #import “NSDate+MGAAdditions.h” ! #import “MGAOrderCell.h" #import “MGACustomer.h" #import "UIImage+MGAAdditions.h" ! #import “MGAHomeViewController.h" #import ! #import “MGAUserCell.h" ! // Frameworks #import #import #import ! // View Controllers #import "MGAMapViewController.h" #import “MGAHomeViewController.h” ! // Views #import “MGAPictureView.h” ! // Cells #import “MGAUserCell.h” #import “MGAOrderCell.h” ! // Models #import “MGAUser.h" #import “MGACustomer.h” ! // Categories #import “NSDate+MGAAdditions.h” #import “UIImage+MGAAdditions.h" #import “NSString+MGAAdditions.h”

Slide 72

Slide 72 text

organize #import statements ‣ group #imports ‣ comment the groups ‣ If your are pedantic you can sort #import by length :)

Slide 73

Slide 73 text

use #pragma mark organize your code, visually

Slide 74

Slide 74 text

#pragma mark #pragma mark - View ! - (void)viewDidLoad { [super viewDidLoad]; … } ! - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; … } ! #pragma mark - UIScrollViewDelegate ! - (void)scrollViewDidScroll:(UIScrollView *)scrollView { … } ! - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { … } ! #pragma mark - Actions ! - (IBAction)loginButtonTouched:(id)sender { }

Slide 75

Slide 75 text

#pragma mark

Slide 76

Slide 76 text

#pragma mark ‣ Use #pragma mark in your @implementation to divide code into logical sections ‣ It is also a way to organize your methods in the method list pop up view in Xcode

Slide 77

Slide 77 text

Coding

Slide 78

Slide 78 text

don’t #import in header file most #import statements should only be written in .m files, not .h headers

Slide 79

Slide 79 text

don’t #import in header file #import "MGAUser.h" #import "MGANetworkManager.h" ! @interface MGALoginViewController : UIViewController ! @property (nonatomic, readonly) MGAUser *user; @property (nonatomic, strong, readwrite) MGANetworkManager *network; ! @end ! ! ! ! // MGALoginViewController.m ! @implementation MGALoginViewController ! ! @end

Slide 80

Slide 80 text

don’t #import in header file #import "MGAUser.h" #import "MGANetworkManager.h" ! @interface MGALoginViewController : UIViewController ! @property (nonatomic, readonly) MGAUser *user; @property (nonatomic, strong, readwrite) MGANetworkManager *network; ! @end ! ! ! ! // MGALoginViewController.m ! @implementation MGALoginViewController ! ! @end @class MGAUser; // forward class declaration @class MGANetworkManager; // forward class declaration ! @interface MGALoginViewController : UIViewController ! @property (nonatomic, readonly) MGAUser *user; @property (nonatomic, strong, readwrite) MGANetworkManager *network; ! @end ! ! ! ! // MGALoginViewController.m ! #import "MGAUser.h" #import “MGANetworkManager.h" ! @implementation MGALoginViewController ! ! @end

Slide 81

Slide 81 text

don’t #import in header file ‣ Rather than adding #import statements for each class, it's good practice to use forward class declarations in the header, and import them in the implementation ‣ It reduce compile times and cyclical references ‣ The one real exception is when subclassing another custom class, you'll need to #import its header.

Slide 82

Slide 82 text

use typedef to simplify Block syntax If you need to define more than one block with the same signature, you might like to define your own type for that signature

Slide 83

Slide 83 text

blocks with typedef ! ! ! @interface MGACustomer ! ! ! - (void)createOrder:(void(^)(MGAOrder *order))completion; ! @end

Slide 84

Slide 84 text

blocks with typedef ! ! ! @interface MGACustomer ! ! ! - (void)createOrder:(void(^)(MGAOrder *order))completion; ! @end typedef void(^MGACustomerBlock)(MGAOrder *order); ! ! @interface MGACustomer ! ! ! - (void)createOrder:(void(^)(MGAOrder *order))completion; ! @end

Slide 85

Slide 85 text

blocks with typedef ! ! ! @interface MGACustomer ! ! ! - (void)createOrder:(void(^)(MGAOrder *order))completion; ! @end typedef void(^MGACustomerBlock)(MGAOrder *order); ! ! @interface MGACustomer ! ! ! - (void)createOrder:(void(^)(MGAOrder *order))completion; ! @end typedef void(^MGACustomerBlock)(MGAOrder *order); ! ! @interface MGACustomer ! ! ! - (void)createOrder:(MGACustomerBlock)completion; ! @end

Slide 86

Slide 86 text

blocks with typedef ! ! ! @interface MGACustomer ! ! ! - (void)createOrder:(void(^)(MGAOrder *order))completion; ! @end typedef void(^MGACustomerBlock)(MGAOrder *order); ! ! @interface MGACustomer ! ! ! - (void)createOrder:(void(^)(MGAOrder *order))completion; ! @end typedef void(^MGACustomerBlock)(MGAOrder *order); ! ! @interface MGACustomer ! ! ! - (void)createOrder:(MGACustomerBlock)completion; ! @end typedef void(^MGACustomerBlock)(MGAOrder *order); ! ! @interface MGACustomer ! @property (nonatomic, copy) MGACustomerBlock completionBlock; ! - (void)createOrder:(MGACustomerBlock)completion; ! @end

Slide 87

Slide 87 text

create types for blocks ‣ types improve readability and clean up your method definitions, life will be easier and It’s highly recommend making use of them as much as possible ‣ If you have to change the block signature, it is much easier to change the typedef. The compiler, being a nice fellow, will then tell you all the places the block signature doesn’t match

Slide 88

Slide 88 text

how to access ivars Oh my!

Slide 89

Slide 89 text

how to access ivars ! - (instancetype)initWithUser:(MGAUser *)user { self = [super init]; if (self) { _user = user; } return self; } ! - (void)viewDidLoad { [super viewDidLoad]; [self.usernameLabel setText:self.user.name]; } ! - (void)createNewUser { self.usernameLabel = [THMUser alloc] init]; }

Slide 90

Slide 90 text

how to access ivars ‣ Direct access bypasses the property’s memory- management semantics defined by the setter ‣ KVO notifications would not be fired when accessing the instance variables directly ‣ In init methods you should use direct instance variable access, because subclasses could override the setter ‣ On the other part of class read/write data through properties

Slide 91

Slide 91 text

always declare atomic/nonatomic Properties are declared as atomic by default

Slide 92

Slide 92 text

declare atomic/nonatomic #import ! @interface MGAMapViewController : UIViewController ! @property (readonly) CERUser *user; @property (copy) NSArray *locations; @property (weak) IBOutlet UITableView *tableView; ! @end

Slide 93

Slide 93 text

declare atomic/nonatomic #import ! @interface MGAMapViewController : UIViewController ! @property (readonly) CERUser *user; @property (copy) NSArray *locations; @property (weak) IBOutlet UITableView *tableView; ! @end #import ! @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) CERUser *user; @property (nonatomic, copy) NSArray *locations; @property (nonatomic, weak) IBOutlet UITableView *tableView; ! @end

Slide 94

Slide 94 text

declare atomic/nonatomic #import ! @interface MGAMapViewController : UIViewController ! @property (readonly) CERUser *user; @property (copy) NSArray *locations; @property (weak) IBOutlet UITableView *tableView; ! @end #import ! @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) CERUser *user; @property (nonatomic, copy) NSArray *locations; @property (nonatomic, weak) IBOutlet UITableView *tableView; ! @end #import ! @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) CERUser *user; @property (nonatomic, readwrite, copy) NSArray *locations; @property (nonatomic, readwrite, weak) IBOutlet UITableView *tableView; ! @end

Slide 95

Slide 95 text

declare atomic/nonatomic ‣ By default, synthesized accessors include locking to make them atomic ‣ If you not need locking on property use nonatomic on iOS, since performance is severely impacted if atomic is used

Slide 96

Slide 96 text

use read-only properties read-only properties are great for exposing information, you should use them often

Slide 97

Slide 97 text

read-only properties ! @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) MGAUser *user; @property (nonatomic, readonly) NSArray *locations; ! @end

Slide 98

Slide 98 text

read-only properties ! @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) MGAUser *user; @property (nonatomic, readonly) NSArray *locations; ! @end ! @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) MGAUser *user; @property (nonatomic, readonly) NSArray *locations; ! @end ! ! ! ! ! ! ! ! ! ! @implementation MGAMapViewController ! - (instancetype)initWithUser:(MGaUser *)user { self = [super init]; if (self) { _user = user; } return self; } ! @end

Slide 99

Slide 99 text

read-only properties ! @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) MGAUser *user; @property (nonatomic, readonly) NSArray *locations; ! @end ! @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) MGAUser *user; @property (nonatomic, readonly) NSArray *locations; ! @end ! ! ! ! ! ! ! ! ! ! @implementation MGAMapViewController ! - (instancetype)initWithUser:(MGaUser *)user { self = [super init]; if (self) { _user = user; } return self; } ! @end ! @interface MGAMapViewController : UIViewController ! @property (nonatomic, readonly) MGAUser *user; @property (nonatomic, readonly) NSArray *locations; ! @end ! ! @interface MGAMapViewController () ! @property (nonatomic, strong, readwrite) MGAUser *user; @property (nonatomic, strong, readwrite) NSArray *locations; ! @end ! ! @implementation MGAMapViewController ! - (instancetype)initWithUser:(MGAUser *)user { self = [super init]; if (self) { _user = user; } return self; } ! @end

Slide 100

Slide 100 text

use read-only properties ‣ expose object data with read-only properties, unless you really need to allow access to internal data through a public setter ‣ for read-only properties, you should defined a public getter but a private setter in class extension

Slide 101

Slide 101 text

use weak to avoid retain cycles a wrong memory management involves in memory leaks

Slide 102

Slide 102 text

retain cycle Object B Object A

Slide 103

Slide 103 text

retain cycle Object B @property (strong) Object A

Slide 104

Slide 104 text

retain cycle Object B @property (strong) @property (strong) Object A

Slide 105

Slide 105 text

retain cycle Object B @property (strong) @property (strong) Object A

Slide 106

Slide 106 text

retain cycle Object B @property (strong) @property (strong) Object A

Slide 107

Slide 107 text

retain cycle Object B @property (strong) Object A @property (weak)

Slide 108

Slide 108 text

use __weak typeof a wrong memory management involves in memory leaks

Slide 109

Slide 109 text

use __weak typeof - (void)viewDidLoad { [super viewDidLoad]; self.manager = [[MGANetworkManager alloc] init]; ! ! ! [self.manager getUsers:^(NSArray *users, NSError *error) { if (!error) { self.users = elements; [self.usersTableView reloadData]; } }]; }

Slide 110

Slide 110 text

use __weak typeof - (void)viewDidLoad { [super viewDidLoad]; self.manager = [[MGANetworkManager alloc] init]; ! ! ! [self.manager getUsers:^(NSArray *users, NSError *error) { if (!error) { self.users = elements; [self.usersTableView reloadData]; } }]; }

Slide 111

Slide 111 text

use __weak typeof - (void)viewDidLoad { [super viewDidLoad]; self.manager = [[MGANetworkManager alloc] init]; ! ! ! [self.manager getUsers:^(NSArray *users, NSError *error) { if (!error) { self.users = elements; [self.usersTableView reloadData]; } }]; }

Slide 112

Slide 112 text

use __weak typeof - (void)viewDidLoad { [super viewDidLoad]; self.manager = [[MGANetworkManager alloc] init]; ! ! ! [self.manager getUsers:^(NSArray *users, NSError *error) { if (!error) { self.users = elements; [self.usersTableView reloadData]; } }]; } - (void)viewDidLoad { [super viewDidLoad]; self.manager = [[MGANetworkManager alloc] init]; ! __weak THMReportsViewController weakSelf = self; ! [weakSelf.manager getUsers:^(NSArray *users, NSError *error) { if (!error) { weakSelf.users = elements; [weakSelf.usersTableView reloadData]; } }]; }

Slide 113

Slide 113 text

use __weak typeof - (void)viewDidLoad { [super viewDidLoad]; self.manager = [[MGANetworkManager alloc] init]; ! ! ! [self.manager getUsers:^(NSArray *users, NSError *error) { if (!error) { self.users = elements; [self.usersTableView reloadData]; } }]; } - (void)viewDidLoad { [super viewDidLoad]; self.manager = [[MGANetworkManager alloc] init]; ! __weak THMReportsViewController weakSelf = self; ! [weakSelf.manager getUsers:^(NSArray *users, NSError *error) { if (!error) { weakSelf.users = elements; [weakSelf.usersTableView reloadData]; } }]; } - (void)viewDidLoad { [super viewDidLoad]; self.manager = [[MGANetworkManager alloc] init]; ! __weak typeof(self) weakSelf = self; ! [weakSelf.manager getUsers:^(NSArray *users, NSError *error) { if (!error) { weakSelf.users = elements; [weakSelf.usersTableView reloadData]; } }]; }

Slide 114

Slide 114 text

Manage third-part libraries with CocoaPods it’s an application level dependency manager for the Objective-C projects

Slide 115

Slide 115 text

Cocoapods $ sudo gem install cocoapods ! $ cd MyGreatApp ! $ pod init ! $ edit Podfile

Slide 116

Slide 116 text

Cocoapods $ sudo gem install cocoapods ! $ cd MyGreatApp ! $ pod init ! $ edit Podfile platform :ios, '6.1' pod 'RestKit', '~> 0.22.0' pod 'FXKeychain', '~> 1.5' pod 'SDWebImage', '~> 3.5.2' pod 'SDWebImage-ProgressView', '~> 0.3.0' pod 'RHAddressBook', '~> 1.1.1' pod 'FormatterKit', '~> 1.4.2'

Slide 117

Slide 117 text

Cocoapods $ sudo gem install cocoapods ! $ cd MyGreatApp ! $ pod init ! $ edit Podfile platform :ios, '6.1' pod 'RestKit', '~> 0.22.0' pod 'FXKeychain', '~> 1.5' pod 'SDWebImage', '~> 3.5.2' pod 'SDWebImage-ProgressView', '~> 0.3.0' pod 'RHAddressBook', '~> 1.1.1' pod 'FormatterKit', '~> 1.4.2' $ pod install Analyzing dependencies ! Downloading dependencies Using AFNetworking (1.3.3) Using FXKeychain (1.5) Using FormatterKit (1.4.2) Using ISO8601DateFormatterValueTransformer (0.5.0) Using RHAddressBook (1.1.1) Using RKValueTransformers (1.0.1) Using RestKit (0.22.0) Using SDWebImage (3.5.4) Using SDWebImage-ProgressView (0.3.1) Using SOCKit (1.1) Using TransitionKit (2.0.0) Generating Pods project Integrating client project ! $

Slide 118

Slide 118 text

Cocoapods ‣ CocoaPods manage dependency for you, it download source files, imports headers and configures flags ‣ CocoaPods is strongly inspired by a combination of the Ruby projects RubyGems and Bundler ‣ CocoaPods focuses on source-based distribution of third party code and automatic integration into Xcode projects ‣ CocoaPods runs from the command line

Slide 119

Slide 119 text

Framework

Slide 120

Slide 120 text

Key-Value Coding dynamically set and get properties and object graph

Slide 121

Slide 121 text

Key-Value Coding MGACustomer *c1 = [[MGACustomer alloc] initWithName:@“ACME Inc.”]; c1.address = [[MGAAddress alloc] initWithZip:@“33568”]; ! ! [c1 valueForKey:@"name"]; // ACME Inc. [c1 valueForKeyPath:@“address.zip"]; // 33568

Slide 122

Slide 122 text

Key-Value Coding MGACustomer *c1 = [[MGACustomer alloc] initWithName:@“ACME Inc.”]; c1.address = [[MGAAddress alloc] initWithZip:@“33568”]; ! ! [c1 valueForKey:@"name"]; // ACME Inc. [c1 valueForKeyPath:@“address.zip"]; // 33568 NSArray *names = @[ @"Steve", @"Bill", @"Ive" ]; ! // you can “get the value” of any method with no parameter [names valueForKey:@"uppercaseString"]; // @[ @“STEVE”, @“BILL”, @“IVE”] [names valueForKey:@"lowercaseString"]; // @[ @“steve”, @“bill”, @“ive”] ! ! // It’s really any method. // For example, -[NSObject self] is a method like any other. [@"Steve" valueForKey:@“self"]; // @“Steve” ! ! // KVC automatically boxes and unboxes values [names valueForKey:@"length"]; // @[ @5, @4, @3 ]

Slide 123

Slide 123 text

Key-Value Coding ‣ Key-value coding is a mechanism for accessing an object’s properties indirectly, using strings to identify properties ‣ You can get the value of any method with no parameter ‣ Key-Value Coding automatically boxes and unboxes values into their object representation.

Slide 124

Slide 124 text

discover KVC Collection Operators save a few extra lines of code

Slide 125

Slide 125 text

KVC Collection Operators MGAPerson *p1 = [[MGAPerson alloc] initWithName:@"Max" age:34]; MGAPerson *p2 = [[MGAPerson alloc] initWithName:@"Steve" age:54]; MGAPerson *p3 = [[MGAPerson alloc] initWithName:@"Bill" age:62]; ! NSArray *people = @[ p1, p2, p3 ]; ! ! // Simple Collection Operators [people valueForKeyPath:@"@count"]; // 3 [people valueForKeyPath:@"@sum.age"]; // 150.00 [people valueForKeyPath:@"@avg.age"]; // 50.00 [people valueForKeyPath:@"@max.age"]; // 63.00 ! ! // Objects Operators [people valueForKeyPath:@"@unionOfObjects.name"]; // "Max", "Steve", “Bill" ! ! // Simple Collection Operators with NSNumber NSArray *numbers = @[ @(1), @(2), @(3) ]; ! [numbers valueForKeyPath:@"@max.self"]; // 3

Slide 126

Slide 126 text

KVC Collection Operators ‣ Collection operators allow actions to be performed on the items of a collection using key path notation and an action operator ‣ Collection operators are specialized key paths that are passed as the parameter to the valueForKeyPath: method ‣ The operator is specified by a string preceded by an at sign (@)

Slide 127

Slide 127 text

discover NSBlockOperation class

Slide 128

Slide 128 text

NSBlockOperation NSBlockOperation *blockOp = [[NSBlockOperation alloc] init]; ! __weak typeof(blockOp) weakOp = blockOp; ! [blockOp addExecutionBlock:^ { for (NSInteger i = 0; i < 10000; i++) { if ([weakOp isCancelled]) { break; } NSLog(@"%i", i); } }]; ! [blockOp start];

Slide 129

Slide 129 text

NSBlockOperation ‣ NSBlockOperation is a concrete subclass that wraps block in operations ‣ You can use this object to execute several blocks at once without having to create separate operation objects for each ‣ When executing more than one block, the operation itself is considered finished only when all blocks have finished executing

Slide 130

Slide 130 text

NSError it encapsulates richer and more extensible error information

Slide 131

Slide 131 text

Create an NSError NSString * const kMGAErrorDomain = @“com.acmeinc.MyGreatApp”; ! ! typedef NS_ENUM(NSInteger, MGAErrorCode) { MGAErrorCodeUnableToLocateUser = 500, MGAErrorCodeInvalidUser = 401 }; ! ! NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedString(@“Location error”, nil), ! NSLocalizedFailureReasonErrorKey : NSLocalizedString(@"The location service is not active.", nil), ! NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"Please turn on the location service and try again", nil) }; ! NSError *error = [NSError errorWithDomain:kMGAErrorDomain code:MGAErrorCodeUnableToLocateUser userInfo:userInfo];

Slide 132

Slide 132 text

Handle NSError - (void)handleError:(NSError *)error { if (error) { NSString *errorMessage; errorMessage = [NSString stringWithFormat:@"%@\n%@", error.localizedFailureReason, error.localizedRecoverySuggestion]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:error.localizedDescription message:errorMessage delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil, nil]; [alert show]; } }

Slide 133

Slide 133 text

Consuming NSError - (void)doLogin { NSString *user = self.usernameTextField.text; NSString *pass = self.passwordTextField.text; __weak typeof(self) weakSelf = self; [self.service loginWithUser:user pass:pass completion:^(TMGAUser *user, NSError *error) { [weakSelf handleError:error]; if (!error) { [weakSelf navigateToHome]; } }]; }

Slide 134

Slide 134 text

NSError ‣ An NSError object encapsulates richer and more extensible error information than is possible using only an error code or error string ‣ The core attributes of an NSError object are an error domain, a domain-specific error code and a user info dictionary containing application specific information. ‣ Domain-specific error codes are generally defined by constants in an enum.

Slide 135

Slide 135 text

use NSCache It’s a collection-like container, or cache, that stores key-value pairs, similar to the NSMutableDictionary class.

Slide 136

Slide 136 text

NSCache NSCache *cache = [[NSCache alloc] init]; ! ! // Sets the maximum number of objects that the cache can hold. ! // This limit is not a strict limit, and if the cache goes over the limit, // an object in the cache could be evicted instantly, later, or possibly never, // all depending on the implementation details of the cache. [cache setCountLimit:50]; ! ! // Sets the maximum total cost that the cache can have before it starts evicting objects ! // If adding this object to the cache causes the cache’s total cost to rise above // totalCostLimit, the cache could automatically evict some of its objects until its // total cost falls below totalCostLimit. The order in which the cache evicts objects is // not guaranteed. This limit is not a strict limit, and if the cache goes over the // limit, an object in the cache could be evicted instantly, at a later point in time, // or possibly never, all depending on the implementation details of the cache. [cache setTotalCostLimit:5000]; ! ! // Add object to cache with cost [cache setObject:customer1 forKey:@"c1" cost:10]; ! ! // Add object to cache without cost [cache setObject:customer2 forKey:@"c2"];

Slide 137

Slide 137 text

NSCache ‣ NSCache is basically just an NSMutableDictionary that automatically evicts objects in order to free up space in memory as needed ‣ The NSCache class incorporates various auto-removal policies, which ensure that it does not use too much of the system’s memory. The system automatically carries out these policies if memory is needed by other applications. When invoked, these policies remove some items from the cache, minimizing its memory footprint.

Slide 138

Slide 138 text

NSCache ‣ It is guaranteed to be thread-safe. ‣ It is much slower to access. ‣ It may throw out objects from time to time. You can set costs and limits, but they're not guaranteed to be followed. ‣ It is not toll-free bridged to anything in CoreFoundation. ‣ You can't query the number of objects that are in the cache. ‣ You can't enumerate a cache.

Slide 139

Slide 139 text

NSValue NSValue is a simple container for a single C or Objective-C data value

Slide 140

Slide 140 text

NSValue NSMutableArray *items = [@[] mutableCopy]; ! ! ! [items addObject:[NSValue valueWithCGSize:CGSizeMake(200.0f, 300.0f)]]; ! NSValue *sizeValue = items[0]; CGSize size = [sizeValue CGSizeValue]; ! ! ! ! ! // define new struct typedef struct MGAColor { float red, blue, green; } MGAColor; ! MGAColor color = { 255.0, 0.0f, 34.0 }; ! [items addObject:[NSValue valueWithBytes:&color objCType:@encode(MGAColor)]];

Slide 141

Slide 141 text

NSValue NSMutableArray *items = [@[] mutableCopy]; ! ! ! [items addObject:[NSValue valueWithCGSize:CGSizeMake(200.0f, 300.0f)]]; ! NSValue *sizeValue = items[0]; CGSize size = [sizeValue CGSizeValue]; ! ! ! ! ! // define new struct typedef struct MGAColor { float red, blue, green; } MGAColor; ! MGAColor color = { 255.0, 0.0f, 34.0 }; ! [items addObject:[NSValue valueWithBytes:&color objCType:@encode(MGAColor)]]; MGAUser *user = [[MGAUser alloc] init]; // it not conforms to NSMutableDictionary *items = [@{} mutableCopy]; // e.g. I want to store num of login attempts of a user // because my custom class MGAUser does not conform to // I can’t use it as key value. The workaround is to box user instance // into NSValue object with valueWithNonretainedObject: [items setObject:numLogin forKey:[NSValue valueWithNonretainedObject:user]];

Slide 142

Slide 142 text

NSValue ‣ It can hold scalars and value types, as well as pointers and object IDs. ‣ NSValue uses type encoding to create the necessary data structures to represent values internally ‣ valueWithNonretainedObject: allows objects to be added to a collection, without the need for satisfying protocol

Slide 143

Slide 143 text

Thanks! Massimo Oliviero @maxoly