底下記錄一些未來的Objective-C將會擁有的新功能與新特色,在Xcode版本4.4以後的編譯器才有這些新功能,編譯出來的程式可以相容於先前版本的系統。
參考影片:
Session 405 - Modern Objective-C
Session 413 - Migrating to Modern Objective-C
首先,跟類別裡方法的宣告順序有關。譬如說,在類別裡有個私有的方法,該怎麼辦?
// SongPlayer.h
@interface SongPlayer : NSObject
{
}
- (void)playSong:(Song *)song;
@end
// SongPlayer.m
@implementation SongPlayer
- (void)playSong:(Song *)song
{
NSError *error;
[self startAudio:&error];
//...
}
- (void)startAudio:(NSError **)error
{
//...
}
@end
其中playSong是公開的介面,而startAudio是內部私用的方法。上面這段程式碼會有編譯錯誤,因為playSong在startAudio宣告之前就呼叫了。
如果能將startAudio搬到前面去就可解決,但有時候做不到或不想這麼做,一般的解法是運用class extension,修改如下:
// SongPlayer.h
@interface SongPlayer : NSObject
{
}
- (void)playSong:(Song *)song;
@end
// SongPlayer.m
@interface SongPlayer ()
- (void)startAudio:(NSError **)error;
@end @implementation SongPlayer
- (void)playSong:(Song *)song
{
NSError *error;
[self startAudio:&error];
//...
}
- (void)startAudio:(NSError **)error
{
//...
}
@end
我們可以把私用方法宣告在class extension(@interface SongPlayer () ... @end)裡,就可解決此問題。
但新的Objective-C更強,它直接幫我們處理好了,編譯器會先解析各方法的宣告(方法名、回傳型別、參數型別),然後再解析方法的內容,這麼一來,一開始的程式碼就不會有錯誤了。
新的Objective-C的enum有了不錯的進步,新型的enum可如下定義:
enum FruitEnum : NSUInteger
{
FruitEnumApple,
FruitEnumBanana,
FruitEnumCherry,
FruitEnumBlueberry,
//...
} FruitEnum;
其名為fixed underlying type enum。
有了這種enum後,Xcode在補足程式碼(code completion)時就更聰明了,而且,編譯器也能作檢查型別,譬如說把某enum值指定給另一個enum型別的變數,就會出現錯誤訊息(編譯器參數為-Wcovnersion),還有,以switch根據某enum型別作判斷的話,若少寫某enum值時編譯器也會提醒你(編譯器參數為-Wswitch)。
對於property也有很多新東西。
原本會這麼寫:
//Person.h
@interface Person : NSObject
{
NSString *_name;
}
@property (strong) NSString *name;
@end//Person.m
@implementation Person
@synthesize name = _name;
@end
可以把instance variable的宣告改放在@implementation裡:
//Person.h
@interface Person : NSObject
{
}
@property (strong) NSString *name;
@end//Person.m
@implementation Person
{
NSString *_name;
}
@synthesize name = _name;
@end
而且,既然用了@synthesize,根本就不需要自己宣告instance variable了:
//Person.h
@interface Person : NSObject
{
}
@property (strong) NSString *name;
@end//Person.m
@implementation Person
{
}
@synthesize name = _name;
@end
以上是本來就有的,現在,在Xcode 4.4(與之後的版本),連@synthesize都可以省略了:
//Person.h
@interface Person : NSObject
{
}
@property (strong) NSString *name;
@end//Person.m
@implementation Person
{
}
@end
也就是說,synthesize是"預設"的行為了,原本property要寫好幾行程式,現在只需一行即可,編譯器會自動加入:
@synthesize name = _name;
預設會在property名字前面加上底線「_」作為instance variable的名字。
注意,為了相容於舊版,若你寫了
@synthesize name;
則等同於
@synthesize name = name;
object literal(這是我覺得早就應該加入的功能)。
首先是NSNumber,原本要這麼寫:
NSNumber *n1 = [NSNumber numberWithInt:1234];
NSNumber *n2 = [NSNumber numberWithFloat:1234.56f];
NSNumber *n3 = [NSNumber numberWithBool:YES];
等等...
新版的只需寫:
NSNumber *n1 = @1234;
NSNumber *n2 = @1234.56f;
NSNumber *n3 = @(YES); // iOS 5寫@YES不行
等等...
不僅如此,以下的運算式都會變成NSNumber:
@( M_PI / 6 )
@( "0123456789ABCDEF"[x % 16] )
@( NSWriteDirectionLeftToRight )
@( getenv("PATH") ),這個會變成NSString
同樣的,NSArray,原本要寫很多程式碼,要擔心有沒有在末端加上nil,現在可改成:
NSArray *array1 = @[]; // 空的陣列
NSArray *array2 = @[a]; // 只含有a物件的陣列
NSArray *array3 = @[@"a", @"b", @"c"]; // 含有三個字串的陣列
既然NSArray出現了,沒道理NSDictionary會缺席啊,同理,NSDictionary也有了類似的新語法:
NSDictionary *dict1 = @{};
NSDictionary *dict2 = @{k1:o1};
NSDictionary *dict3 = @{k1:o1, k2:o2, k3:o3};
id value = aDictionary[@"key"]; // 讀取
aMutableDictionary[@"key"] = newValue; // 寫入
subscripting
NSArray的物件也可以用[]來存取其內的東西了,
NSArray *songs = @[@"song1", @"song2", @"song3"];
NSString *s1 = songs[0]; //讀取
songs[0] = @"newSong"; //寫入
不只NSArray可以利用[]語法,你自己寫的類別也可以喔,只需實作底下的方法即可:
-(元素型別)objectAtIndexedSubscript:(索引型別)idx;
-(void)setObject:(元素型別)object atIndexedSubscript:(索引型別)idx;
嗯,還不錯,程式碼可以大幅簡化囉。
布林的新寫法
ReplyDeleteNSNumber *n3 = @YES;
會跳出
Unexpected type name BOOL expected expression
WWDC也寫錯XD
要寫NSNumber *n3 = @(YES);
感謝提醒,在iOS 5寫@YES會有錯誤,但iOS 6就可以了。
ReplyDelete