LoginSignup
122
122

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-03-13

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

--

その他

122
122
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
122
122