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


Adopting Modern Objective-Cより

## instancetype

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

```objectivec:before
@interface MyObject
- (id)myFactoryMethod;
@end
```

↓

```objectivec:after
@interface MyObject
- (instancetype)myFactoryMethod;
@end
```

## ￼Enumeration Macros

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

```objectivec:before
enum {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;
```

↓

```objectivec:after
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};
```

bitmaskは ``NS_OPTIONS`` を使う

```objectivec: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;
```

↓

```objectivec: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

```objectivec:before
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h> //or @class NSString;
```

↓

```objectivec:after
#import <Foundation/Foundation.h>
```

--

## ￼Accessor Methods

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

```objectivec:before
- (void)myMethod {
    // ...
    [self setTitle:[NSString stringWithFormat:@"Area: %1.2f", [self area]]];
    // ...
}
```

↓

```objectivec:after
- (void)myMethod {
    // ...
    self.title = [NSString stringWithFormat:@"Area: %1.2f", self.area];
    // ...
}
```

--

## ￼Memory Management

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

```objectivec:before
NSMutableArray *array = [[NSMutableArray alloc] init];
// Use the array
[array release];

// or 

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

↓

```objectivec:after
NSMutableArray *array = [NSMutableArray array];
// Use the array
```

--

```objectivec:before
id heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// ...
[heisenObject doSomething];
[heisenObject release];
// ...
```

↓

```objectivec:after
id heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// ...
[heisenObject doSomething];
// ...
```

---

```objectivec:before
- (void)takeLastNameFrom:(Person *)person {
    NSString *oldLastname = [[self lastName] retain];
    [self setLastName:[person lastName]];
    NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);
    [oldLastName release];
}
```

↓

```objectivec:after
- (void)takeLastNameFrom:(Person *)person {
    NSString *oldLastname = [self lastName];
    [self setLastName:[person lastName]];
    NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);
}
```

--

```objectivec:before
CFUUIDRef cfUUID = CFUUIDCreate(NULL);
NSString *noteUUID = (NSString *)CFUUIDCreateString(NULL, cfUUID);
CFRelease(cfUUID);
```

↓

```objectivec: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は除く)

```objectivec: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
```
↓

```objectivec:after
@interface Thing : NSObject 
@property (copy) NSString *title;
@end

@implementation Thing
@synthesize title;
@end
```

---


```objectivec: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
```

↓


```objectivec: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
```

----


```objectivec: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
```

↓


```objectivec:after
@interface Thing : NSObject
@property (weak) id delegate;
@end

@implementation Thing
@synthesize delegate;
@end
```

## ￼Private State

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

```objectivec:before
@interface Thing {
   BOOL privateTest;
}
```

↓

```objectivec:after
@interface Thing ()
@property BOOL privateTest;
@end
// ...
```

---

```objectivec:before
@interface Thing (PrivateMethods)
- (void)doSomethingPrivate;
- (void)doSomethingElsePrivate;
@end
```

↓

```objectivec:after
@interface Thing ()
- (void)doSomethingPrivate;
- (void)doSomethingElsePrivate;
@end
```

## Outlets

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

```objectivec:before
@interface MyViewController : MySuperclass {
    IBOutlet ElementClass *uiElement;
}
@end
```

↓

```objectivec:after
@interface MyViewController : MySuperclass
@property (weak) IBOutlet ElementClass *uiElement;
@end
```

## ￼Initializer Methods and dealloc


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

```objectivec:before
- (id)init {
    if (self = [super init]) {
        [self setTitle:@"default"];
    }
    return self;
}
```

↓

```objectivec:after
- (id)init {
    self = [super init];
    if (self) {
        _title = @"default";
    }
    return self;
}
```

---

```objectivec:before
- (void)dealloc {
    [self setTitle:nil];
    [super dealloc];
}
```

↓

```objectivec:after
- (void)dealloc {
    [_title release];
    [super dealloc];
}
```


## Protocols

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


```objectivec:before
@ButlerProtocol
- (void)makeTea;
- (void)serveSandwiches;
- (void)mowTheLawn;
@end
```

↓

```objectivec:after
@protocol ButlerProtocol <NSObject>
- (void)makeTea;
- (void)serveSandwiches;
@optional
- (void)mowTheLawn;@end
```

```objectivec:before
- (id <ButlerProtocol>)butler;
```

↓

```objectivec:after
@property id <ButlerProtocol> butler;
```

## ￼Collections and Literals

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

```objectivec:before
NSNumber *aNumber = [NSNumber numberWithFloat:2.3];
```

↓

```objectivec:after
NSNumber *aNumber = @2.3f;
```
---

```objectivec:before
NSNumber *anotherNumber = [NSNumber numberWithFloat:x];
```

↓

```objectivec:after
NSNumber *anotherNumber = @(x);
```

---

```objectivec:before
NSArray *anArray = [NSArray arrayWithObjects:aThing, @"A String",
                       [NSNumber numberWithFloat:3.14], nil];
```

↓

```objectivec:after
NSArray *anArray = @[ aThing, @"A String", @3.14 ];
```

---
```objectivec:before
NSDictionary *aDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                value, @"Key",
                                [NSNumber numberWithBOOL:YES], @"OtherKey",                                nil];
```

↓

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

---

```objectivec: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];
```

↓

```objectivec:after
NSDictionary *distanceDict = @{
        kCIAttributeMin       : @0.0,
        kCIAttributeMax       : @1.0,
        kCIAttributeSliderMin : @0.0,
        kCIAttributeSliderMax : @0.7,
        kCIAttributeDefault   : @0.2,
        kCIAttributeIdentity  : @0.0,
        kCIAttributeType      : kCIAttributeTypeScalar
};
```

---

```objectivec: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];
```

↓

```objectivec:after
NSDictionary *slopeDict = @{
        kCIAttributeSliderMin : @-0.01,
        kCIAttributeSliderMax : @0.01,
        kCIAttributeDefault   : @0.00,
        kCIAttributeIdentity  : @0.00,
        kCIAttributeType      : kCIAttributeTypeScalar };
```

---

```objectivec:before
id firstElement = [anArray objectAtIndex:0];
[anArray replaceObjectAtIndex:0 withObject:newValue];
```

↓

```objectivec:after
id firstElement = anArray[0];
anArray[0] = newValue;
```

---

```objectivec:before
id value = [aDictionary objectForKey:@"key"];
[aDictionary setObject:newValue forKey:@"key"];
```

↓

```objectivec:after
id value = aDictionary[@"key"];
aDictionary[@"key"] = newValue;
```


---

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

↓

```objectivec:after
NSArray *array = ...;
for (id element in array) {
     // ...
}
```

## Blocks


### NSArray

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

```objectivec: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;
}
```

↓

```objectivec: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``が使える。

```objectivec:before
NSArray *array = ...;
for (id element in array) {
    // ... 
}
```

↓

```objectivec: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``が使える。

```objectivec: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.
}
```

↓

```objectivec: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```に変換して使わないといけない。

--

## その他

* 元資料は [Mac Developer Library](https://developer.apple.com/library/mac/navigation/index.html) **modern**で検索
* 使いたいメソッドがBlocksに対応していない場合 → [BlocksKit](https://github.com/pandamonia/BlocksKit)の利用を検討する
* Xcode(コンパイラ)の更新は [What’s New in Xcode](https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/WhatsNewXcode/00-Introduction/Introduction.html) を参照