转载

Questing for Swift Source Code - 布尔值

Questing for Swift Source Code - 布尔值

"Questing for Swift Source Code" 系列是我学习 Swift 源码的心得和记录,内容主要是 Swift 源代码的相关分析和探究,如果您对 Swift 源代码也很感兴趣的话,欢迎阅读这个系列的文章。

Swift 源码极其庞大,里面所使用的语言囊括了 Swift、 Python、C++等,因此我觉得这是一个巨大的坑,我不知道能不能把它填完,不过我会尽力而为的~^_^

我们将一起通过分析和学习 Swift 源码,来体会 Swift 这个最前沿编程语言的设计思想,并且从中找出一些鲜为人知的用例!

Swift 的基本类型包含了 “布尔类型 (boolean)”,表示为 Bool。布尔值通常情况下是作为逻辑值来使用的,因为它的值要么为真 (true),要么为假 (false),除此之外就没有其他值了。

定义

首先我们可以看到 Swift 中对 Bool 的定义:

public struct Bool

在 Swift 中,这些基本类型大多都是由结构体 (struct) 定义的,这也准确地表述了基本类型的“值类型 (value-type)”特征。

随后我们可以看到 Bool 中唯一的实际值:

internal var _value: Builtin.Int1

Builtin 是 Swift 内置库之一,由 C++ 写成,位于 Builtins.cpp 文件当中。我们暂时不去了解 Builtin 的具体调用方式,在之后的文章中我们会进行探讨。只需知道,这个操作会调用 Builtin 会调用此文件中的 swift::getBuiltinType(ASTContext &Context, StringRef Name) 函数,这个函数将会给 LLVM 返回一个用以数据处理的类型。Name 参数就是要获取的类型名称。具体该函数的实现和使用我们这里也暂时不介绍。

在这个函数中,有一个非常重要的片段:

// Handle 'int8' and friends. if (Name.substr(0, 3) == "Int") {   unsigned BitWidth;   if (!Name.substr(3).getAsInteger(10, BitWidth) &&     BitWidth <= 2048 && BitWidth != 0)  // Cap to prevent insane things.     return BuiltinIntegerType::get(BitWidth, Context); }

这个函数将会截取 Name 参数的前 3 个字符,这里也就是截取 Int1 的前 3 个字符,从而判断其是不是 "Int" 一族。随后,开始读取 "Int" 之后的内容,这里用到了定义在 StringRef 中的函数 getAsInteger(unsigned Radix, T &Result)。

getAsInteger(unsigned Radix, T &Result) 函数将当前字符串解析为基于指定基数 (radix) 的整数,例如,Radix 指定为 10 表明将以十进制来解析字符串,Radix 指定为 2 将以二进制来解析字符串。如果字符串无法解析的话,那么这个函数就会返回 true。如果字符串成功解析,那么结果就会写到 Result 当中。

这里用预定义的 BitWidth 来存储转换过的数字,通过这个函数的调用,我们将能得到所读取到的数字 1。随后,要判断读取的数字是否在可处理范围之内(也就是(0,2048])。如果成功的话,就返回一个内置的 BuiltinIntegerType 类型,其定义为:

class BuiltinIntegerType : public BuiltinType

这个内置的整数类型会直接与 LLVM 编译器的 IR 整数类型响应。当然,这其中最重要的值是 BuiltinIntegerWidth 类型的 Width。

BuiltinIntegerWidth Width;

这也说明了,对于 Swift 中内置的整数类型来说,无论是 Int8 还是 Int32,都是属于同样的类型,只不过它们的 Width 属性,也就是“位长(占用空间)”不同而已。

因此,Builtin.Int1 就很好理解了:这代表了一个内置的整数类型,它的位长是 1 个字节,也就是只有 “0” 和 “1” 两个值,刚好符合传统上布尔值的定义:0 代表 false,1 代表 true。

初始化

我们下面来查看定义的布尔值初始化方法。

Swift 内部定义了两个初始化方法:

@_transparent public init() {   let zero: Int8 = 0   self._value = Builtin.trunc_Int8_Int1(zero._value) }  @_transparent internal init(_ v: Builtin.Int1) { self._value = v }

设置为 public 的只有一个,也就是我们使用的 let bool = Bool(),这个初始化方法的默认值是 false。

解析:从语义上来看,@_transparent 类似于“将此操作视为一种原始操作(primitive operation)”。该特性会导致编译器在管道(pipeline)中更早地将函数内联。

这两个初始化很简单,第一个是将布尔值初始化为 false 的 init() 方法。它建立了一个值为 0 的 Int8 常量(因为 Swift 内部不支持 Int1 的直接建立),然后通过内置的转换函数,将 Builtin.Int8 转换为 Builtin.Int1。

BooleanLiteral 协议

这里有两个协议,是布尔值的字面量协议扩展,实现这两个协议之后就可以使用 true 和 false 字面量或者 1 和 0 字面量直接赋值了。

注意:只有使用 _BuiltinBooleanLiteralConvertible 协议的方法才能使用 1 和 0 字面量进行赋值的,这里的 1 和 0 字面量都会被自动转换位 Builtin.Int1 类型。

_BuiltinBooleanLiteralConvertible 需要实现的是 public init(_builtinBooleanLiteral value: Builtin.Int1) 方法,BooleanLiteralConvertible 需要实现的是 public init(booleanLiteral value: Bool) 方法,这两个方法都被标注为了 @_transparent 作为原始操作。

解析:在目前 Swift 的规则当中,前面加上"_"(下划线)的协议、方法、变量等等都是只在内部有效的,也就是说它们需要设定为 internal 的权限。

我们可以在外面实现 BooleanLiteralConvertible 协议来实现此功能,您可以打开 Playground,写入以下代码来体验一下:

struct TestBoolean: BooleanLiteralConvertible {     var value: Int = 0          init() { }          init(booleanLiteral value: Bool) {       self.value = value ? 1 : 0     } }  var test: TestBoolean = true test.value

这样您就可以看到,我们可以用布尔字面量给我们自定义的 TestBoolean 结构体赋值了。

BooleanType 协议

这个协议主要需要实现一个 boolValue 的只读属性,其类型是 Bool。

在 Swift 源码的实现中,除了这个只读属性外,它还实现了两个方法:

@_transparent @warn_unused_result public func _getBuiltinLogicValue() -> Builtin.Int1 {   return _value }  /// Construct an instance representing the same logical value as /// `value`. public init(_ value: T) {   self = value.boolValue }

第一个方法是用以获取内置的实际值的,这是一个内部方法,它返回一个 Builtin.Int1 值。

解析:@warn_unused_result 是一个 GCC 内核中自带的关键字,这个关键字用于检查这个函数被调用的时候是否使用了其返回值,否则的话编译器会对其警告。换句话说,如果只是单纯的调用了此函数,并没有利用其返回值的话(比如说继续判断、赋值等等),那么编译器会弹出警告。

比如,如果上面的函数这样调用的话:true._getBuiltinLogicValue(),那么编译其就会弹出警告,除非使用 let value = true._getBuiltinLogicValue()。

第二个方法就比较有意思了,它可以根据一个同样实现 BooleanType 协议的值来实现自身的初始化。

我们继续使用上面创造的那个 TestBoolean 结构体,在原来的基础上加上:

extension TestBoolean: BooleanType {     var boolValue: Bool {         return self.value == 1     } }

这样你就可以可以使用下列语句创建一个新的布尔值了:

var bool: Bool = Bool(test) // 输出值为 true

CustomStringConvertible 协议

这是一个 Swift 中绝大多数类型都实现的协议,我们将在探寻 CompilerProtocols 文件时统一进行介绍。

布尔值实现此协议后,你就会发现它能够显示出其字面量的字符串,是 "true" 还是 "false"。

Equatable、Hashable 协议

这是一个 Swift 中大多数类型都实现的协议,我们将在探寻 CompilerProtocols 文件时统一进行介绍。

布尔值实现此协议后,你就会发现它可以使用 == 来判断两个布尔值是否相等。

其他

除此之外,布尔值源文件中还定义了两个方法:

第一个是一个根据 Builtin.Int1 获取布尔值的全局函数:

// This is a magic entry point known to the compiler. @_transparent public // COMPILER_INTRINSIC func _getBool(v: Builtin.Int1) -> Bool

第二个是一个取相反布尔值的运算符函数:

// Unary logical complement. @_transparent @warn_unused_result public prefix func !(a: Bool) -> Bool

这样我们就可以通过 ! 运算符来实现取布尔值的相反值了。

总结

在本文当中,我们介绍了以下几点内容:

  • Swift 中布尔值的基本定义

  • Builtin 的介绍

  • swift::getBuiltinType(ASTContext &Context, StringRef Name) 函数的部分内容

  • getAsInteger(unsigned Radix, T &Result) 函数作用

  • @_transparent 和 @warn_unused_result 关键字

  • BooleanLiteral 协议

  • BooleanType 协议

至此,这就是我们所看到的 Swift 布尔值的自举部分内容了,这是一个非常简单的类型,但是我们在其中也能看到很多精彩的内容。

原文  http://www.cocoachina.com/swift/20160218/15318.html
正文到此结束
Loading...