LoginSignup
40
37

More than 5 years have passed since last update.

Xcode 7で追加されたSwift用Objective-Cマクロ

Posted at

はじめに

Xcode 7(iOS 9.0 / OS X 10.11 / watchOS 2.0, Apple LLVM version 7.0.0)では、SwiftからObjective-Cを使いやすくするための以下の4つのマクロが追加されている。

NS_SWIFT_UNAVAILABLE(_msg)
NS_REFINED_FOR_SWIFT
NS_SWIFT_NAME(_name)
NS_SWIFT_NOTHROW

NS_SWIFT_UNAVAILABLE(_msg)

Foundation/NSObjCRuntime.h

#define NS_SWIFT_UNAVAILABLE(_msg) CF_SWIFT_UNAVAILABLE(_msg)

CoreFoundation/CFAvailability.h

#if __has_feature(attribute_availability_swift)
#define CF_SWIFT_UNAVAILABLE(_msg) __attribute__((availability(swift, unavailable, message=_msg)))
#else
#define CF_SWIFT_UNAVAILABLE(_msg)
#endif

いくつかのObjective-CインターフェイスはSwiftインターフェイスとして公開されるのにふさわしくない、または必要でないかもしれない。Objective-Cの宣言がSwiftによってインポートされないようにするために、NS_SWIFT_UNAVAILABLE(_msg)を利用すること。APIコンシューマに存在するかもしれない代案を指示するメッセージを引数に渡す。

例えば、キーと値のペアの可変長引数を取る簡易イニシャライザを提供するObjective-Cクラスは、Swiftコンシューマに代わりに辞書リテラルを利用ことを忠告する:

+ (instancetype)collectionWithKeysAndValues:(id)firstKey, ... NS_SWIFT_UNAVAILABLE("Use a dictionary literal instead");

Swiftコードから+collectionWithKeysAndValues:を呼び出そうとするとコンパイルエラーになる。

NS_REFINED_FOR_SWIFT

Foundation/NSObjCRuntime.h

#define NS_REFINED_FOR_SWIFT CF_REFINED_FOR_SWIFT

CoreFoundation/CFBase.h

#if __has_attribute(swift_private)
# define CF_REFINED_FOR_SWIFT __attribute__((swift_private))
#else
# define CF_REFINED_FOR_SWIFT
#endif

NS_REFINED_FOR_SWIFTマクロは、元の実装を有効にしたまま、Swiftで同じAPIの改良版を提供するためにObjective-Cの宣言を移動するために使用することができる。(例えば、Classを取るObjective-CのAPIはSwiftでより的確なパラメータ型を提供することができる。)

NS_REFINED_FOR_SWIFTマクロは異なる宣言で動作が異なる:

  • 初期化メソッドは、最初の外部パラメータ名の前に付加された“__”を持つSwiftのイニシャライザとしてインポートされる。

    - (instancetype)initWithClassName:(NSString *)name NS_REFINED_FOR_SWIFT;
    
    init(__className: String)
    
  • 他のメソッドは、そのベース名の前に“__”が付加されてインポートされる。

    - (NSString *)displayNameForMode:(DisplayMode)mode NS_REFINED_FOR_SWIFT;
    
    func __displayNameForMode(mode: DisplayMode) -> String
    
  • サブスクリプトメソッドは他のメソッドと同様に扱われ、サブスクリプトとしてインポートされない。

  • 他の宣言は、その名前の前に付加される“__”を持つ。

    @property DisplayMode mode NS_REFINED_FOR_SWIFT;
    
    var __mode: DisplayMode { get set }
    

NS_SWIFT_NAME(_name)

Foundation/NSObjCRuntime.h

#define NS_REFINED_FOR_SWIFT CF_REFINED_FOR_SWIFT

CoreFoundation/CFBase.h

#if __has_attribute(swift_name)
# define CF_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
#else
# define CF_SWIFT_NAME(_name)
#endif

NS_SWIFT_NAMEマクロは、その定数がSwiftにきれいにマップされない列挙型のインポートを制御するために使用することができる。例えば:

typedef NS_ENUM(NSInteger, DisplayMode) {
  DisplayMode256Colors NS_SWIFT_NAME(With256Colors),
  DisplayModeThousandsOfColors,
  DisplayModeMillionsOfColors
};

これは以下のようにSwiftにインポートされる:

@objc enum DisplayMode : Int {
  case With256Colors
  case ThousandsOfColors
  case MillionsOfColors
}

このマクロは、ファクトリメソッドがイニシャライザとしてインポートされているかどうかを制御するためにも使用することができる。例えば:

@interface MyController : UIViewController
+ (instancetype)standardControllerForURLKind:(URLKind)kind NS_SWIFT_NAME(init(URLKind:));
@end

これは、自動ファクトリメソッドのインポートの規則に従っていない場合でも、以下のようにSwiftにインポートされる:

class MyController : UIViewController {
  init(URLKind kind: URLKind)
}

NS_SWIFT_NOTHROW

Foundation/NSObjCRuntime.h

#if __has_attribute(swift_error)
#define NS_SWIFT_NOTHROW __attribute__((swift_error(none)))
#else
#define NS_SWIFT_NOTHROW
#endif

スローするメソッドとしてSwiftによってインポートされないようにするためにNSErrorを生成するObjective-Cメソッドの宣言には、NS_SWIFT_NOTHROWマクロを利用すること。

例:

Foundation/NSURL.h

- (BOOL)checkResourceIsReachableAndReturnError:(NSError **)error NS_SWIFT_NOTHROW NS_AVAILABLE(10_6, 4_0);
40
37
0

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
40
37