Adopting Modern Objective-Cより
instancetype
instancetype
の方がコンパイラのチェックがきく。
@interface MyObject
- (id)myFactoryMethod;
@end
↓
@interface MyObject
- (instancetype)myFactoryMethod;
@end
Enumeration Macros
iOS6 SDKから追加されたマクロ。
enum {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;
↓
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
bitmaskは NS_OPTIONS
を使う
enum {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};typedef NSUInteger UIViewAutoresizing;
↓
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
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h> //or @class NSString;
↓
#import <Foundation/Foundation.h>
--
Accessor Methods
常にアクセサメソッドを使おう。ただしinitializerやdealloc内は除く。
- (void)myMethod {
// ...
[self setTitle:[NSString stringWithFormat:@"Area: %1.2f", [self area]]];
// ...
}
↓
- (void)myMethod {
// ...
self.title = [NSString stringWithFormat:@"Area: %1.2f", self.area];
// ...
}
--
Memory Management
ARCを使う。ARCはXcode 4.2(LLVM compiler 3.0)からサポート、一部の動作がコンパイラだけで解決できないためiOS5以上が必要になる。
NSMutableArray *array = [[NSMutableArray alloc] init];
// Use the array
[array release];
// or
NSMutableArray *array = [[[NSMutableArray alloc] init] autoelease];
// Use the array
↓
NSMutableArray *array = [NSMutableArray array];
// Use the array
--
id heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// ...
[heisenObject doSomething];
[heisenObject release];
// ...
↓
id heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// ...
[heisenObject doSomething];
// ...
- (void)takeLastNameFrom:(Person *)person {
NSString *oldLastname = [[self lastName] retain];
[self setLastName:[person lastName]];
NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);
[oldLastName release];
}
↓
- (void)takeLastNameFrom:(Person *)person {
NSString *oldLastname = [self lastName];
[self setLastName:[person lastName]];
NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);
}
--
CFUUIDRef cfUUID = CFUUIDCreate(NULL);
NSString *noteUUID = (NSString *)CFUUIDCreateString(NULL, cfUUID);
CFRelease(cfUUID);
↓
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は除く)
@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
↓
@interface Thing : NSObject
@property (copy) NSString *title;
@end
@implementation Thing
@synthesize title;
@end
@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
↓
@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
@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
↓
@interface Thing : NSObject
@property (weak) id delegate;
@end
@implementation Thing
@synthesize delegate;
@end
Private State
非公開な変数はクラスエクステンション内でpropertyとして宣言する。メソッドも同様にクラスエクステンションを使う。
@interface Thing {
BOOL privateTest;
}
↓
@interface Thing ()
@property BOOL privateTest;
@end
// ...
@interface Thing (PrivateMethods)
- (void)doSomethingPrivate;
- (void)doSomethingElsePrivate;
@end
↓
@interface Thing ()
- (void)doSomethingPrivate;
- (void)doSomethingElsePrivate;
@end
Outlets
メモリ管理のためにstrongかweakかをproperty宣言する。
@interface MyViewController : MySuperclass {
IBOutlet ElementClass *uiElement;
}
@end
↓
@interface MyViewController : MySuperclass
@property (weak) IBOutlet ElementClass *uiElement;
@end
Initializer Methods and dealloc
初期化と解放はアクセサじゃなくて変数で。
- (id)init {
if (self = [super init]) {
[self setTitle:@"default"];
}
return self;
}
↓
- (id)init {
self = [super init];
if (self) {
_title = @"default";
}
return self;
}
- (void)dealloc {
[self setTitle:nil];
[super dealloc];
}
↓
- (void)dealloc {
[_title release];
[super dealloc];
}
Protocols
必須ではない実装していないメソッドがあるためにidをそのまま使ったりせずにoptional宣言を使ってid型にもちゃんとprotocolを指定する。
@ButlerProtocol
- (void)makeTea;
- (void)serveSandwiches;
- (void)mowTheLawn;
@end
↓
@protocol ButlerProtocol <NSObject>
- (void)makeTea;
- (void)serveSandwiches;
@optional
- (void)mowTheLawn;@end
- (id <ButlerProtocol>)butler;
↓
@property id <ButlerProtocol> butler;
Collections and Literals
@
リテラルシンタックス と []
添字シンタックスはXcode 4.4(Apple LLVM 4.0 Compiler)でサポートされたがiOSで使えるようになったのはXcode4.5から。
NSNumber *aNumber = [NSNumber numberWithFloat:2.3];
↓
NSNumber *aNumber = @2.3f;
NSNumber *anotherNumber = [NSNumber numberWithFloat:x];
↓
NSNumber *anotherNumber = @(x);
NSArray *anArray = [NSArray arrayWithObjects:aThing, @"A String",
[NSNumber numberWithFloat:3.14], nil];
↓
NSArray *anArray = @[ aThing, @"A String", @3.14 ];
NSDictionary *aDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
value, @"Key",
[NSNumber numberWithBOOL:YES], @"OtherKey", nil];
↓
objectivec:afterNSDictionary *aDictionary = @{ @"Key" : value, @"OtherKey" : @YES };
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];
↓
NSDictionary *distanceDict = @{
kCIAttributeMin : @0.0,
kCIAttributeMax : @1.0,
kCIAttributeSliderMin : @0.0,
kCIAttributeSliderMax : @0.7,
kCIAttributeDefault : @0.2,
kCIAttributeIdentity : @0.0,
kCIAttributeType : kCIAttributeTypeScalar
};
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];
↓
NSDictionary *slopeDict = @{
kCIAttributeSliderMin : @-0.01,
kCIAttributeSliderMax : @0.01,
kCIAttributeDefault : @0.00,
kCIAttributeIdentity : @0.00,
kCIAttributeType : kCIAttributeTypeScalar };
id firstElement = [anArray objectAtIndex:0];
[anArray replaceObjectAtIndex:0 withObject:newValue];
↓
id firstElement = anArray[0];
anArray[0] = newValue;
id value = [aDictionary objectForKey:@"key"];
[aDictionary setObject:newValue forKey:@"key"];
↓
id value = aDictionary[@"key"];
aDictionary[@"key"] = newValue;
NSArray *array = ...;
int i;
for (i = 0; i < [array count]; i++) {
id element = [array objectAtIndex:i];
// ...
}
↓
NSArray *array = ...;
for (id element in array) {
// ...
}
Blocks
NSArray
ソートは(NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr
が使える。
NSArray *array = ...;
NSArray *sortedArray;
sortedArray = [array sortedArrayUsingFunction:MySort context:NULL];
NSInteger MySort(id num1, id num2, void *context) {
NSComparisonResult result;
// Do comparison
return result;
}
↓
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
が使える。
NSArray *array = ...;
for (id element in array) {
// ...
}
↓
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
が使える。
NSDictionary *dictionary = ...;
for (NSString *key in dictionary) {
id object = [dictionary objectForKey:key];
// Do things with key and object.
}
↓
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
が使える。
- (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.
}
↓
- (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 modernで検索
- 使いたいメソッドがBlocksに対応していない場合 → BlocksKitの利用を検討する
- Xcode(コンパイラ)の更新は What’s New in Xcode を参照