この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Objective-C でも Optional
Xcode 6.3 で Objective-C の言語機能のアップデートがあり、 Optional と同等の機能を提供する __nullable と __nonnull が利用できるようになりました。端的に言うと Objective-C で値が空の状態がより明確に表現できるようになったということです。今回は、この機能をご紹介します。
- Xcode 6.3 から利用可能
- __nullable __nonnullで定義
- nullable nonnull でも記述可能
__nullable と __nonnull
__nullable は、NULL または nil が含まれる可能性がある場合に使用します。一方 __nonnull は NULL または nil が含まれる可能性がない場合(つまり、絶対に空ではない場合)に使用します。これらを使うと、例えば __nonnull な引数に nil を渡すと警告が出るようになります。nil を入れて良いのか入れてはいけないのか、ひと目で分かりますね!
@interface AAPLList : NSObject <NSCoding, NSCopying>
- (AAPLListItem * __nullable)itemWithName:(NSString * __nonnull)name;
@property (copy, readonly) NSArray * __nonnull allItems;
@end
// 利用するとき--------------
[self.list itemWithName:nil]; // Warning!
よりモダンな表現である nullable と nonnull も利用できます。
- (nullable AAPLListItem *)itemWithName:(nonnull NSString *)name;
- (NSInteger)indexOfItem:(nonnull AAPLListItem *)item;
@property についても、今までの nonatomic や weak などと同じように記述可能です。
@property (copy, nullable) NSString *name;
@property (copy, readonly, nonnull) NSArray *allItems;
__null_unspecified
__null_unspecified は、nil になるかどうかが不明な場合に使用します。リリースノートには very rare (非常にまれ)とも書かれているため、__nullable と __nonnull を適切に指定していれば、使用する機会はかなり少ないと思います。
検査範囲の指定
NS_ASSUME_NONNULL_BEGIN と NS_ASSUME_NONNULL_END を使うと、範囲内のプロパティやメソッドに対してまとめて nonnull とすることが可能です。明示的に nullable を指定している箇所は nullable として扱うことができます。
NS_ASSUME_NONNULL_BEGIN
@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
- (nullable AAPLListItem *)itemWithName:(NSString *)name;
- (NSInteger)indexOfItem:(AAPLListItem *)item;
@property (copy, nullable) NSString *name;
@property (copy, readonly) NSArray *allItems;
// ...
@end
NS_ASSUME_NONNULL_END
// --------------
self.list.name = nil; // OK!
AAPLListItem *matchingItem = [self.list itemWithName:nil]; // Warning!
次の点に注意してください。
- typedef で nonnull は表現できない
- id * のような複雑なポインタ型は __nullable id * __nonnull のように明示的に表現する必要がある
- NSError ** は Nullable な NSError への Nullable なポインタであると仮定される
Swift での利用
nullable と nonnull を利用しておくと、Swift では Optional として変換されます。いままでは Implicitly Unwrapped Optional 型に強制変換だったので、これは嬉しいですね!
class AAPLList : NSObject, NSCoding, NSCopying {
// ...
func itemWithName(name: String) -> AAPLListItem?
func indexOfItem(item: AAPLListItem) -> Int
@NSCopying var name: String? { get set }
@NSCopying var allItems: [AnyObject] { get }
// ...
}
まとめ
Objective-C と Swift との間の大きな違いであった Optional の機能でしたが、Objective-C でも利用できるようになりました。上手く活用することで、値をより堅牢に扱えるようになります。これからの開発で、ぜひ積極的に採用していきましょう。