C の API との相互作用
Objective-C との相互運用性の一環として、Swift は、C 言語の型と機能との多くの互換性を維持しています。Swift はまた、コードがそれを必要とする場合には、一般的な C 言語構文やパターンでの作業の方法を提供します。
原始的な型
Swift は、C の原始的な整数と同じ型を提供します。たとえば、char、int、float、および double です。ただし、Int のように、これらの型と Swift の核となる整数型の間には暗黙の型変換はありません。したがって、コードでは、具体的にそれらを必要とする場合にはそれらの型を使用しますが、そうでなければ可能な限りは Int 型を使用して下さい。
C の型 | Swift の型 |
---|---|
bool | CBool |
char, signed char | CChar |
unsigned char | CUnsignedChar |
short | CShort |
unsigned short | CUnsignedShort |
int | CInt |
unsigned int | CUnsignedInt |
long | CLong |
unsigned long | CUnsignedLong |
long long | CLongLong |
unsigned long long | CUnsignedLongLong |
wchar_t | CWideChar |
char16_t | CChar16 |
char32_t | CChar32 |
float | CFloat |
double | CDouble |
グローバル定数
C と Objective-C のソースファイルで定義されたグローバル定数は自動的に Swift のグローバル定数として Swift のコンパイラによって import されます。
import された定数の列挙体と構造体
Objective-C では、プロパティとメソッドのパラメータに可能な値のリストを提供するために定数がよく使われます。Objective-C の typedef 宣言に、NS_TYPED_ENUM または NS_TYPED_EXTENSIBLE_ENUM マクロを使用して注釈を付け、その型の定数を Swift によって共通の型のメンバとして import できます。論理的に値を Swift の拡張機能で追加できない値のセットには、NS_TYPED_ENUM を使用します。拡張機能で展開できる値のセットには、NS_TYPED_EXTENSIBLE_ENUM を使用します。
可能な値の固定したセットを表す定数は、NS_TYPED_ENUM マクロを追加することによって構造体として import できます。たとえば、以下の TrafficLightColor 型の整数定数の Objective-C の宣言を考えてみましょう。
- // Store the three traffic light color options as 0, 1, and 2.
- typedef long TrafficLightColor NS_TYPED_ENUM;
- TrafficLightColor const TrafficLightColorRed;
- TrafficLightColor const TrafficLightColorYellow;
- TrafficLightColor const TrafficLightColorGreen;
<<OBJECTIVE-C>>
Swift は、これを以下のように import します。
- struct TrafficLightColor: RawRepresentable, Equatable, Hashable {
-         typealias RawValue = Int
-         init(rawValue: RawValue)
-         var rawValue: RawValue { get }
-         static var red: TrafficLightColor { get }
-         static var yellow: TrafficLightColor { get }
-         static var green: TrafficLightColor { get }
- }
<< SWIFT >>
可能な値の拡張可能なセットを表す定数は、NS_TYPED_EXTENSIBLE_ENUM マクロを追加することによって構造体として import できます。たとえば以下の、交通信号でアクティブに点灯した色のセットを表す Objective-C 宣言を考えてみましょう。
- typedef TrafficLightColor TrafficLightCombo [3] NS_TYPED_EXTENSIBLE_ENUM;
- TrafficLightCombo const TrafficLightComboJustRed;
- TrafficLightCombo const TrafficLightComboJustYellow;
- TrafficLightCombo const TrafficLightComboJustGreen;
- // Some places use a combination of lights to indicate a specific condition.
- TrafficLightCombo const TrafficLightComboRedYellow;
<<OBJECTIVE-C>>
Swift は、これを以下のように import します。
- struct TrafficLightCombo: RawRepresentable, Equatable, Hashable {
-         typealias RawValue = (TrafficLightColor, TrafficLightColor, TrafficLightColor)
-         init(_ rawValue: RawValue)
-         init(rawValue: RawValue)
-         var rawValue: RawValue { get }
-         static var justRed: TrafficLightCombo { get }
-         static var justYellow: TrafficLightCombo { get }
-         static var justGreen: TrafficLightCombo { get }
-         static var redYellow: TrafficLightCombo { get }
- }
<< SWIFT >>
この拡張可能な形式を使用する定数は、新しい値でセットを拡張するときに、引数ラベルを省略できる追加のイニシャライザとともに import されます。
NS_TYPED_EXTENSIBLE_ENUM マクロでマークされた型宣言から import された定数は、Swift コードで拡張して新しい値を追加できます。
- extension TrafficLightCombo {
-         static var all: TrafficLightCombo {
-                 return TrafficLightCombo((.red, .yellow, .green))
-         }
- }
<< SWIFT >>
関数
Swift は、C のヘッダで宣言された全ての関数を Swift のグローバル関数として import します。たとえば、以下の C の関数の宣言を考えてみましょう。
- int product(int multiplier, int multiplicand);
- int quotient(int dividend, int devisor, int *remainder);
- struct Point2D createPoint2D(float x, float y);
- float distance(struct Point2D from, struct Point2D to);
<<OBJECTIVE-C>>
Swift は、これを以下のように import します。
- func product(_ multiplier: Int32, _ multiplicand: Int32) -> Int32
- func quotient(_ dividend: Int32, _ devisor: Int32, _ remainder:
UnsafeMutablePointer<Int32>) -> Int32 - func createPoint2D(_ x: Float, _ y: Float) -> Point2D
- func distance(_ from: Point2D, _ to: Point2D) -> Float
<< SWIFT >>
可変個引数の関数
Swift では、getVaList(_:) または withVaList(_:_:) 関数を使用して、vasprintf のような C の可変個引数の関数を呼び出すことができます。getVaList(_:) 関数は CVarArg 値の配列を取り、CVaListPointer 値を返しますが、ここで withVaList(_:_:) は、この値を直接返すのではなく、この値を本体内でクロージャーパラメータとして提供します。結果として得られる CVaListPointer 値は、C 可変個引数関数の va_list 引数に渡されます。
たとえば、Swift で vasprintf 関数を呼び出す方法は以下のとおりです。
- func swiftprintf(format: String, arguments: CVarArg...) -> String? {
-         return withVaList(arguments) { va_list in
-                 var buffer: UnsafeMutablePointer<Int8>? = nil
-                 return format.withCString { cString in
-                         guard vasprintf(&buffer, cString, va_list) != 0 else {
-                         return nil
-                         }
-                         return String(validatingUTF8: buffer!)
-                 }
-         }
- }
- print(swiftprintf(format: "√2 ≅ %g", arguments: sqrt(2.0))!)
- // Prints "√2 ≅ 1.41421"
<< SWIFT >>
構造体
Swift は、C のヘッダで宣言された C の構造体を Swift の構造体として import します。import された Swift の構造体は、各 C の構造体フィールドの格納されたプロパティと、格納されたプロパティに対応するパラメータを持つイニシャライザを含んでいます。import されたメンバーのすべてがデフォルト値を持つ場合、Swift は引数を取らないデフォルトのイニシャライザも提供します。たとえば、以下の C 構造が与えられたとしましょう:
- struct Color {
-         float r, g, b;
- };
- typedef struct Color Color ;
<<OBJECTIVE-C>>
対応する Swift 型はこうです:
- public struct Color {
-         var r: Float
-         var g: Float
-         var b: Float
-         init()
-         init(r: Float, g: Float, b: Float)
- }
<< SWIFT >>
型メンバとして関数を import
Core Foundation フレームワークのような C の API は、多くの場合、C 構造体を作成し、アクセスし、または変更する関数を提供します。独自のコードで CF_SWIFT_NAME マクロを使用すると、import された構造体型のメンバとして Swift は、C 関数を import できます。たとえば、以下の C 関数宣言が与えられたとします:
- Color ColorCreateWithCMYK(float c, float m, float y, float k)
CF_SWIFT_NAME(Color.init(c:m:y:k:)); - float ColorGetHue(Color color) CF_SWIFT_NAME(getter:Color.hue(self:));
- void ColorSetHue(Color color, float hue)
CF_SWIFT_NAME(setter:Color.hue(self:newValue:)); - Color ColorDarkenColor(Color color, float amount)
CF_SWIFT_NAME(Color.darken(self:amount:)); - extern const Color ColorBondiBlue CF_SWIFT_NAME(Color.bondiBlue);
- Color ColorGetCalibrationColor(void) CF_SWIFT_NAME(getter:Color.calibration());
- Color ColorSetCalibrationColor(Color color)
CF_SWIFT_NAME(setter:Color.calibration(newValue:));
<<OBJECTIVE-C>>
Swift は、それらを型メンバとして import します。
- extension Color {
-         init(c: Float, m: Float, y: Float, k: Float)
-         var hue: Float { get set }
-         func darken(amount: Float) -> Color
-         static var bondiBlue: Color
-         static var calibration: Color
- }
<< SWIFT >>
CF_SWIFT_NAME マクロに渡される引数は、#selector 式と同じ構文を使用します。CF_SWIFT_NAME 引数での self の使用は、メソッド受信者を参照するインスタンスメソッドに使用されます。
列挙型
NS_ENUM マクロでマークされた全ての C の列挙型を Swift は列挙型として Int の生の値型で Swift に import します。C の列挙型の case の名前への接頭辞は、Swift に import される時、システムフレームワークまたはカスタムコードで定義されているかにより削除されます。
たとえば、以下の C の列挙型は、NS_ENUM マクロを使用して宣言されています。
- typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
-         UITableViewCellStyleDefault,
-         UITableViewCellStyleValue1,
-         UITableViewCellStyleValue2,
-         UITableViewCellStyleSubtitle
- };
<<OBJECTIVE-C>>
Swift では、以下のように import されます:
- enum UITableViewCellStyle: Int {
-        case `default`
-        case Value1
-        case Value2
-        case Subtitle
- }
<< SWIFT >>
列挙型の値を参照する場合、先頭のドット(.) を持つ値の名前を使用して下さい。
<< SWIFT >>
let cellStyle: UITableViewCellStyle = .default
NS_ENUM または NS_OPTIONS マクロでマークされていない C の列挙型は Swift の構造体として import されます。C の列挙型の各メンバは、Swift 構造体のメンバそれ自体ではなく、構造体型のグローバルの読み込み専用の計算されたプロパティとして import されます。
たとえば、以下の C 列挙型は NS_ENUM マクロを使用しては宣言されていません。
- typedef enum {
-         MessageDispositionUnread = 0,
-         MessageDispositionRead = 1,
-         MessageDispositionDeleted = -1,
- } MessageDisposition;
<<OBJECTIVE-C>>
Swift では、これは以下のように import されます:
- struct MessageDisposition: RawRepresentable, Equatable {}
- var MessageDispositionUnread: MessageDisposition { get }
- var MessageDispositionRead: MessageDisposition { get }
- var MessageDispositionDeleted: MessageDisposition { get }
<< SWIFT >>
Swift は import された C の列挙型の Equatable プロトコルへの準拠を自動的に合成します。
Option のセット
Swift はまた、Swift の Option のセットとして NS_OPTIONS マクロでマークされた C の列挙体を import します。Option のセットは、接頭辞を option の値の名前から切り捨てることによって、import された列挙型と同様に振る舞います。
たとえば、以下の Objective-C の options の宣言を考えてみましょう:
- 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
- };
<<OBJECTIVE-C>>
Swift では、これは以下のように import されます。
- public struct UIViewAutoresizing : OptionSet {
-         public init(rawValue: UInt)
-         public static var flexibleLeftMargin: UIViewAutoresizing { get }
-         public static var flexibleWidth: UIViewAutoresizing { get }
-         public static var flexibleRightMargin: UIViewAutoresizing { get }
-         public static var flexibleTopMargin: UIViewAutoresizing { get }
-         public static var flexibleHeight: UIViewAutoresizing { get }
-         public static var flexibleBottomMargin: UIViewAutoresizing { get }
- }
<< SWIFT >>
Objective-C では、option セットは整数値のビットマスクです。ビット単位の OR 演算子(|) を使用して option 値を結合し、ビット単位の AND 演算子(&) を使用して option 値を確認して下さい。定数値または式から新しい option セットを作成し、空の option セットは定数ゼロ(0) で表されます。
Swift では、Option セットは OptionSet プロトコルに準拠した構造体で表され、各 option 値ごとに静的変数があります。配列リテラルを使用して新しい option セットの値を作成し、列挙型に似た先行ドット(.) で option 値にアクセスできます。空の配列リテラル([]) から、またはデフォルトのイニシャライザを呼び出すことによって、空の option セットを作成できます。
option セットは、Swift の Set コレクション型のように振る舞います。option 値を追加するには、insert(_:) または formUnion(_:) メソッドを使用し、option 値を削除するには、remove(_:) または subtract(_:) メソッドを使用し、また option 値を確認するには contains(_:) メソッドを使用して下さい。
- let options: Data.Base64EncodingOptions = [
-         .lineLength64Characters,
-         .endLineWithLineFeed
- ]
- let string = data.base64EncodedString(options: options)
<< SWIFT >>
ユニオン
Swift は Swift 構造体として C のユニオン (union) を import します。Swift は union をサポートしていませんが、Swift 構造体として import された C の union はまだ C の union のように動作します。たとえば、isAlive および isDead フィールドを持つ SchroedingersCat という名前の C のユニオンを考えます。
- union SchroedingersCat {
-         bool isAlive;
-         bool isDead;
- };
<<OBJECTIVE-C>>
Swift では、これはこのように import されます。
- struct SchroedingersCat {
-         var isAlive: Bool { get set }
-         var isDead: Bool { get set }
-         init(isAlive: Bool)
-         init(isDead: Bool)
-         init()
- }
<< SWIFT >>
C のユニオンは、すべてのフィールドに同じ基本メモリー・アドレスを使用するため、Swift によって import されたユニオン内の計算されたプロパティーはすべて同じ基本メモリーを使用します。その結果、import された構造体のインスタンスの計算されたプロパティの値を変更すると、その構造体で定義された他のすべてのプロパティの値を変更します。
以下の例では、SchroedingersCat 構造体のインスタンス上の isAlive の計算されたプロパティの値を変更すると、インスタンスの isDead の計算されたプロパティの値も変更します。
- var mittens = SchroedingersCat(isAlive: false)
- print(mittens.isAlive, mittens.isDead)
- // Prints "false false"
- mittens.isAlive = true
- print(mittens.isDead)
- // Prints "true"
<< SWIFT >>
ビットフィールド
Swift は、Foundation の NSDecimal 型にあるような構造体のビットフィールドを、計算されたプロパティとして import します。ビットフィールドに対応する計算されたプロパティにアクセスすると、Swift は互換性のある Swift 型に、またそれから自動的に値を変換します。
名前のない構造体と union のフィールド
C の struct と union 型は、名前がなく、名前のない型としてフィールドを定義できます。名前のないフィールドは名前が付いたフィールドで struct または union の入れ子になった型から成ります。
たとえば、名前のない union 型の中に入れ子になったたフィールドの layers と height と、名前のないの struct 型のフィールドの topping を含む Cake という C の構造体を考えてみましょう。
- struct Cake {
-         union {
-                 int layers;
-                 double height;
-         };
-         struct {
-                 bool icing;
-                 bool sprinkles;
-         } toppings;
- };
<<OBJECTIVE-C>>
Cake 構造体を import した後、初期化して以下のように使用できます。
- var simpleCake = Cake()
- simpleCake.layers = 5
- print(simpleCake.toppings.icing)
- // Prints "false"
<< SWIFT >>
Cake 構造体は、以下のように、そのフィールドのカスタム値で構造体を初期化するために使用できるメンバ毎のイニシャライザで import されます。
- let cake = Cake(
-         .init(layers: 2),
-         toppings: .init(icing: true, sprinkles: false)
- )
- print("The cake has \(cake.layers) layers.")
- // Prints "The cake has 2 layers."
- print("Does it have sprinkles?", cake.toppings.sprinkles ? "Yes." : "No.")
- // Prints "Does it have sprinkles? No."
<< SWIFT >>
Cake 構造体の最初のフィールドは名前が付けられていないため、イニシャライザの最初のパラメータにはラベルがありません。Cake 構造体には名前のない型のフィールドがあるので、型推論を使用して選択された .init イニシャライザを使用して、構造体の名前のない各フィールドの初期値を設定します。
ポインタ
可能な限り、Swift はポインタへの直接のアクセスを与えることを回避します。しかし、メモリへの直接アクセスを必要とするために利用可能なさまざまなポインタ型があります。以下の表は、マッピングのための構文を示すために、プレースホルダの型の名前として Type を使用しています。
戻り値の型、変数、および引数については、以下のマッピングが適用されます。
C の構文 | Swift の構文 |
---|---|
const Type * | UnsafePointer<Type> |
Type * | UnsafeMutablePointer<Type> |
クラス型の場合は、以下のマッピングが適用されます。
C の構文 | Swift の構文 |
---|---|
Type * const * | UnsafePointer<Type> |
Type * __strong * | UnsafeMutablePointer<Type> |
Type ** | AutoreleasingUnsafeMutablePointer<Type> |
型のない、生のメモリへのポインタについては、以下のマッピングが適用されます。
C の構文 | Swift の構文 |
---|---|
const void * | UnsafeRawPointer |
void * | UnsafeMutableRawPointer |
Swift はまた、バッファを扱うためのポインタ型も提供します。これについては、バッファポインタ を参照してください。
不完全な struct 型のように、C ポインタが指す値の型を Swift で表現できない場合、ポインタは OpaquePointer として import されます。
定数ポインタ
関数が UnsafePointer<Type> 引数を取るように宣言されると、以下のいずれかを受け入れることができます。
- UnsafePointer<Type>、UnsafeMutablePointer<Type>、または 必要に応じて UnsafePointer<Type>に変換される AutoreleasingUnsafeMutablePointer<Type> 値。
- Type が Int8 または UInt8 の場合の、String 値。文字列はバッファ内の UTF8 に自動的に変換され、そのバッファへのポインタが関数に渡されます。
- 左辺の識別子のアドレスへのポインタとして渡される Type 型の可変 (mutable) 変数、プロパティ、またはサブスクリプトの参照を含む in-out 式。
- [Type] 値は、配列の先頭へのポインタとして渡されます。
関数に渡されたポインタは、関数呼び出しの間だけ有効であることが保証されています。関数が返した後でポインタを保持したりそれにアクセスしようとしないでください。
以下のような関数を宣言した場合:
- func takesAPointer(_ p: UnsafePointer<Float>) {
-         //...
- }
<< SWIFT >>
以下の方法のいずれでもそれを呼び出すことができます。
- var x: Float = 0.0
- takesAPointer(&x)
- takesAPointer([1.0, 2.0, 3.0])
<< SWIFT >>
関数が UnsafeRawPointer 引数を取るように宣言されると、任意の型の Type 用の UnsafePointer<Type> と同じオペランドをそれは受け入れる事ができます。
以下のような関数を宣言した場合:
- func takesARawPointer(_ p: UnsafeRawPointer?) {
-         // ...
- }
<< SWIFT >>
以下の方法のいずれでもそれを呼び出すことができます。
- var x: Float = 0.0, y: Int = 0
- takesARawPointer(&x)
- takesARawPointer(&y)
- takesARawPointer([1.0, 2.0, 3.0] as [Float])
- let intArray = [1, 2, 3]
- takesARawPointer(intArray)
<< SWIFT >>
可変ポインタ
関数が UnsafeMutablePointer<Type> 引数を取るように宣言されると、以下のいずれかをそれは受け入れる事ができます。
- UnsafeMutablePointer<Type> 値
- 変更可能な変数、プロパティ、またはサブスクリプト参照を含む型の Type の in-out 式。それは、変更可能な値のアドレスへのポインタとして渡されます。
- 変更可能な変数、プロパティ、またはサブスクリプト参照を含む型の [Type] の in-out 式。それは、配列の先頭へのポインタとして渡され、呼び出しの間寿命を継続できます。
このような関数を宣言した場合:
- func takesAMutablePointer(_ p: UnsafeMutablePointer<Float>) {
-         //...
- }
<< SWIFT >>
以下の方法のいずれでもそれを呼び出すことができます。
- var x: Float = 0.0
- var a: [Float] = [1.0, 2.0, 3.0]
- takesAMutablePointer(&x)
- takesAMutablePointer(&a)
<< SWIFT >>
関数が UnsafeMutableRawPointer 引数を取るように宣言されると、任意の型の Type の UnsafeMutablePointer<Type> と同じオペランドを受け入れます。
以下のような関数を宣言した場合:
- func takesAMutableRawPointer(_ p: UnsafeMutableRawPointer?) {
-         // ...
- }
<< SWIFT >>
以下の方法のいずれでもそれを呼び出せます。
- var x: Float = 0.0, y: Int = 0
- var a: [Float] = [1.0, 2.0, 3.0], b: [Int] = [1, 2, 3]
- takesAMutableRawPointer(&x)
- takesAMutableRawPointer(&y)
- takesAMutableRawPointer(&a)
- takesAMutableRawPointer(&b)
<< SWIFT >>
自動解放ポインタ
関数が AutoreleasingUnsafeMutablePointer<Type> を取るように宣言されると、以下のいずれかを受け入れる事ができます。
- AutoreleasingUnsafeMutablePointer<Type> 値
- Type 型の可変 (mutable) 変数、プロパティ、またはサブスクリプト参照を含む in-out 式。それは、一時的な非所有バッファにビット単位でコピーされます。そのバッファのアドレスが呼び出し元に渡され、戻ってくると、バッファ内の値がロードされ、保持され、オペランドに再割り当てされます。
このリストは、配列を含んでいないことに注意してください。
以下のような関数を宣言した場合:
- func takesAnAutoreleasingPointer(_ p: AutoreleasingUnsafeMutablePointer<NSDate?>) {
-         // ...
- }
<< SWIFT >>
以下の方法でそれを呼び出せます。
- var x: NSDate? = nil
- takesAnAutoreleasingPointer(&x)
<< SWIFT >>
ポインタの先の型は、ブリッジされません。たとえば、NSString ** は、 AutoreleasingUnsafeMutablePointer<NSString?> として Swift にやって来ますが、 AutoreleasingUnsafeMutablePointer<String?> としてではありません。
関数ポインタ
C の関数ポインタは @convention(c) 属性で示される、C の関数ポインタ呼び出し規約でクロージャとして Swift に import されます。たとえば、C 言語での int (*)(void) 型を持つ関数ポインタは @convention(c) () -> Int32 として Swift に import されます。
関数ポインタ引数をとる関数を呼び出すときは、最上位の Swift 関数、クロージャリテラル、または nil を渡すことができます。また、クロージャーの引数リストまたは本体で汎用型パラメーターが参照されていない限り、汎用型または汎用メソッドのクロージャプロパティを渡すこともできます。たとえば、Core Foundation の CFArrayCreateMutable(_:_:_:) 関数を考えてみましょう。CFArrayCreateMutable(_:_:_:) 関数は、CFArrayCallBacks 構造体を取り、この構造体は、関数ポインタのコールバックで初期化されます。
- func customCopyDescription(_ p: UnsafeRawPointer?) -> Unmanaged<CFString>? {
-         // return an Unmanaged<FString>? value
- }
- var callbacks = CFArrayCallBacks(
-         version: 0,
-         retain: nil,
-         release: nil,
-         copyDescription: customCopyDescription,
-         equal: { (p1, p2) -> DarwinBoolean in
-                 // return Bool value
-         }
- )
- var mutableArray = CFArrayCreateMutable(nil, 0, &callbacks)
<< SWIFT >>
上記の例では、CFArrayCallBacks イニシャライザは、retain および release パラメータに引数として nil 値を使用し、customCopyDescription パラメータの引数としての customCopyDescription(_:) 関数は、equal パラメータの引数としてクロージャリテラルを使用します。
詳細については、Swift プログラミング言語(Swift 4.0.3) の 型の属性 を参照してください。
バッファポインタ
バッファポインタ は、メモリ領域への低レベルアクセスに使用されます。たとえば、バッファポインタを使用すると、効率的な処理と、アプリとサービスの間のデータのやりとりを行うことができます。
Swift には、以下のバッファポインタ型があります。
- UnsafeBufferPointer
- UnsafeMutableBufferPointer
- UnsafeRawBufferPointer
- UnsafeMutableRawBufferPointer
バッファポインタ型の、UnsafeBufferPointer および UnsafeMutableBufferPointer を使用すると、連続したメモリブロックを表示または変更できます。これらの型を使用すると、メモリをコレクションとしてアクセスでき、各アイテムは、バッファ型の Element 汎用型パラメータのインスタンスです。
生のバッファポインタ型、UnsafeRawBufferPointer および UnsafeMutableRawBufferPointer を使用すると、連続したメモリブロックを UInt8 値のコレクションとして表示または変更でき、各値はメモリの1バイトに対応します。これらの型を使用すると、コンパイラが確認した型の安全性を持たない生のメモリ上での操作や、同じメモリの複数の異なる型付きの解釈の間の切り替えなど、低レベルのプログラミングパターンを使用できます。
ヌルポインタ
Objective-C では、ポインタ型宣言は _Nullable と _Nonnull 注釈を使用して、そのポインタが nil かまたは NULL 値を持つことができるかどうかを指定できます。Swift では、ヌルポインタは、optional のポインタ型の nil 値で表されます。メモリ内のアドレスの整数表現を取るポインタのイニシャライザは失敗可能です。optional でないポインタ型には nil 値を代入できません。
以下のマッピングが適用されます:
C の構文 | Swift の構文 |
---|---|
const Type * _Nonnull | UnsafePointer<Type> |
const Type * _Nullable | UnsafePointer<Type>? |
const Type * _Null_unspecified | UnsafePointer<Type>! |
ポインタの計算
不透明なデータ型で作業する場合、安全でないポインター操作を実行する必要があります。Swift のポインタ値の計算演算子を使用して、指定されたオフセットで新しいポインタを作成することができます。
- let pointer: UnsafePointer<Int8>
- let offsetPointer = pointer + 24
- // offsetPointer is 24 strides ahead of pointer
<< SWIFT >>
データ型サイズの計算
C では、sizeof および alignof 演算子は任意の変数またはデータ型のサイズと配置を返します。Swift では、MemoryLayout<T> を使用して、size、stride、 および alignment プロパティを通じて、パラメータ化された型 T のメモリレイアウトにアクセスします。たとえば、Darwin の timeval 構造体は、16 の size と stride があり、8 の alignment を持っています。
- print(MemoryLayout<timeval>.size)
- // Prints "16"
- print(MemoryLayout<timeval>.stride)
- // Prints "16"
- print(MemoryLayout<timeval>.alignment)
- // Prints "8"
<< SWIFT >>
これは、型または値のサイズを引数として持つ Swift から C 関数を呼び出すときに使用します。たとえば、 setsockopt(_:_:_:_:_:) 関数は、その値へのポインタとその値の長さを渡すことによって、ソケットの受信タイムアウトオプション(SO_RCVTIMEO) として timeval の値を指定できます。
- let sockfd = socket(AF_INET, SOCK_STREAM, 0)
- var optval = timeval(tv_sec: 30, tv_usec: 0)
- let optlen = socklen_t(MemoryLayout<timeval>.size)
- if setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &optval, optlen) == 0 {
-         // ...
- }
<< SWIFT >>
詳細については、MemoryLayout を参照してください。
1回限りの初期化
C では、POSIX の pthread_once() 関数と Grand Central Dispatch の dispatch_once() 関数と dispatch_once_f() 関数は、初期化コードを正確に1回実行するためのメカニズムを提供します。Swift では、グローバル定数と格納された型プロパティは、複数のスレッドで同時にアクセスされた場合でも、1度だけ初期化されることが保証されています。この機能は言語機能によって提供されるため、対応する POSIX および Grand Central Dispatch の C 関数呼び出しは Swift によって公開されません。
プリプロセッサの指令
Swift コンパイラはプリプロセッサを含みません。代わりに、コンパイル時属性、条件付きコンパイルブロック、および言語機能を利用して、同じ機能を実現します。このため、プリプロセッサの指令は Swift に import されません。
簡単なマクロ
#define ディレクティブ(指令) を典型的に使って C と Objective-C の原始定数を定義する所は、Swift では代わりにグローバル定数を使用します。たとえば、定数定義 #define FADE_ANIMATION_DURATION 0.35 は、Swift では let FADE_ANIMATION_DURATION = 0.35 としてうまく表現できます。単純な定数のようなマクロは Swift のグローバル変数に直接マッピングされるため、コンパイラは C および Objective-C ソースファイルで定義された単純なマクロを自動的に import します。
複雑なマクロ
複雑なマクロは、C および Objective-C で使用されますが、Swift には対応するものがありません。複雑なマクロは括弧で囲まれた、関数のようなマクロを含む定数を定義していないマクロです。型チェック制約を回避するために、または定型コードを大量に再入力しなくて済むように、C と Objective-C では複雑なマクロを使用します。しかし、マクロはデバッグやリファクタリングを困難にします。Swift では、妥協せずに同じ結果を達成するための関数や汎用の物を使用できます。したがって、C と Objective-C のソースファイル内にある複雑なマクロは、あなたの Swift コードでは利用できません。
条件付きコンパイルブロック
Swift のコードと Objective-C のコードは条件付きで、異なる方法でコンパイルされます。Swift のコードは、条件付きコンパイルブロック を使用して条件付きでコンパイルできます。たとえば、以下のコードを swift -D DEBUG_LOGGING を使用してコンパイルして DEBUG_LOGGING 条件付きコンパイルフラグを設定した場合、コンパイラは条件付きコンパイルブロックの本体にコードを含めます。
- #if DEBUG_LOGGING
- print("Flag enabled.")
- #endif
<< SWIFT >>
コンパイル条件 は、リテラルの true 値と false 値、カスタムの条件付きコンパイルフラグ(-D <#flag#> を使用して指定)、および以下の表にリストしたプラットフォーム条件を含めることができます。
関数 | 有効な引数 |
---|---|
os() | macOS, iOS, watchOS, tvOS, Linux |
arch() | x86_64, arm, arm64, i386 |
swift() | >= バージョン番号が続く |
&& および || 演算子を使用してコンパイル条件を組み合わせることができ、! 演算子で、それらを否定し、また #elseif と #else のコンパイル指示文で分岐を追加できます。また、条件付きコンパイルブロックは、他の条件付きコンパイルブロック内に入れ子にもできます。
- #if arch(arm) || arch(arm64)
- #if swift(>=3.0)
- print("Using Swift 3 ARM code")
-         #else
-         print("Using Swift 2.2 ARM code")
- #endif
- #elseif arch(x86_64)
- print("Using 64-bit x86 code.")
- #else
- print("Using general code.")
- #endif
<< SWIFT >>
C のプリプロセッサの条件コンパイルとは対照的に、Swift での条件付きコンパイルブロックは、自己完結型で構文的に有効なコードのブロックを完全に囲まなければなりません。これは、すべての Swift コードがコンパイルされていなくても構文チェックされているためです。ただし、コンパイル条件が swift() プラットフォーム条件を含んでいる場合は例外があります。Swift のコンパイラのバージョンがプラットフォーム条件で指定されたものと一致する場合のみ、文が解析されます。この例外は、古いコンパイラが Swift の新しいバージョンで導入された構文を解析しようとしないことを保証します。
前:Cocoa デザインパターンの採用 次:同じプロジェクトでの Swift と Objective-C