Pragma Night @ Talent Garden
Modern Objective-C
Giuseppe Arici
Pragma Night
It’s All About ...
Syntactic Sugar
Pragma Night
Unordered Method Declarations
Pragma Night
Public & Private Method Ordering
@interface SongPlayer : NSObject- (void)playSong:(Song *)song;
@end
@implementation SongPlayer- (void)playSong:(Song *)song { NSError *error; [self startAudio:&error]; /* ... */}
- (void)startAudio:(NSError **)error { /* ... */ }@endWarning:
instance method '-startAudio:' not found (return type defaults to 'id')
Pragma Night
Wrong Workaround
@interface SongPlayer : NSObject- (void)playSong:(Song *)song;- (void)startAudio:(NSError **)error;@end
@implementation SongPlayer- (void)playSong:(Song *)song { NSError *error; [self startAudio:&error]; /* ... */}
- (void)startAudio:(NSError **)error { /* ... */ }@end
In the public interface
Pragma Night
Okay Workaround 1
@interface SongPlayer ()
- (void)startAudio:(NSError **)error;@end
@implementation SongPlayer- (void)playSong:(Song *)song { NSError *error; [self startAudio:&error]; /* ... */}
- (void)startAudio:(NSError **)error { /* ... */ }@end
In a class extension
Pragma Night
Okay Workaround 2
@interface SongPlayer : NSObject- (void)playSong:(Song *)song;
@end
@implementation SongPlayer- (void)startAudio:(NSError **)error { /* ... */ }- (void)playSong:(Song *)song { NSError *error; [self startAudio:&error]; /* ... */}
@end
Reorder methods
Pragma Night
Best Solution
@interface SongPlayer : NSObject- (void)playSong:(Song *)song;
@end
@implementation SongPlayer- (void)playSong:(Song *)song { NSError *error; [self startAudio:&error]; /* ... */}
- (void)startAudio:(NSError **)error { /* ... */ }@end
Parse the @implementation declarations then bodies
Xcode 4.4+
Pragma Night
Enum with Fixed Underlying Type
Pragma Night
Enum with Indeterminate Type
typedef enum { NSNumberFormatterNoStyle, NSNumberFormatterDecimalStyle, NSNumberFormatterCurrencyStyle, NSNumberFormatterPercentStyle, NSNumberFormatterScientificStyle, NSNumberFormatterSpellOutStyle} NSNumberFormatterStyle;
//typedef int NSNumberFormatterStyle;
Before OS X v10.5 and iOS
Pragma Night
Enum with Explicit Type
enum { NSNumberFormatterNoStyle, NSNumberFormatterDecimalStyle, NSNumberFormatterCurrencyStyle, NSNumberFormatterPercentStyle, NSNumberFormatterScientificStyle, NSNumberFormatterSpellOutStyle};
typedef NSUInteger NSNumberFormatterStyle;
After OS X v10.5 and iOS
• Pro: 32-bit and 64-bit portability
• Con: no formal relationship between type and enum constants
Pragma Night
Enum with Fixed Underlying Type
typedef enum NSNumberFormatterStyle : NSUInteger { NSNumberFormatterNoStyle, NSNumberFormatterDecimalStyle, NSNumberFormatterCurrencyStyle, NSNumberFormatterPercentStyle, NSNumberFormatterScientificStyle, NSNumberFormatterSpellOutStyle} NSNumberFormatterStyle;
LLVM 4.2+ Compiler
• Stronger type checking
• Better code completion
Xcode 4.4+
Pragma Night
Enum with Fixed Underlying Type
typedef NS_ENUM(NSUInteger, NSNumberFormatterStyle) { NSNumberFormatterNoStyle, NSNumberFormatterDecimalStyle, NSNumberFormatterCurrencyStyle, NSNumberFormatterPercentStyle, NSNumberFormatterScientificStyle, NSNumberFormatterSpellOutStyle};
NS_ENUM macro
• Foundation declares like this
Xcode 4.4+
Pragma Night
Enum with Fixed Underlying Type
NSNumberFormatterStyle style = NSNumberFormatterRoundUp; // 3
Stronger type checking (-Wenum-conversion)
warning:implicit conversion from enumeration type 'enum NSNumberFormatterRoundingMode' to different enumeration type 'NSNumberFormatterStyle' (aka 'enum NSNumberFormatterStyle')
Pragma Night
Enum with Fixed Underlying Type
- (void) printStyle:(NSNumberFormatterStyle) style{ switch (style) { case NSNumberFormatterNoStyle: break; case NSNumberFormatterSpellOutStyle: break; }}
Handling all enum values (-Wswitch)
warning: 4 enumeration values not handled in switch: 'NSNumberFormatterDecimalStyle', 'NSNumberFormatterCurrencyStyle', 'NSNumberFormatterPercentStyle'...
Pragma Night
@Synthesize by Default
Pragma Night
Properties Simplify Classes
@interface Person : NSObject { NSString *_name;}@property(strong) NSString *name;@end
@implementation Person
@synthesize name = _name;
@end
@interface instance variables
Pragma Night
Properties Simplify Classes
@interface Person : NSObject
@property(strong) NSString *name;@end
@implementation Person { NSString *_name;}
@synthesize name = _name;
@end
@implementation instance variables
Pragma Night
Properties Simplify Classes
@interface Person : NSObject
@property(strong) NSString *name;@end
@implementation Person
@synthesize name = _name;
@end
Synthesized instance variables
Pragma Night
@Synthesize by Default
@interface Person : NSObject
@property(strong) NSString *name;@end
@implementation Person
@end
LLVM 4.2+ Compiler
Xcode 4.4+
Pragma Night
Instance Variable Name !?
@interface Person : NSObject
@property(strong) NSString *name;@end
@implementation Person- (NSString *)description { return _name;}/* as if you'd written: @synthesize name = _name; */
@end
Instance variables now prefixed with “_”
Xcode 4.4+
Pragma Night
Backward Compatibility !?
@interface Person : NSObject
@property(strong) NSString *name;@end
@implementation Person
@synthesize name;/* as if you'd written: @synthesize name = name; */@end
Be careful, when in doubt be fully explicit
Pragma Night
To ⇒ @Synthesize by Default
• Warning: @Synthesize by Default will not synthesize a property declared in a protocol
• If you use custom instance variable naming convention, enable this warning( -Wobjc-missing-property-synthesis )
Pragma Night
Core Data NSManagedObject
/* NSManagedObject.h */
NS_REQUIRES_PROPERTY_DEFINITIONS@interface NSManagedObject : NSObject {
Opts out of synthesize by default
• NSManagedObject synthesizes properties
• Continue to use @property to declare typed accessors
• Continue to use @dynamic to inhibit warnings
Pragma Night
NSNumbers Literals
Pragma Night
NSNumber Creation
NSNumber *value;
value = [NSNumber numberWithChar:'X'];
value = [NSNumber numberWithInt:42];
value = [NSNumber numberWithUnsignedLong:42ul];
value = [NSNumber numberWithLongLong:42ll];
value = [NSNumber numberWithFloat:0.42f];
value = [NSNumber numberWithDouble:0.42];
value = [NSNumber numberWithBool:YES];
Pragma Night
NSNumber Creation
NSNumber *value;
value = @'X';
value = @42;
value = @42ul;
value = @42ll;
value = @0.42f;
value = @0.42;
value = @YES;
Xcode 4.4+
Pragma Night
Backward Compatibility !?
#define YES (BOOL)1 // Before iOS 6, OSX 10.8
#define YES ((BOOL)1) // After iOS 6, OSX 10.8
@(YES) // Use parentheses around BOOL Macros
Workarounds
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000#if __has_feature(objc_bool)#undef YES#undef NO#define YES __objc_yes#define NO __objc_no#endif#endif
// Redefine BOOL Macros
Pragma Night
Boxed Expression Literals
Pragma Night
Boxed Expression Literals
NSNumber *orientation = [NSNumber numberWithInt:UIDeviceOrientationPortrait];
NSNumber *piOverSixteen = [NSNumber numberWithDouble:( M_PI / 16 )]; NSNumber *parityDigit = [NSNumber numberWithChar:"EO"[i % 2]]; NSString *path = [NSString stringWithUTF8String:getenv("PATH")];
NSNumber *usesCompass = [NSNumber numberWithBool: [CLLocationManager headingAvailable]];
Pragma Night
Boxed Expression Literals
NSNumber *orientation = @( UIDeviceOrientationPortrait );
NSNumber *piOverSixteen = @( M_PI / 16 ); NSNumber *parityDigit = @( "OE"[i % 2] ); NSString *path = @( getenv("PATH") );
NSNumber *usesCompass = @( [CLLocationManager headingAvailable] );
Xcode 4.4+
Pragma Night
Array Literals
Pragma Night
Array Creation
More choices, and more chances for errors
NSArray *array;
array = [NSArray array];
array = [NSArray arrayWithObject:a];
array = [NSArray arrayWithObjects:a, b, c, nil];
id objects[] = { a, b, c };NSUInteger count = sizeof(objects) / sizeof(id);array = [NSArray arrayWithObjects:objects count:count];
Pragma Night
// if you write:id objects[] = { nil, @"hello", @42 };NSUInteger count = sizeof(objects)/ sizeof(id);NSArray *array = [NSArray arrayWithObjects:objects count:count];
Nil Termination
// if you write:id a = nil, b = @"hello", c = @42;NSArray *array = [NSArray arrayWithObjects:a, b, c, nil];
Inconsistent behavior
Array will be empty
Exception: attempt to insert nil object from objects[0]
Pragma Night
Array Creation
NSArray *array;
array = [NSArray array];
array = [NSArray arrayWithObject:a];
array = [NSArray arrayWithObjects:a, b, c, nil];
id objects[] = { a, b, c };NSUInteger count = sizeof(objects) / sizeof(id);array = [NSArray arrayWithObjects:objects count:count];
Pragma Night
Array Creation
NSArray *array;
array = @[];
array = @[ a ];
array = @[ a, b, c ];
array = @[ a, b, c ];
Xcode 4.4+
Pragma Night
// compiler generates:
id objects[] = { a, b, c };NSUInteger count = sizeof(objects)/ sizeof(id);NSArray *array = [NSArray arrayWithObjects:objects count:count];
How Array Literals Work
// when you write this:
NSArray *array = @[ a, b, c ];
Pragma Night
Dictionary Literals
Pragma Night
NSDictionary *dict;
dict = [NSDictionary dictionary];
dict = [NSDictionary dictionaryWithObject:o1 forKey:k1];
dict = [NSDictionary dictionaryWithObjectsAndKeys: o1, k1, o2, k2, o3, k3, nil];
id objects[] = { o1, o2, o3 };id keys[] = { k1, k2, k3 };NSUInteger count = sizeof(objects) / sizeof(id);dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:count];
Dictionary Creation
More choices, and more chances for errors
Pragma Night
Dictionary Creation
NSDictionary *dict;
dict = [NSDictionary dictionary];
dict = [NSDictionary dictionaryWithObject:o1 forKey:k1];
dict = [NSDictionary dictionaryWithObjectsAndKeys: o1, k1, o2, k2, o3, k3, nil];
id objects[] = { o1, o2, o3 };id keys[] = { k1, k2, k3 };NSUInteger count = sizeof(objects) / sizeof(id);dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:count];
Pragma Night
Dictionary Creation
NSDictionary *dict;
dict = @{};
dict = @{ k1 : o1 }; // key before object
dict = @{ k1 : o1, k2 : o2, k3 : o3 };
dict = @{ k1 : o1, k2 : o2, k3 : o3 };
Xcode 4.4+
Pragma Night
// compiler generates:
id objects[] = { o1, o2, o3 };id keys[] = { k1, k2, k3 };NSUInteger count = sizeof(objects) / sizeof(id);NSDictionary *dict = [NSDictionary dictionaryWithObjects:objects orKeys:keys count:count];
How Dictionary Literals Work
// when you write this:
NSDictionary *dict = @{ k1 : o1, k2 : o2, k3 : o3 };
Pragma Night
static NSArray *thePragmers;
+ (void)initialize { if (self == [MyClass class]) { thePragmers = @[ @"Fra", @"Giu", @"Mat", @"Max", @"Ste" ]; }}
Container Literals Restriction
NSMutableArray *mutablePragmers = [@[ @"Fra", @"Giu", @"Mat", @"Max", @"Ste" ] mutableCopy];
All containers are immutable, mutable use: -mutableCopy
For constant containers, simply implement +initialize
Pragma Night
Object Subscripting
Pragma Night
Array Subscripting
New syntax to access object at index: nsarray[index]
@implementation SongList { NSMutableArray *_songs;}
- (Song *)replaceSong:(Song *)newSong atIndex:(NSUInteger)idx { Song *oldSong = [_songs objectAtIndex:idx]; [_songs replaceObjectAtIndex:idx withObject:newSong]; return oldSong;}@end
Pragma Night
Array Subscripting
New syntax to access object at index: nsarray[index]
@implementation SongList { NSMutableArray *_songs;}
- (Song *)replaceSong:(Song *)newSong atIndex:(NSUInteger)idx { Song *oldSong = _songs[idx]; _songs[idx] = newSong; return oldSong;}@end
Xcode 4.4+
Pragma Night
Dictionary Subscripting
New syntax to access object by key: nsdictionary[key]
@implementation Database{ NSMutableDictionary *_storage;}
- (id)replaceObject:(id)newObject forKey:(id <NSCopying>)key{ id oldObject = [_storage objectForKey:key]; [_storage setObject:newObject forKey:key]; return oldObject;}@end
Pragma Night
Dictionary Subscripting
New syntax to access object by key: nsdictionary[key]
@implementation Database{ NSMutableDictionary *_storage;}
- (id)replaceObject:(id)newObject forKey:(id <NSCopying>)key{ id oldObject = _storage[key]; _storage[key] = newObject; return oldObject;}@end
Xcode 4.4+
Pragma Night
- (elementType)objectForKeyedSubscript:(keyType)key;
- (void)setObject:(elementType)obj forKeyedSubscript:(keyType)key;
How Subscripting Works
- (elementType)objectAtIndexedSubscript:(indexType)idx
- (void)setObject:(elementType)obj atIndexedSubscript:(indexType)idx;
Array Style: Indexed subscripting methods
elementType must be an object pointer, indexType must be integral
elementType and keyType must be an object pointer
Dictionary Style: Keyed subscripting methods
iOS 6 OSX 10.8
iOS 6 OSX 10.8
Pragma Night
Indexed Subscripting
setObject:atIndexedSubscript:
@implementation SongList { NSMutableArray *_songs;}
- (Song *)replaceSong:(Song *)newSong atIndex:(NSUInteger)idx { Song *oldSong = _songs[idx]; _songs[idx] = newSong; return oldSong;}@end
Pragma Night
Indexed Subscripting
setObject:atIndexedSubscript:
@implementation SongList { NSMutableArray *_songs;}
- (Song *)replaceSong:(Song *)newSong atIndex:(NSUInteger)idx { Song *oldSong = _songs[idx]; [_songs setObject:newSong atIndexedSubscript:idx]; return oldSong;}@end
Pragma Night
Keyed Subscripting
setObject:atIndexedSubscript:
@implementation Database{ NSMutableDictionary *_storage;}
- (id)replaceObject:(id)newObject forKey:(id <NSCopying>)key{ id oldObject = _storage[key]; _storage[key] = newObject; return oldObject;}@end
Pragma Night
Keyed Subscripting
setObject:atIndexedSubscript:
@implementation Database{ NSMutableDictionary *_storage;}
- (id)replaceObject:(id)newObject forKey:(id <NSCopying>)key{ id oldObject = _storage[key]; [_storage setObject:newObject forKey:key]; return oldObject;}@end
Pragma Night
Backward Compatibility !?
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000@interface NSDictionary(BCSubscripting)- (id)objectForKeyedSubscript:(id)key;@end
@interface NSMutableDictionary(BCSubscripting)- (void)setObject:(id)obj forKeyedSubscript:(id )key;@end
@interface NSArray(BCSubscripting)- (id)objectAtIndexedSubscript:(NSUInteger)idx;@end
@interface NSMutableArray(BCSubscripting)- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;@end#endif
To deploy back to iOS 5 and iOS 4 you need ARCLite: use ARC or set explicit linker flag: “-fobjc-arc”
To make compiler happy, you should add 4 categories:
Pragma Night
Your Classes Can Be Subscriptable
@interface SongList : NSObject- (Song *)objectAtIndexedSubscript:(NSUInteger)idx;- (void) setObject:(Song *)song atIndexedSubscript:(NSUInteger)idx;@end
@implementation SongList { NSMutableArray *_songs;}- (Song *)objectAtIndexedSubscript:(NSUInteger)idx { return (Song *)_songs[idx];}- (void) setObject:(Song *)song atIndexedSubscript:(NSUInteger)idx { _songs[idx] = song;}@end
Pragma Night
Summary
Pragma Night
AvailabilityFeature Xcode 4.4+ iOS 6 OSX 10.8
Unordered Method Declarations ✓Enum With Fixed Underlying Type ✓
@Synthesize by Default ✓NSNumbers Literals ✓
Boxed Expression Literals ✓Array Literals ✓
Dictionary Literals ✓Object Subscripting ✓ ✓
* Partially Required
*
Pragma Night
Migration
Apple provides a migration tool which is build into Xcode: Edit ⇒ Refactor ⇒ Convert to Modern ...
Pragma Night
Demo
Pragma Night
Modern Objective-C References
• Clang: Objective-C Literals
• Apple: Programming with Objective-C
• WWDC 2012 – Session 405 – Modern Objective-C
• Mike Ash: Friday Q&A 2012-06-22: Objective-C Literals
Pragma Night
Modern Objective-C Books