码迷,mamicode.com
首页 > 编程语言 > 详细

Swift 学习笔记 (二)

时间:2016-05-11 19:27:44      阅读:246      评论:0      收藏:0      [点我收藏+]

标签:

 

41.闭包表达式语法(Closure Expression Syntax

闭包表达式语法有如下一般形式:

{ (parameters) -> returnType in

    statements

}

 

闭包表达式语法可以使用常量、变量和inout类型作为参数,不能提供默认值。也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。

下面的例子展示了之前backwards(_:_:)函数对应的闭包表达式版本的代码:

reversed = names.sort({ (s1: String, s2: String) -> Bool in

    return s1 > s2

})

需要注意的是内联闭包参数和返回值类型声明与backwards(_:_:)函数类型声明相同。在这两种方式中,都写成了(s1: String, s2: String) -> Bool。然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。

 

闭包的函数体部分由关键字in引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。

由于这个闭包的函数体部分如此短,以至于可以将其改写成一行代码:

reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } )

该例中sort(_:)方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。

 

 

42.根据上下文推断类型(Inferring Type From Context

因为排序闭包函数是作为sort(_:)方法的参数传入的,Swift 可以推断其参数和返回值的类型。sort(_:)方法被一个字符串数组调用,因此其参数必须是(String, String) -> Bool类型的函数。这意味着(String, String)和Bool类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略:

reversed = names.sort( { s1, s2 in return s1 > s2 } )

实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,都可以推断出闭包的参数和返回值类型。 这意味着闭包作为函数或者方法的参数时,您几乎不需要利用完整格式构造内联闭包。

尽管如此,您仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则可以采用完整格式的闭包。而在sort(_:)方法这个例子里,闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。

 

43.参数名称缩写(Shorthand Argument Names

Swift 自动为内联闭包提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数,以此类推。

如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:

reversed = names.sort( { $0 > $1 } )

在这个例子中,$0和$1表示闭包中第一个和第二个String类型的参数。

 

44.运算符函数(Operator Functions

实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift 的String类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。而这正好与sort(_:)方法的第二个参数需要的函数类型相符合。因此,您可以简单地传递一个大于号,Swift 可以自动推断出您想使用大于号的字符串函数实现:

reversed = names.sort(>)

 

 

45.尾随闭包(Trailing Closures

如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用:

func someFunctionThatTakesAClosure(closure: () -> Void) {

    // 函数体部分

}

 

// 以下是不使用尾随闭包进行函数调用

someFunctionThatTakesAClosure({

    // 闭包主体部分

})

 

// 以下是使用尾随闭包进行函数调用

someFunctionThatTakesAClosure() {

    // 闭包主体部分

}

 

闭包表达式语法一节中作为sort(_:)方法参数的字符串排序闭包可以改写为:

reversed = names.sort() { $0 > $1 }

如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以()省略掉:

reversed = names.sort { $0 > $1 }

 

 

let digitNames = [

    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",

    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"

]

let numbers = [16, 58, 510]

如上代码创建了一个数字位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。

您现在可以通过传递一个尾随闭包给numbers的map(_:)方法来创建对应的字符串版本数组:

let strings = numbers.map {

    (var number) -> String in

    var output = ""

    while number > 0 {

        output = digitNames[number % 10]! + output

        number /= 10

    }

    return output

}

// strings 常量被推断为字符串类型数组,即 [String]

// 其值为 ["OneSix", "FiveEight", "FiveOneZero"]

 

46.解决闭包引起的循环强引用:解决闭包引起的循环强引用

定义捕获列表

捕获列表中的每一项都由一对元素组成,一个元素是weak或unowned关键字,另一个元素是类实例的引用(例如self)或初始化过的变量(如delegate = self.delegate!)。这些项在方括号中用逗号分开。

如果闭包有参数列表和返回类型,把捕获列表放在它们前面:

lazy var someClosure: (Int, String) -> String = {

    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in

    // 这里是闭包的函数体

}

如果闭包没有指明参数列表或者返回类型,即它们会通过上下文推断,那么可以把捕获列表和关键字in放在闭包最开始的地方:

lazy var someClosure: Void -> String = {

    [unowned self, weak delegate = self.delegate!] in

    // 这里是闭包的函数体

}

 

 

弱引用和无主引用

 

47.

在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为无主引用。

相反的,在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包体内检查它们是否存在。

注意
如果被捕获的引用绝对不会变为nil,应该用无主引用,而不是弱引用。

 

前面的HTMLElement例子中,无主引用是正确的解决循环强引用的方法。这样编写HTMLElement类来避免循环强引用:

class HTMLElement {

 

    let name: String

    let text: String?

 

    lazy var asHTML: Void -> String = {

        [unowned self] in

        if let text = self.text {

            return "<\(self.name)>\(text)</\(self.name)>"

        } else {

            return "<\(self.name) />"

        }

    }

 

    init(name: String, text: String? = nil) {

        self.name = name

        self.text = text

    }

 

    deinit {

        print("\(name) is being deinitialized")

    }

 

}

上面的HTMLElement实现和之前的实现一致,除了在asHTML闭包中多了一个捕获列表。这里,捕获列表是[unowned self],表示“将self捕获为无主引用而不是强引用”。

和之前一样,我们可以创建并打印HTMLElement实例:

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")

print(paragraph!.asHTML())

// 打印 “<p>hello, world</p>”

使用捕获列表后引用关系如下图所示:

 

这一次,闭包以无主引用的形式捕获self,并不会持有HTMLElement实例的强引用。如果将paragraph赋值为nil,HTMLElement实例将会被销毁,并能看到它的析构函数打印出的消息:

paragraph = nil

// 打印 “p is being deinitialized”

 

 

 

48.闭包是引用类型(Closures Are Reference Types

上面的例子中,incrementBySeven和incrementByTen是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是引用类型

无论您将函数或闭包赋值给一个常量还是变量,您实际上都是将常量或变量的值设置为对应函数或闭包的引用。上面的例子中,指向闭包的引用incrementByTen是一个常量,而并非闭包内容本身。

这也意味着如果您将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包:

let alsoIncrementByTen = incrementByTen

alsoIncrementByTen()

// 返回的值为50

 

 

 

49.枚举语法

使用enum关键词来创建枚举并且把它们的整个定义放在一对大括号内:

enum CompassPoint {

    case North

    case South

    case East

    case West

}

 

C 和 Objective-C 不同,Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的CompassPoint例子中,North,South,East和West不会被隐式地赋值为0,1,2和3。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的CompassPoint类型。

 

多个成员值可以出现在同一行上,用逗号隔开:

enum Planet {

    case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune

}

每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如CompassPoint和Planet)应该以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于读起来更加

 

容易理解:

var directionToHead = CompassPoint.West

directionToHead的类型可以在它被CompassPoint的某个值初始化时推断出来。一旦directionToHead

 

被声明为CompassPoint类型,你可以使用更简短的点语法将其设置为另一个CompassPoint的值:

directionToHead = .East

 

directionToHead的类型已知时,再次为其赋值可以省略枚举类型名。在使用具有显式类型的枚举值时,这种写法让代码具有更好的可读性。

 

switch语句必须穷举所有情况。如果忽略了.West这种情况,上面那段代码将无法通过编译,因为它没有考虑到CompassPoint的全部成员。强制穷举确保了枚举成员不会被意外遗漏。

当不需要匹配每个枚举成员的时候,你可以提供一个default分支来涵盖所有未明确处理的枚举成员:

 

let somePlanet = Planet.Earth

switch somePlanet {

case .Earth:

    print("Mostly harmless")

default:

    print("Not a safe place for humans")

}

// 输出 "Mostly harmless”

 

 

Swift 学习笔记 (二)

标签:

原文地址:http://www.cnblogs.com/Jenaral/p/5483168.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!