Swift互用性:与 C的API交互(Swift 2.0版)-b

节包含内容:

  • 基本数据类型(Primitive Types)
  • 枚举(Enumerations)
  • 指针(Pointer)
  • 全局常量(Global Constants)
  • 预处理指令(Preprocessor Directives)

作为与Objective-C语言的互用性的一部分,Swift也对一些C语言的类型和特性保持了兼容性。如果你的代码有需要,Swift也提供了一些方式来使用常见的C结构和模式。

基本数据类型

Swift提供了一些与C语言基本类型如char,int,float和double等的对应类型。然而,这些类型和Swift核心基本类型之间不能进行隐式转换,如Int。因此,只有你的代码明确要求时才使用这些类型,其它任何可能的情况下都应该使用Int。

枚举

任何用宏NS_ENUM来声明的C风格的枚举,都会被Swfit导入为一个Swfit枚举类型。无论枚举值是在系统框架还是在自己的代码中定义的,当它们导入到Swift时,它们的前缀名将被截掉。

例如,看这个Objective-C枚举的声明:

    //Objective-C
    typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
    };

在Swift中,会被导入为这样:

    //Swift
    enum UITableViewCellStyle: Int {
        case Default
        case Value1
        case Value2
        case Subtitle
    }

当你需要使用一个枚举值时,使用以点(.)开头的枚举名称:

    //Swift
    let cellStyle: UITableViewCellStyle = .Default

选项集
对使用宏NS_OPTIONS声明的C风格的枚举,Swift会把它导入为一个Swfit选项集类型。选项集像枚举一样,会把前缀截掉,只剩下选项值名称。

例如,看这个Objective-C选项的声明:

    //Objective-C
    typedef NS_OPTIONS(NSUInteger, NSJSONReadingOptions) {
        NSJSONReadingMutableContainers = (1UL << 0),
        NSJSONReadingMutableLeaves = (1UL << 1),
        NSJSONReadingAllowFragments = (1UL << 2)
    };

在Swift中,它被导入为这样:

    //Swift
    struct NSJSONReadingOptions : OptionSetType {
        init(rawValue: UInt)
        static var MutableContainers: NSJSONReadingOptions { get }
        static var MutableLeaves: NSJSONReadingOptions { get }
        static var AllowFragments: NSJSONReadingOptions { get }
    }

在Objective-C中,一个选项集是整数值的一个位掩码。你可以使用按位或操作符(|)来组合选项值,使用按位与操作符(&)以检测选项值。创建一个选项集,可以使用常量值或者表达式。一个空的选项集使用常数0来表示。

在Swift中,选项集使用一个遵循OptionSetType协议的结构体来表示,其中每个选项值都是一个静态变量。选项集类似于Swift的集合类型Set,你可以用insert(_:)或者unionInPlace(_:)方法来添加选项值,用remove(_:)或者subtractInPlace(_:)方法来删除选项值,用contains(_:)方法来检测选项值。创建一个选项集的值可以使用一个数组字面量,里面的选项值像枚举一样都用点(.)开头。创建一个空的选项集可以使用一个空的数组字面量,也可以调用默认初始化函数。

    //Swift
    let options: NSDataBase64EncodingOptions = [
        .Encoding76CharacterLineLength,
        .EncodingEndLineWithLineFeed
    ]
    let string = data.base64EncodedStringWithOptions(options)

共用体

Swift仅部分支持C的共用体(union)类型。在导入混有C的共用体或者位段(bitfields)的类型时,例如Foundation的NSDecimal类型,Swift不能存取不支持的字段。但是,参数和/或返回值为这些类型的C和Objective-C的API是能够在Swift中使用的。

指针

Swift尽可能避免让您直接访问指针。然而,当您需要直接操作内存的时候,Swift也为您提供了多种指针类型。下面的表使用Type作为类型名称的占位符。

对于返回类型,变量和参数,使用如下形式:

对于类(class)类型,使用如下形式:

常量指针

当一个函数被声明为接受UnsafePointer参数时,这个函数可以接受下列任何一个类型作为参数:

  • nil,作为空指针传入;
  • 一个UnsafePointer,UnsafeMutablePointer, 或者AutoreleasingUnsafeMutablePointer的值,在必要情况下会转换成UnsafePointer的值;
  • 一个String类型的值,如果Type是Int8或者UInt8的话。该字符串会自动在一个缓冲区内被转换为UTF8,该缓冲区在本次调用期间有效;
  • 一个左值操作数为Type类型的输入输出(inout)表达式,传入的是这个左值的内存地址;
  • 一个[Type]值,传入该数组的起始指针,并且它的生命周期将在本次调用期间被延长。

如果您这样定义了一个函数:

   //Swift
    func takesAPointer(x: UnsafePointer) { /*...*/ }

那么您可以使用以下任何一种方式来调用这个函数:

   //Swift
    var x: Float = 0.0
    var p: UnsafePointer = nil
    takesAPointer(nil)
    takesAPointer(p)
    takesAPointer(&x)
    takesAPointer([1.0, 2.0, 3.0])

如果函数被声明为使用一个UnsafePointer参数,那么这个函数接受任何Type的UnsafePointer类型的操作数。 ? 如果您这样定义了一个函数:

   //Swift
    func takesAVoidPointer(x: UnsafePointer)  { /* ... */ }

那么您可以使用以下任何一种方式来调用这个函数:

   //Swift
    var x: Float = 0.0, y: Int = 0
    var p: UnsafePointer = nil, q: UnsafePointer = nil
    takesAVoidPointer(nil)
    takesAVoidPointer(p)
    takesAVoidPointer(q)
    takesAVoidPointer(&x)
    takesAVoidPointer(&y)
    takesAVoidPointer([1.0, 2.0, 3.0] as [Float])
    let intArray = [1, 2, 3]
    takesAVoidPointer(intArray)

可变指针

当一个方法被声明为接受UnsafeMutablePointer参数时,这个函数可以接受下列任何一个类型作为参数:

  • nil,作为空指针传入;
  • 一个UnsafeMutablePointer类型的值;
  • 一个输入输出(inout)表达式,其左值操作数是Type类型的,且被存储起来了。传入的是这个左值的内存地址;
  • 一个输入输出的[Type]类型的值,传入的是该数组的起始指针,并且它的生命周期将在本次调用期间被延长。

如果您这样定义了一个函数:

   //Swift
    func takesAMutablePointer(x: UnsafeMutablePointer) { /*...*/ }

那么您可以使用以下任何一种方式来调用这个函数:

   //Swift
    var x: Float = 0.0
    var p: UnsafeMutablePointer = nil
    var a: [Float] = [1.0, 2.0, 3.0]
    takesAMutablePointer(nil)
    takesAMutablePointer(p)
    takesAMutablePointer(&x)
    takesAMutablePointer(&a)

如果函数被声明使用一个UnsafeMutablePointer参数,那么这个函数接受任何Type的UnsafeMutablePointer类型的操作数。

如果您这样定义了一个函数:

//Swift
func takesAMutableVoidPointer(x: UnsafeMutablePointer)  { /* ... */ }

那么您可以使用以下任何一种方式来调用这个函数:

   //Swift
    var x: Float = 0.0, y: Int = 0
    var p: UnsafeMutablePointer = nil, q: UnsafeMutablePointer = nil
    var a: [Float] = [1.0, 2.0, 3.0], b: [Int] = [1, 2, 3]
    takesAMutableVoidPointer(nil)
    takesAMutableVoidPointer(p)
    takesAMutableVoidPointer(q)
    takesAMutableVoidPointer(&x)
    takesAMutableVoidPointer(&y)
    takesAMutableVoidPointer(&a)
    takesAMutableVoidPointer(&b)

自动释放指针

当一个函数被声明为接受AutoreleasingUnsafeMutablePointer参数时,这个函数可以接受下列任何一个类型作为参数:

  • nil,作为空指针传入;
  • 一个AutoreleasingUnsafeMutablePointer类型的值;
  • 一个输入输出(inout)表达式,其操作数首先被拷贝到一个无拥有者的缓冲区,传递给被调用函数的就是这个缓冲区的地址。在调用返回时,缓冲区中的值被加载、保存、并重新复制给操作数。

注意,这个列表中没有包含数组。

如果您这样定义了一个函数:

   //Swift
    func takesAnAutoreleasingPointer(x: AutoreleasingUnsafeMutablePointer) { /* ... */ }

那么您可以使用以下任何一种方式来调用这个函数:

   //Swift
    var x: NSDate? = nil
    var p: AutoreleasingUnsafeMutablePointer = nil
    takesAnAutoreleasingPointer(nil)
    takesAnAutoreleasingPointer(p)
    takesAnAutoreleasingPointer(&x)

被指针指向的类型并不会被桥接。例如,NSString **转换到Swift后,是AutoreleasingUnsafeMutablePointer<nsstring?>,而不是AutoreleasingUnsafeMutablePointer<string?>。

函数指针

C语言的函数指针通过调用约定,以闭包的形式被引入Swift中,表示形式为@convention(c)。例如,一个类型为int (*)(void)的C语言函数指针,会转换为Swift的@convention(c) () -> Int32。

在调用一个以函数指针为参数的函数时,给它传的值可以是一个顶层的Swift函数,也可以是个闭包字面量,或者nil。只有符合C语言函数指针调用约定的Swift函数,才能用来给函数指针类型的形参传值。例如,Core Foundation的CFArrayCreateMutable(_:_:_:)函数,它有个参数的类型为CFArrayCallBacks结构体。这个CFArrayCallBacks结构体就是用一些函数指针进行初始化的:

   func customCopyDescription(p: UnsafePointer) -> Unmanaged! {
        // return an Unmanaged! value
    }
    let callbacks = CFArrayCallBacks(
        version: 0 as CFIndex,
        retain: nil,
        release: nil,
        copyDescription: customCopyDescription,
        equal: { (p1, p2) -> Boolean in
            // return Boolean value
        }
    )
    var mutableArray = CFArrayCreateMutable(nil, 0, callbacks)

在上面的例子中,在CFArrayCallBacks初始化时,传给retain和release作参数的是nil,传给copyDescription作参数的是函数customCopyDescription,传给equal作参数的是一个闭包字面量。

全局常量

在C和Objective-C语言源文件中定义的全局常量会自动地被Swift编译引进并做为Swift的全局常量。

预处理指令

Swift编译器不包含预处理器。取而代之的是,它充分利用了编译时属性,生成配置,和语言特性来完成相同的功能。因此,Swift没有引进预处理指令。

简单宏

在C和Objective-C中,通常使用#define指令来定义一个简单的常数,在Swift,您可以使用全局常量来代替。例如:定义一个常数的#define FADE_ANIMATION_DURATION 0.35,在Swift使用let FADE_ANIMATION_DURATION = 0.35来表述会更好一些。由于简单的用于定义常量的宏会被直接被映射成Swift全局量,Swift编译器会自动引进在C或Objective-C源文件中定义的简单宏。

复杂宏

在C和Objective-C中使用的复杂宏在Swift中没有相对应的东西。复杂宏是那些不用来定义常量的宏,包含了括号的函数式宏。您在C和Objective-C使用复杂的宏以避免类型检查的限制或避免重新键入大量的样板代码。然而,宏也会造成debug和重构起来更困难。在Swift中你可以使用函数和泛型来达到同样的效果,而没有任何的委屈折中。因此,在C和Objective-C源文件中定义的复杂宏在Swift是不能使用的。

生成配置

Swift代码使用和C、Objective-C代码不同的方式进行条件编译。Swift代码可以根据生成配置的组合进行条件编译。生成配置包括true和false字面值,命令行标志,和下表中的平台测试函数。您可以使用-D <#flag#>指定命令行标志。

注意:生成配置arch(arm)不会为64位ARM设备返回true,生成配置arch(i386)在为32位iOS模拟器编译代码时会返回true。

一个简单的条件编译可以像下面这段代码:

   #if build configuration
      statements
    #else
      statements
    #endif

由零个或多个有效的Swift语句组成的statements,可以包括表达式,普通语句和控制流语句。可以使用&&和||操作符往一个条件编译语句上添加新的编译条件,使用!操作符来否定某条件,使用#elseif来添加编译块:

    #if build configuration && !build configuration
      statements
    #elseif build configuration
      statements
    #else
      statements
    #endif

条件编译不同的是,Swift条件编译的语句必须是独立完整、语法有效的代码块。这是因为所有的Swift代码都会做语法检查,而不管会不会被编译。

时间: 2024-10-06 11:46:29

Swift互用性:与 C的API交互(Swift 2.0版)-b的相关文章

Swift互用性: 使用Objective-C特性编写Swift类(Swift 2.0版)-b

本节包括内容: 继承Objective-C的类(Inheriting from Objective-C Classes) 采用协议(Adopting Protocols) 编写构造器和析构器(Writing Initializers and Deinitializers) 集成Interface Builder(Integrating with Interface Builder) 指明属性特性(Specifying Property Attributes) 实现Core Data Manage

Swift互用性:采用Cocoa设计模式(Swift 2.0版)-b

本页包含内容: 委托(Delegation) 错误处理(Error Handling) 键值观察(Key-Value Observing) Target-Action模式(Target-Action) 类型匹配与统一规范(Introspection) API 可用性 使用 Cocoa 现有的一些设计模式,是帮助开发者开发一款拥有合理设计思路.稳定的性能.良好的可扩展性应用的有效方法之一.这些模式都依赖于在 Objective-C 中定义的类.因为 Swift 与 Objective-C 的互用性

Swift互用性:与 Objective-C 的 API 交互(Swift 2.0版更新)-备

本页包含内容: 初始化 可失败初始化 访问属性 方法 id 兼容性(id Compatibility) 空值和可选值 扩展(Extensions) 闭包(Closures) 比较对象 Swift 类型兼容性 动态分发 轻量级泛型 Objective-C 选择器(Selectors) 互用性是让 Swift 和 Objective-C 相接合的一种特性,使你能够在一种语言编写的文件中使用另一种语言.当你准备开始把 Swift 融入到你的开发流程中时,你应该懂得如何利用互用性来重新定义并提高你写 C

Swift互用性:与 Cocoa 数据类型共舞(Swift 2.0版)-b

本节内容包括: 字符串(Strings) 数值(Numbers) 集合类(Collection Classes) 错误(Errors) Foundation数据类型(Foundation Data Types) Foundation函数(Foundation Functions) Core Foundation 作为对 Objective-C 互用性(互操作性)的一部分,Swift提供快捷高效的方式来处理 Cocoa 数据类型. Swift 会自动将一些 Objective-C 类型转换为 Sw

The Swift Programming Language-官方教程精译Swift(5)集合类型 -- Collection Types

Swift语言提供经典的数组和字典两种集合类型来存储集合数据.数组用来按顺序存储相同类型的数据.字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对). Swift语言里的数组和字典中存储的数据值类型必须明确. 这意味着我们不能把不正确的数据类型插入其中. 同时这也说明我们完全可以对获取出的值类型非常自信. Swift对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误. 注意: Swift的数组结构在被声明成常

The Swift Programming Language-官方教程精译Swift(4)字符串和字符

String 是一个有序的字符集合,例如 "hello, world", "albatross".Swift 字符串通过 String 类型来表示,也可以表示为 Character 类型值的集合. Swift 的 String 和 Character 类型提供了一个快速的,兼容 Unicode 的方式来处理代码中的文本信息.创建和操作字符串的语法与 C的操作方式相似,轻量并且易读.字符串连接操作只需要简单地通过 + 号将两个字符串相连即可.与 Swift 中其他值一

Swift 中的开关语句switch在swift中的使用

在swift 中使用switch 开关语句在进行匹配的时候不需要在匹配成功的地方加上break了,在swift中它会自动在匹配成功的地方跳出去.不会在向下面执行 example: “let vegetable = "red pepper"switch vegetable {case "celery": let vegetableComment = "Add some raisins and make ants on a log."case &qu

The Swift Programming Language-官方教程精译Swift(6)控制流--Control Flow

Swift提供了类似C语言的流程控制结构,包括可以多次执行任务的for和while循环,基于特定条件选择执行不同代码分支的if和switch语句,还有控制流程跳转到其他代码的break和continue语句. 除了C里面传统的 for 条件递增循环,Swift 还增加了 for-in 循环,用来更简单地遍历数组(array),字典(dictionary),范围(range),字符串(string)和其他序列类型. Swift 的 switch 语句比 C 语言中更加强大.在 C 语言中,如果某个

The Swift Programming Language-官方教程精译Swift(8)闭包 -- Closures

闭包是功能性自包含模块,可以在代码中被传递和使用. Swift 中的闭包与 C 和 Objective-C中的 blocks 以及其他一些编程语言中的 lambdas 比较相似. 闭包可以捕获和存储其所在上下文中任意常量和变量的引用. 这就是所谓的闭合并包裹着这些常量和变量,俗称闭包.Swift会为您管理在捕获过程中涉及到的内存操作.  注意:如果您不熟悉 捕获 (capturing) 这个概念也不用担心,后面会详细对其进行介绍. 在 函数 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采