Swift for AS3 developers 系列

Swift 类型推断

原文:Swift type inference
翻译:dreamana.com

Flash 开发者们经常认为能够声明变量的类型是最喜欢的 ActionScript 特性之一,这使得切换到其他动态语言(比如 JavaSctipt)变得步履艰难。使用了类型,编译器可以很容易地捕获错误,可以优化字节码使得我们的游戏和 Apps 运行得更快;编辑器带代码提示功能的,可以在我们每次敲键盘的时候提供大量关于代码库的信息,而重构时能让我们跳过繁琐又易错的跨多个文件编辑。就像 ActionScript, Apple 的新语言采用几乎一样的语法提供一样的便利:

// ActionScript
var toggle:Boolean = false;
var integer:int = 2;
var ratio:Number = 0.5;
var text:String = "Hello, ActionScript!";
var custom:MyClass = new MyClass();

// Swift
var toggle:Bool = false
var integer:Int = 2
var ratio:Double = 0.5
var text:String = "Hello, Swift!"
var custom:MyClass = MyClass()

不过,Swift 更进了一步。如果我们可以不用每次都声明变量类型,就可以获得严格编译器、额外优化和所有强大编辑器所带来的好处会怎样呢?我正是在说类型推断 (type inference) 特性,Swift 有!其实我们可以简化以上的代码,因为编译器懂得自动检测每一个变量的类型,不需要额外的注释:

// Swift
var toggle = false
var integer = 2
var ratio = 0.5
var text = "Hello, Swift!"
var custom = MyClass()

编译器很聪明,它能明白你声明了一个 Bool 型,一个 Int 型,一个 Double 型,一个 String 型和一个 MyClass 实例,因为那就是等号的另一边的东西。如果你阅读代码,这很明显,Swift 编译器也这么认为。然而,如果代码变得有点复杂时,可能你不能一眼就看出变量的类型是什么。那么就去声明类型吧,提高可读性,用上面第一个例子的语法。也两者中是最好的。

正如你看到的这些例子,Swift 不使用 new 关键字去实例化一个新对象,一行末尾的分号实际不是必需的。加上类型推断,有三个方式使你花更少时间去打字,更快地完成产品。每天都节省了时间,与此同时,你不会失去来自编译器和编辑器的强大的语言智能特性。

July 16, 2014

Swift 可选类型 (optionals) 与 nil

原文:Swift optionals and nil
翻译:dreamana.com

在 ActionScript 里,你可以将任何变量设为 null,这个 null 值基本的意思是这变量引用“空”。移除了引用,对象就可以被垃圾回收 (garbage collection):

// ActionScript
var text:String = "Hello, ActionScript!";
text = null;

大多数 Flash 开发者应该都对一个 ActionScript 运行时错误非常熟悉:

TypeError: Error #1009: Cannot access a property or method of a null object reference.

这个意思是你尝试调用某个方法或者通过一个变量访问某个属性,但是那个变量为 null。你真的没料到它会为 null,可能你也不知道为什么会是 null。所人人都一次又一次遇到这个错误,然后就非常折腾花时间去调试 (debug)。

Swift 也有一个 nil 值允许变量引用“空”。然而,Swift 对怎样使用 nil 比较严格。你不能将任意变量设为 nil。如果你尝试复制上面的代码到 Swift,编译器会报错:

// Swift
var text:String = "Hello, Swift!"
text = nil // error!

注意:为表达更清晰,我在这段代码里选用类型注解而不用 类型推断;

在 Swift 中使用 nil,你需要用到可选类型 (optionals)

// Swift
var text:String? = "Hello, Swift!"
text = nil // this is acceptable

注意到字符串类型后面的 ? 了吗。这告诉了编译器这个变量可以设为字符串,也允许设为 nil。

如果你将一个非可选 (non-optional) 字符串类型赋值为可选字符串类型,同样也会得到编译器报错:

// Swift
var text:String? = "Hello, Swift!"
var notOptional:String = text // error

同样,我们不希望在不需要的时候意外地引入了 nil 值。

怎样能使一个非空 (non-nil) 值对得上可选类型呢?我们可使用一种叫做可选类型绑定 (optional binding) 的方法:

// Swift
var text:String? = "Hello, Swift"
if let notNilText:String = text
{
    // the value is not nil
}
else
{
    // the value is nil
}

这个 if 语句首先检查这个 text 变量是否为 nil。如果变量为 nil,马上就会跳到 else 的区块;如果变量不为 nil,那么它就会被赋值到一个临时非可选常量(如你喜欢,这里可以换用变量代替常量),而且 if 的区块也会被执行。那么你就可以使用这个临时常量 / 变量而无需担心它是否为 nil。

在 ActionScript 里是非常容易将 null 值传到一个预料不到的地方。而 Swift 编译器会要求你选择性加入接受 nil 值。多亏有了可选类型 (optionals),你只需要在你想用的地方处理 nil,而且你可以确信你会不意外地遇到 nil 值错误,因为编译器在你背后盯着你。

July 16, 2014

Swift 遍及网络

原文:Swift around the web
翻译:dreamana.com

在网络上各个地方,开发者们都在写一些关于 Swift 的很有见地的文章,分享一些很有帮助的教程。Swift 对于大家来说都是新的 ——即使是有多年开发 iOS/OS X 应用经验的人。我们都在一起学习新语言,这种分享的文化让我想起以前使用 Flash 的美好时光。下面选取了一些我特别感兴趣的文章。

Swift in Flux 是关于 Swift 语言变动的必备清单。类似的,Swift Programming Language Document Revision History 这个官方文档也列出来最近更新(和改动)。Swift 的最终第一版指日可待,但是 Apple 仍然需要时间去更新它。看一下这些文档还是不错的,不会让你错过最近的更新。

提到最近的更新,Apple 在 Swift Beta4 里面增加了 访问控制 (Access Control)。可选择 private, internal, public。

Type Variance in Swift 详细讲述 Swift 的多态,尤其在使用泛型和函数的时候。很好很深入地查看 Swift 是怎样运作的。

Swift: Init with _ 给你展示如何使用 _ 去创建 initializers 而不需要命名参数。显然,ActionScript 里没有 命名参数,因此我喜欢像这样的文章,让我更好了解何时、为何我要使用这些我不熟悉的特性。Nil-coalescing Operator in Swift 介绍了一个自定义操作符,能让你只需要一行代码实现:如果 optional 是 nil 的时候退回到默认值

// Swift
let quantity = quantityTextField.text.toInt() !! 1

在 Swift 中建立 assert(), Part 1: 惰性求值 (Lazy Evaluation) 是 Apple 的官方博客,展示 Swift 很酷很高级的特性及叫做 auto-closures 仅在需要时才计算表达式。实际上,前面的 nil-coalescing operator 文章提到使用 auto-closures 来实现:仅当 optional 是 nil 的时候才计算右边的表达式。

祝你阅读愉快!欢迎回访,获取更多为 Flash 开发者写的 Swift 提点。

July 25, 2014

Swift 函数与参数名

原文:Swift functions and parameter names
翻译:dreamana.com

函数是编程中基本的构建块。通过他们重用代码来帮助你避免重劳动,而且也是将代码分解成更小更易读的部分的一种好方法。我们值得庆幸的是,在 Swift 里面声明和调用一个简单的函数,出乎意料的,跟我们在 ActionScript 里面做法类似。在 Swift 里使用函数有种宾至如归的感觉之外,我们将会再看看 Swift 怎么提高代码的可读性,通过将变量名作为函数标识的一部分。

我们从实现一个简单的函数 “clamp” 开始,这函数确保数值限制在一个最小值到最大值之间的范围内。如你所见,在两种不同语言里的代码写得几乎一样:

// ActionScript
function clampNumber( number:Number, min:Number, max:Number ):Number
{
    if( number < min )
    {
        return min;
    }
    else if( number > max )
    {
        return max;
    }
    return number;
}

// Swift
func clampNumber( number:Double, min:Double, max:Double ) -> Double
{
    if( number < min )
    {
        return min;
    }
    else if( number > max )
    {
        return max;
    }
    return number;
}

这里不同的地方只是:ActionScript 使用 function 作关键字,而 Swift 使用更短的 func 作为函数关键字; ActionScript 使用 :(冒号 )表示返回类型,而 Swift 使用 -> (箭头)。参数定义方式一样,两种语言都是用 return 关键字来返回值。

两种语言调用函数看起来一样:

// ActionScript
var clamped:Number = clampNumber( temperature, freezingPoint, boilingPoint );

// Swift
var clamped = clampNumber( temperature, freezingPoint, boilingPoint )

这种级别的相似,很明显,熟悉 ActionScript 的人很快就会适应 Swift。那么,下面再看看 Swift 的函数还能额外做点什么。

使用外部参数名 (external parameter names) 提高代码的可读性

在大多数语言里面,传递到函数里的参数需要按照特性的顺序来写。如果你在阅读一些不熟悉的代码,传进函数的值都没有揭示函数内部如何使用这个参数。当你想弄懂这些不熟悉的代码,你可能需要花一点时间去查函数标识。Swift 提供了一种有趣的方式,在调用函数的时候可以包含参数名,紧接着变量值,以提高可读性。

我们用 Swift 给 clampNumber() 函数添加标识:

// Swift
func clampNumber( number:Double, #min:Double, #max:Double ) -> Double

那个 # 哈希符号告诉编译器第二和第三个参数包含外部参数名 (external parameter names)。当我们现在调用 clampNumber() 函数,我们需要指定外部参数名作为调用的一部分:

// Swift
var clamped = clampNumber( temperature, min: freezingPoint, max: boilingPoint )

带着参数名调用函数,让所有阅读这段代码的人都清楚第二个参数是最小值和第三个参数是最大值。不需要去查哪一个在先。

你发现我们没有给第一个参数创建外部名。在 Swift 里,通常习惯上会将一个参数名包含在函数名里面。这里的 clampNumber(),就是以 Number 结尾。我们可以推断的第一个参数是那个要被限制的数值。

(译者注:Swift 的这一特性与函数命名风格,其实是继承了 Objective-C 的函数调用的优点,就是为了保留可读性。只不过 Objective-C 面不叫做函数调用,而是消息传递。)

** 带有描述性外部名的缩写内部名 **

外部参数名不一定要跟在函数里面使用内部变量名一样。有时,我们可能想使用更具描述性的外部参数名。我们再一次重写 clampNumber() 的标识(这次我会包含函数体,更加清晰):

// Swift
func clampNumber( number:Double, fromMinimum min:Double, toMaximum max:Double ) -> Double
{
    if( number < min )
    {
        return min;
    }
    else if( number > max )
    {
        return max;
    }
    return number;
}

注意第二和第三个参数现在有了两个名字。函数体不变,仍然用 min 和 max 变量名。但是调用 clampNumber() 稍微要改一点:

// Swift
var clamped = clampNumber( temperature, fromMinimum: freezingPoint, toMaximum: boilingPoint )

如果函数参数有 2 个名字,第一个就是外部参数名。当你调用函数的时候要用,并且应该具有描述性。第二个是内部参数名,你可以使用更短的名字而不需要在函数内部一次又一次地打长名字。

Swift 的 Cocoa 设计模式:用 target-action 监听事件

原文:Cocoa design patterns in Swift: listening to events with target-action
翻译:dreamana.com

我们常常在 ActionScript 代码中监听事件,不论是需要知道显示对象什么时候被鼠标点击,又或是什么时候图片文件加载完成。在 Flash Runtime 中,有大量的类是继承 EventDispatcher 的,我们可以调用 addEventListener() 去监听事件:

// ActionScript
object.addEventListener( MouseEvent.CLICK, listener );

你可能惊奇地发现 iOS 的核心框架不提供单独一个使对象获得派发事件能力的基类。然而,这并不意味着你需要学各种不同的方法去实现相同的东西。Apple Cocoa 框架通常依赖于共享的设计模式去保持一致性。其中一个模式,叫 target-action,让你可以在事件发生的时候调用对象的函数,比如当按钮按下或者滑动条的值改变的时候。如果你认为这听起来像派发到事件监听者,那就对了。

target-action 与 UIControl

UIControl 是很多 iOS 用户界面组件的基类,比如 UIButton 和 UISilder. 它不只是简单地将东西渲染到屏幕的类(是它的超类 UIView 实现的)。一个 UI 控件同时也跟用户输入交互,如触摸或键盘打字。点击按钮会按下,拖动滑块滑动条的值会改变。UIControl 实现了 target-action 模式,因此我们可像这样收到事件通知。

监听按钮什么时候被按下。我们从创建一个 UIButton 开始:

// Swift
let button = UIButton.buttonWithType( UIButtonType.System ) as UIButton
button.setTitle( "Click Me", forState: UIControlState.Normal )
button.sizeToFit()
view.addSubview( button )

现在,看看 target-action 模式是怎样让我们知道按钮按下:

// Swift
button.addTarget( self, action: "buttonPressed", forControlEvents: UIControlEvents.TouchUpInside )

我们传递了三个参数到 addTarget( : action: forControlEvents: ).

  1. target 是事件发生时通知的对象。我们可以传 self,类似 ActionScript 的 this.
  2. action method 是在 target 对象上定义函数。action method 用字符串表示 ——被称为选择器 (selector)。待会我们会更详细解释选择器。主要是,我们让它去调用 buttonPressed(),这个是 self 的成员方法。
  3. 最后,因为 UI 控件通常有多个事件,我们可以指定我们关心哪一个事件类型 (event type)。就像是给 ActionSctipt 里的 addEventListener() 传 Event.CHANGE 或者 MouseEvent.CLICK.

最基本的 action 方法像这样:

// Swift
func buttonPressed()
{
    println( "button pressed" )
}

注意到这个特定 action 方法没有参数。不需要附加信息的时候我们可以保持简单。但有时候我们可能想知道多一点关于事件的信息。

在 ActionScript 中,我们通常会给多个对象添加同一个监听器 (listener),而且我们需要知道是哪一个对象派发了事件:

// ActionScript
function buttonPressed( event:MouseEvent ):void
{
    var button:Button = Button( event.currentTarget );
    trace( "button pressed:", button.label );
}

我们也可以在 Swift 获取派发事件的对象。在最先的例子里面,当调用 addTarget(:action:forControlEvents:),我们用了一个选择器 (selector) 定义监听器 (listener)。我们可以修改 selector 去表明我们想那个发送者 (sender) 作为单独参数传给我们的监听器。sender 是派发事件的对象。只需简单地添加 : 冒号在 selector 的末尾就定义了这个方法接受一个参数,这样:

// Swift
button.addTarget( self, action: "buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside )

这个冒号表示方法的地方一个参数。之前,我们讨论 函数与参数名,我们知道调用方法的时候第一个参数名会被省略。相同的原因,“buttonPressed:” 选择器在冒号前省略第一个参数名。如果监听器需要接收多于一个参数,附加的参数需要在选择器中指定它们的外部参数名。

作为例子,addTarget(:action:forControlEvents:) 函数的选择器是 “addTarget:action:forControlEvents:” 基本上,你需要去掉括号,包含一个冒号作为第一个参数,然后包含每一个剩下的参数的外部名和冒号。

当我们添加一个单独冒号到选择器,监听器必须改成接受一个参数以表示发送者 (sender):

// Swift
func buttonPressed( sender:UIButton )
{
    let buttonTitle = sender.titleForState( UIControlState.Normal )
    println( "button pressed: \( buttonTitle )" )
}

我们可以定义 sender 的类型,所以不需要做类型转换 (type casting) 了。

target-action 变种

不是所有类都实现 target-action 模式派发多于一种事件类型。这些情况,你只需指定一个 target 和一个 action 方法。例如,UITapGestureRecognizer 类只有一种事件:通知你何时界面被点中。只有这个事件,因此指定事件类型不是必须的。

为了演示出区别,现在不监听 UIButton 的 UIControlEvents.TouchUpInside 事件。而是给按钮添加一个点击手势识别器,监听来自手势识别器的事件。

let gesture = UITapGestureRecognizer()
button.addGestureRecognizer( gesture )
gesture.addTarget( self, action: "buttonTapped:" )

类似上面,我们指定一个 target 和一个 action 方法,但因为 UITapGestureRecognizer 只有一种事件,它的 addTarget(:action:) 函数值接受 2 个参数而不用 3 个。

因为是监听来自 UITapGestureRecognizer 的事件而不是 UIButton 的事件,我们需要写一个稍微不同的 action 方法:

func buttonTapped( sender:UITapGestureRecognizer )
{
    let button = sender.view as UIButton
    let buttonTitle = button.titleForState( UIControlState.Normal )
    println( "button tapped: \( buttonTitle )" )
}

Cocoa 设计模式

Apple 的 Cocoa 框架常使用 target-action 和其他设计模式去提供一致性的架构。虽然实现上和 ActionScript 的 EventDispatcher 类有一点区别,但监听事件 (这个概念) 应该都非常熟悉。以后,我们会继续更深入讨论其他在 UI Kit 和其他 Cocoa 框架中的常用的设计模式。

August 12, 2014

Swift 的 for 循环

原文:Swift for loops
翻译:dreamana.com

当我们需要在一行里多次重复相同的动作,常常触及到我们的老朋友,for 循环。在 ActionScript 中,有三种 for 循环供我们所用:

Swift 的 for 循环提供这些相同的能力,同时也加入了一些额外的、便利的方式帮助我们更快更简单地写 for 循环。

让我们先从对比 ActionScript 与 Swift 中的传统三表达式 for 循环开始:

// ActionScript
var animals:Array = [ "ant", "beaver", "canary", "donkey" ];
var length:int = animals.length;
for( var i:int = 0; i < length; i++ )
{
    trace( animals[i] );
}

// Swift
let animals = [ "ant", "beaver", "canary", "donkey" ]
let count = animals.count
for var i = 0; i < count; i++ 
{
    println( animals[i] )
}

你可能发现了,Swift 的 for 循环缺少圆括弧。他们是可选的!Swift 编译器知道 for 循体以花括弧开始,因此它能够节省我们一组不必要的按键。

当循环体只有一行的时候,ActionScript 允许我们选择性地省略花括弧:

// ActionScript
for( var i:int = 0; i < length; i++ )
    trace( animals[i] );

在 Swift 中,循环体必须用花括弧包围,无一例外!大多数 ActionScript 开发者从不省略循环的花括弧以避免产生愚蠢的错误。可见 Swift 不错,它首先就确保让我们不用担心这种事情了。

遍历变量值

ActionScript 的 for-each 循环将遍历的细节隐藏在背后,它让我们完全专注于数组的条目。Swift 的 for-in 循环通用适用:

// ActionScript
var animals:Array = [ "ant", "beaver", "canary", "donkey" ];
for each( var animal:String in animals)
{
    trace( animal );
}

// Swift
let animals = [ "ant", "beaver", "canary", "donkey" ]
for animal in animals
{
    println( animal )
}

在 ActionScript 中,我们需要用 var 关键字声明 animal。而 Swift 让我们少打几个字,通过自动声明 animal 为常量(就像我们用过的 let 关键字),我们就只需提供一个名字。

带范围循环

还记得传统三表达式 for 循环中索引从 0 到 count 吗?如果我们可以用 for-in 循环来递增索引会怎样?这会更少代码!适用一个叫 ranges 的特性,可以让编译器自动创建一个临时整数数组给我们:

// Swift
let animals = [ "ant", "beaver", "canary", "donkey" ]
let count = animals.count
for i in 0..<count
{
    println( animals[i] )
}

0..<count 是一个半开区间从 0 开始并在达到 count 值之前结束。在上面的例子,count 值是 4,那么 range 创建的数组即为 [ 0, 1, 2, 3 ].

在 ActionScript 中,创建临时对象会导致更多的垃圾回收,会影响性能。因为 Swift 适用自动引用计数,我们可以方便地创建临时对象而无需担心垃圾回收器影响性能。

遍历 key

通常,在 ActionScript 处理 Object 和 Dictionary 地时候,我们需要用 for-in 循环遍历 key。Swift 也有 dictionary 类型,而且 Swift 的 for-in 循环也能遍历 key:

// ActionScript
var animalsWithLegs:Array = { ant: 6, beaver: 4, canary: 2, donkey: 4 };
for( var animal:String in animalsWithLegs )
{
    var legCount:int = animalsWithLegs[ animal ] as int;
    trace( animal, "has", legCount, "legs." );
}

// Swift
let animalsWithLegs = [ "ant": 6, "beaver": 4, "canary": 2, "donkey": 4 ]
for (animal, legCount) in animalsWithLegs
{
    println( "\( animal ) has \( legCount ) legs." )
}

在 ActionScript 中的 for-in 循环遍历 key,而且需要分开一行代码去读取每一个 key 的值。Swift 为我们节省了这一步,通过一个元组(tuple, 一组多值) 同时返回 key 和 value。因为 Swift 的元组中每一个值有独立的名字,所以我们可以在循环体内使用这些名字作为常量。在上面的 Swift 例子,我们使用 animal 和 legCout,就像在 ActionScript 例子中的一样。

Stay in the loop

Swift 让我们可以使用像 ActionScript 里的相同类型的 for 循环,再加上 Swift 加入里一些便捷方式,如省略圆括弧,范围循环,还可以在元组中同时接收到 key 和 value. 有兴趣想读到更多的为 Flash 开发者写的 Swift 提点吗?Sign up for email updates!

August 25, 2014

Swift Bits, Issue 2

原文:Swift Bits, Issue 2
翻译:dreamana.com

这是另一辑关于 Swift 的文章和教程的集合,我觉得比较感兴趣的。

Value and Reference Types 是一篇来自 Apple 官方博客上的文章,谈了一点关于传递 与传递引用 的区别(加上,一些关于不变性 immutability 的有趣讨论)。在 ActionScript 中,我们有除了一个是传递值的有限类型集合(比如 Number, String 和 Boolean),大部分类型都是传递引用的。在 Swift 中使用结构体,结果处理很多对象都是传递值的,在习惯使用之前我们需要额外地小心。举个例子,Swift 的 array 是传递值的,但在 ActionScript 里是传递引用的。

The Blub Paradox in Swift 谈论里怎么 Swift 会看起来不吸引,在某些方面,对于一个老练的 Objective-C 开发者。也许 Swift 的一些特性看起来像过度设计,不必要,或者让人一头雾水。作为一个 ActionScript 开发者,Swift 的某些部分让我觉得也是如此。然而,Swift 提供令许多东西帮助我们写更好的代码 ——更容易理解的代码,避免常见 bug 的代码,及跑等更快的代码。理解这些优点需要花费时间和实际经验,因此作者鼓励大家保持好奇心而不要有挫败感。

Expanded Thoughts on Swift’s Type Inference 文章里头,Andrew Bancroft 考虑的是人类期望的变量类型推断有什么不一样,以及编译器怎样更简单地自动完成。我才发现这结论非常有意义,减少令我之前在某些情况下使用类型推断时的不适:都是关系到 API 设计。

Swift Proposal: ‘protected’ 谈论关于 Swift 访问控制缺少 protected 的问题。许多 Flash 开发者都大量使用 protected,因此这个讨论与我们尤其相关。作为参考,Apple 之前也解释过 为何 Swift 不支持 protected (why Swift doesn’t support protected)

Alamofire 是一个用 Swift 写的 HTTP 网络处理库。它以非常简洁的 API 即可使用 Swift 的强大特性。毫无疑问我未来将会使用这个。作者 Mattt Thompson,在 introduces and explains Alamofire 中更详细地介绍这个库。

如果你因我的 Feathers UI components 而认识我,你可能猜测我对创建自定义 iOS 组件感兴趣。How To Make a Custom Control in Swift 这篇文章深入介绍用 Swift 构建基于 UI Kit 的组件的过程。

阅读愉快!欢迎回访获取更多为 Flash 开发者写的 Swift 提点。或者 Sign up for email updates!

August 30, 2014

Swift 元组:从一个函数返回多个值

原文:Swift tuples: returning multiple values from a function
翻译:dreamana.com

有时,我真希望能在 ActionScript 中的函数返回多个值。虽然可以创建一个简单的类取保存那些值,但我觉得只作为一个函数里返回值时才需要用到这个类有点杀鸡用牛刀。我不得不打断我的流程取创建一个新文件,考虑该放到哪个包里面,而且也许还要为它写点文档(如果在团队里工作或者分享代码)。最后变成很多样板——尤其如果不只返回两个值!我发现我自己要在代码中多处来回跳转很多次才让所有东西正确地设置。

如果有一些声明简单类型的简写会怎样?不用一个完整的类,可能只是一个基本的声明,说明我要返回一个包含一些布尔型和字符串型的对象。也许我甚至可以给这些值命名 … 但只在我需要的时候。如果我可以将这个类型声明简写直接放到函数标识中,那么就不用被那些创建新类的样板搞糊涂了。

引入元组 (tuples)

Swift 提供所有这些,而且多一些,使用一个很酷的结构叫元组 (tuple)。使用元组,我们可以将多个值组织到一个东西里,而不用打断我们的工作流程,担心添加额外的类带来的影响。为了更好地理解元组,让我们来写一个用元组作为返回类型的函数。

假设我们的函数执行某种动作,返回值表示执行是否成功。如果失败了,就提供一个错误字符串。我们以这个函数标识开始:

func activateGizmo() -> (Bool, String?)

我们可以看到函数返回的元组包含布尔型和一个 可选 字符串值。元组的定义方式是括号包着一个类型列表,以逗号分隔。

接下来,我们返回元组:

func activateGizmo() -> (Bool, String?)
{
    return (false, "The gizmo is miscalibrated. Aborted operation.")
}

类似于怎样使用括号定义一个元组类型,元组值是用括号包围各个单值,以逗号分隔来创建的。编译器完全知道元组的类型,根据函数标识(那是运行时的 类型推断),因此如果我们在返回值里弄错了,都可看到对应的错误提示。

最后,让我们调用这个函数并且使用储存在元组中的值:

let result = activateGizmo()
if !result.0
{
    println( "Error: \(result.1)" )
}

如果操作不成功,我们打印错误字符串到控制台。我们可以通过读取在索引 0 的布尔值检测操作是否成功。类似地,错误字符串存储在索引 1。再说,编译器我们在函数标识中是怎么定义那个元组的,因此它可以推断出正确的类型,并且如果我们用错了元组它会报错。

那么,基本上,元组就像一个数组?

在大多数简单形式的情况下,是,元组看起来非常类似于数组。但是,那并不是元组的全部。例如,我们可以给元组值命名,像这样:

func activateGizmo() -> (success: Bool, errorString: String)
{
    return (false, "The gizmo is miscalibrated. Aborted operation.")
}

let result = activateGizmo()
if !result.success
{
    println( "Error: \(result.errorString)" )
}

现在,元组更像一个类实例或结构体。我们可以用 result.success 和 result.errorString 来读取元组的值。即时值被命名了,如果有需要我们也还可以用 result.0 和 result.1 来读取。

分解一个元组

元组可以实现的最强大功能之一就是可以将它的值分离到各个局部变量或常量中。这叫做分解 (decomposition)

let (isSuccessful, errorDescription) = activateGizmo()
if !isSuccessful
{
    println( "Error: \(errorDescription)" )
}

这里我们创建离两个局部常量分别叫 isSuccessful 和 errorDescription。再一次,感谢类型推断,编译器能自动感知 isSuccessful 是一个布尔值而 errorDescription 是一个字符串。

注意这些局部变量不需要使用元组值一样的名称。如果我们想叫他们为 success 和 errorString 也没关系,但不是必须的。

如果我不需要返回的元组的所有值该怎样?

如果我们只需关系那个操作是否成功的话要怎样做。也许我们不需要错误信息。如果不需要它的话,没理由要将那字符串拷贝到局部变量中:

let (isSuccessful, _) = activateGizmo()
if !isSuccessful
{
    // handle the error
}

当分解元组的时候,要忽略任意的值,只需简单地在变量名位置使用下划线 _ 字符。

Here’s to a Swift return!

有时候,觉得在 ActionScript 中我们想简单地返回多个值的时候创建一个新类是一种累赘。有了元组,Swift 给了我们力量,就在一个函数定义里面定义一个新的轻量级的类型。我们甚至能给值命名来设计一个元组,那么就更像一个类或结构体。通过分解一个元组,我们可以很快将元组各个值赋值到多个变量或常量中,我们可以选取哪一个值保留哪一个忽略。

有兴趣想读到更多的为 Flash 开发者写的 Swift 提点吗?Sign up for email updates!

October 10, 2014

by Thibault Imbert

Swift: Preloading an image and displaying it
Swift: Loading and parsing a remote JSON file
Swift: ARC vs Flash GC
Swift: Overflowing
Swift: Drawing text on bitmap
Swift: Bitmap data and filters
Swift: Touch event and physics field
Swift: Bouncing balls with built-in physics engine
Swift: Types conversion, subclassing, casting and drawing
Hello Swift/Playgrounds

by Josh Tynjala

Swift type inference
Swift optionals and nil
Swift around the web
Swift functions and parameter names
Cocoa design patterns in Swift: listening to events with target-action
Swift for loops
Swift Bits, Issue 2
Swift tuples: returning multiple values from a function