Edited at

Modern Objective-C ビフォーアフター

More than 5 years have passed since last update.

Adopting Modern Objective-Cより


instancetype

instancetype の方がコンパイラのチェックがきく。


before

@interface MyObject

- (id)myFactoryMethod;
@end


after

@interface MyObject

- (instancetype)myFactoryMethod;
@end


Enumeration Macros

iOS6 SDKから追加されたマクロ。


before

enum {

UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;


after

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {

UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};

bitmaskは NS_OPTIONS を使う


before

enum {

UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};typedef NSUInteger UIViewAutoresizing;


after

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {

UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5};


Migrating to Modern Objective-Cより


Importing Headers


before

#import <Foundation/NSObject.h>

#import <Foundation/NSString.h> //or @class NSString;


after

#import <Foundation/Foundation.h>


--


Accessor Methods

常にアクセサメソッドを使おう。ただしinitializerやdealloc内は除く。


before

- (void)myMethod {

// ...
[self setTitle:[NSString stringWithFormat:@"Area: %1.2f", [self area]]];
// ...
}


after

- (void)myMethod {

// ...
self.title = [NSString stringWithFormat:@"Area: %1.2f", self.area];
// ...
}

--


Memory Management

ARCを使う。ARCはXcode 4.2(LLVM compiler 3.0)からサポート、一部の動作がコンパイラだけで解決できないためiOS5以上が必要になる。


before

NSMutableArray *array = [[NSMutableArray alloc] init];

// Use the array
[array release];

// or

NSMutableArray *array = [[[NSMutableArray alloc] init] autoelease];
// Use the array



after

NSMutableArray *array = [NSMutableArray array];

// Use the array

--


before

id heisenObject = [[array objectAtIndex:n] retain];

[array removeObjectAtIndex:n];
// ...
[heisenObject doSomething];
[heisenObject release];
// ...


after

id heisenObject = [array objectAtIndex:n];

[array removeObjectAtIndex:n];
// ...
[heisenObject doSomething];
// ...



before

- (void)takeLastNameFrom:(Person *)person {

NSString *oldLastname = [[self lastName] retain];
[self setLastName:[person lastName]];
NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);
[oldLastName release];
}


after

- (void)takeLastNameFrom:(Person *)person {

NSString *oldLastname = [self lastName];
[self setLastName:[person lastName]];
NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);
}

--


before

CFUUIDRef cfUUID = CFUUIDCreate(NULL);

NSString *noteUUID = (NSString *)CFUUIDCreateString(NULL, cfUUID);
CFRelease(cfUUID);


after

CFUUIDRef cfUUID = CFUUIDCreate(NULL);

NSString *noteUUID = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, cfUUID);
CFRelease(cfUUID);

--


Properties

property宣言、実装ではsynthesizeを使う。インスタンス変数に直接アクセスしたい場合は@synthesize title = _title;にして_title変数を使う。この時_titleは宣言しなくてもコンパイラが解決する。また@synthesize自体を省略することも可能(Xcode 4.4 - Apple LLVM 4.0 Compilerから)。

一つの考え方として変数 _title を参照用、プロパティ self.title = ... (setter) を更新用に使うという方法がある。(initializerやdeallocは除く)


before

@interface Thing : NSObject {

NSString *title;
}
- (NSString *)title;
- (void)setTitle:(NSString *)newTitle;
@end

@implementation Thing
- (NSString *)title {
return title;
}
- (void)setTitle:(NSString *)newTitle {
if (title != newTitle) {
[title release];
title = [newTitle copy];
}
}
@end



after

@interface Thing : NSObject 

@property (copy) NSString *title;
@end

@implementation Thing
@synthesize title;
@end




before

@interface Thing : NSObject {

float radius;
}
- (float)radius;
- (void)setRadius:(float)newValue;
- (float)area;
@end

@implementation Thing
- (float)radius {
return radius;
}
- (void)setRadius:(float)newValue {
radius = newValue;
}
- (float)area {
return M_PI * pow(radius, 2.0);
}
@end



after

@interface Thing : NSObject

@property float radius;
@property (readonly, nonatomic) float area;
@end

@implementation Thing
@synthesize radius;
- (float)area {
return M_PI * pow(self.radius, 2.0)
;}
@end




before

@interface Thing : NSObject {

id delegate;
}
- (id)delegate;
- (void)setDelegate:(id)newDelegate;
@end

@implementation Thing
- (id)delegate {
return delegate;
}
- (void)setDelegate:(id)newDelegate {
delegate = newDelegate;
}
@end



after

@interface Thing : NSObject

@property (weak) id delegate;
@end

@implementation Thing
@synthesize delegate;
@end



Private State

非公開な変数はクラスエクステンション内でpropertyとして宣言する。メソッドも同様にクラスエクステンションを使う。


before

@interface Thing {

BOOL privateTest;
}


after

@interface Thing ()

@property BOOL privateTest;
@end
// ...



before

@interface Thing (PrivateMethods)

- (void)doSomethingPrivate;
- (void)doSomethingElsePrivate;
@end


after

@interface Thing ()

- (void)doSomethingPrivate;
- (void)doSomethingElsePrivate;
@end


Outlets

メモリ管理のためにstrongかweakかをproperty宣言する。


before

@interface MyViewController : MySuperclass {

IBOutlet ElementClass *uiElement;
}
@end


after

@interface MyViewController : MySuperclass

@property (weak) IBOutlet ElementClass *uiElement;
@end


Initializer Methods and dealloc

初期化と解放はアクセサじゃなくて変数で。


before

- (id)init {

if (self = [super init]) {
[self setTitle:@"default"];
}
return self;
}


after

- (id)init {

self = [super init];
if (self) {
_title = @"default";
}
return self;
}



before

- (void)dealloc {

[self setTitle:nil];
[super dealloc];
}


after

- (void)dealloc {

[_title release];
[super dealloc];
}


Protocols

必須ではない実装していないメソッドがあるためにidをそのまま使ったりせずにoptional宣言を使ってid型にもちゃんとprotocolを指定する。


before

@ButlerProtocol

- (void)makeTea;
- (void)serveSandwiches;
- (void)mowTheLawn;
@end


after

@protocol ButlerProtocol <NSObject>

- (void)makeTea;
- (void)serveSandwiches;
@optional
- (void)mowTheLawn;@end


before

- (id <ButlerProtocol>)butler;



after

@property id <ButlerProtocol> butler;



Collections and Literals

@ リテラルシンタックス と [] 添字シンタックスはXcode 4.4(Apple LLVM 4.0 Compiler)でサポートされたがiOSで使えるようになったのはXcode4.5から。


before

NSNumber *aNumber = [NSNumber numberWithFloat:2.3];



after

NSNumber *aNumber = @2.3f;




before

NSNumber *anotherNumber = [NSNumber numberWithFloat:x];



after

NSNumber *anotherNumber = @(x);




before

NSArray *anArray = [NSArray arrayWithObjects:aThing, @"A String",

[NSNumber numberWithFloat:3.14], nil];


after

NSArray *anArray = @[ aThing, @"A String", @3.14 ];




before

NSDictionary *aDictionary = [NSDictionary dictionaryWithObjectsAndKeys:

value, @"Key",
[NSNumber numberWithBOOL:YES], @"OtherKey", nil];

objectivec:afterNSDictionary *aDictionary = @{ @"Key" : value, @"OtherKey" : @YES };



before

NSDictionary *distanceDict = [NSDictionary dictionaryWithObjectsAndKeys:            [NSNumber numberWithDouble:  0.0], kCIAttributeMin,

[NSNumber numberWithDouble: 1.0], kCIAttributeMax,
[NSNumber numberWithDouble: 0.0], kCIAttributeSliderMin,
[NSNumber numberWithDouble: 0.7], kCIAttributeSliderMax,
[NSNumber numberWithDouble: 0.2], kCIAttributeDefault,
[NSNumber numberWithDouble: 0.0], kCIAttributeIdentity,
kCIAttributeTypeScalar, kCIAttributeType,
nil];


after

NSDictionary *distanceDict = @{

kCIAttributeMin : @0.0,
kCIAttributeMax : @1.0,
kCIAttributeSliderMin : @0.0,
kCIAttributeSliderMax : @0.7,
kCIAttributeDefault : @0.2,
kCIAttributeIdentity : @0.0,
kCIAttributeType : kCIAttributeTypeScalar
};



before

NSDictionary *slopeDict = [NSDictionary dictionaryWithObjectsAndKeys:            [NSNumber numberWithDouble: -0.01], kCIAttributeSliderMin,

[NSNumber numberWithDouble: 0.01], kCIAttributeSliderMax,
[NSNumber numberWithDouble: 0.00], kCIAttributeDefault,
[NSNumber numberWithDouble: 0.00], kCIAttributeIdentity,
kCIAttributeTypeScalar, kCIAttributeType,
nil];


after

NSDictionary *slopeDict = @{

kCIAttributeSliderMin : @-0.01,
kCIAttributeSliderMax : @0.01,
kCIAttributeDefault : @0.00,
kCIAttributeIdentity : @0.00,
kCIAttributeType : kCIAttributeTypeScalar };



before

id firstElement = [anArray objectAtIndex:0];

[anArray replaceObjectAtIndex:0 withObject:newValue];


after

id firstElement = anArray[0];

anArray[0] = newValue;



before

id value = [aDictionary objectForKey:@"key"];

[aDictionary setObject:newValue forKey:@"key"];


after

id value = aDictionary[@"key"];

aDictionary[@"key"] = newValue;



before

NSArray *array = ...;

int i;
for (i = 0; i < [array count]; i++) {
id element = [array objectAtIndex:i];
// ...
}


after

NSArray *array = ...;

for (id element in array) {
// ...
}


Blocks


NSArray

ソートは(NSArray *)sortedArrayUsingComparator:(NSComparator)cmptrが使える。


before

NSArray *array = ...;

NSArray *sortedArray;
sortedArray = [array sortedArrayUsingFunction:MySort context:NULL];

NSInteger MySort(id num1, id num2, void *context) {
NSComparisonResult result;
// Do comparison
return result;
}



after

NSArray *array = ...;

BOOL reverse = ...;
NSArray *sortedArray;
sortedArray = [array sortedArrayUsingComparator:^(id num1, id num2) {
NSComparisonResult result;
// Do comparison
return result;
}];

--

eachには(void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))blockが使える。


before

NSArray *array = ...;

for (id element in array) {
// ...
}


after

NSArray *array = ...;

[array enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop) {
// ...
NSLog(@"Processing %@ at index %d”, obj, idx);
// ...
}];

--


NSDictionary

(void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))blockが使える。


before

NSDictionary *dictionary = ...;

for (NSString *key in dictionary) {
id object = [dictionary objectForKey:key];
// Do things with key and object.
}


after

NSDictionary *dictionary = ...;

[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop) {
// Do things with key and object.
}];

--


Notifications

(id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))blockが使える。


before

- (void)registerForNotifications {

NSNotificationCenter *center = ...
[center addObserver:self
selector:@selector(windowBecameKey:)
name:NSWindowDidBecomeKeyNotification
object:self.window];
}
// Different context
// No queue information
- (void)windowBecameKey:(NSNotification *)notification {
// Get contextual information.
}


after

- (void)registerForNotifications {

NSNotificationCenter *center = ...
MyClass *__weak weakSelf = self;
[center addObserverForName:NSWindowDidBecomeKeyNotification
object:self.window
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *) {
// ...
[weakSelf doSomething];
// ...
}];
}

Blosks内でselfは直接参照せず、弱参照 MyClass *__weakに変換して使わないといけない。

--


その他