Swift之旅_Language Guide1

最近抽空阅读了swift官方文档的Language Guide部分小节,下面记录一些知识盲点和个人认为比较有用的东西。


The Basics

第一个小节主要讲解了一些基本的东西,如var let等。也有一些知识点可以记录一下吧。

  • typealias

相当于oc里的tyepdef吧,可以用来取别名。

typealias AudioSample = UInt16
  • optional & nil

Swift’s nil isn’t the same as nil in Objective-C. In Objective-C, nil is a pointer to a nonexistent object. In Swift, nil isn’t a pointer—it’s the absence of a value of a certain type. Optionals of any type can be set to nil, not just object types.

大概的意思是说oc里的nil是指向了一个不存在的对象,而在swift中,nil不是指针,它是一个确定类型的缺失(= =蹩脚的英文),任何可选类型可以设置成nil,不只是对象类型。网上也有很多资料都介绍过可选类型,可选其实就是对某个类型进行了一层包装,里面包含了some和none,none就是这个nil,some就是具体的值。

  • assert:断言

大部分用来调试使用,类似guard,但是不符合条件程序会直接退出。

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
  • Error handle

异常处理,不过目前只在调用别人的api的时候用到过= =。

do {
    try caThrowAnError(age: 1000)
} catch AgeEorror.negative {
    print(AgeEorror.negative.rawValue)
} catch AgeEorror.tooLarge {
    print(AgeEorror.tooLarge.rawValue)
} catch {
    print("未知错误~")
}
enum AgeEorror: String, Error {
    case negative = "输入的年龄不能是负数!"
    case tooLarge = "不可能活那么久!"
}
func caThrowAnError(age: Int) throws {
    if age < 0 {
        throw AgeEorror.negative
    } else if age > 200 {
        throw AgeEorror.tooLarge
    }
}

这里结合枚举使用,抛出的错误类型必须遵循Error协议。


Basic Operators

这一小节比较简单,讲解了大部分操作符。相信有点编程基础的应该都会一眼带过,但也抽两个知识点意思意思吧!

  • ??

这个操作符在平时开发中使用还是非常频繁的,至少本人平时经常使用,有点像是三目运算符的感觉。

var a: Int?
let b = a ?? 0

如上代码,大致的意思就是假设a有值,即a不为nil的时候会把a赋值给b,如果a为nil则把0赋值给b。

  • 区间运算符[...]

这个操作符在遍历集合等场景使用较多。

let names = ["1","2","3","4","5"]
for name in names[...3] {
    print(name)
}

没错,就是你心里想的那样,遍历第0-3个元素,也可以加入<操作符,如[..<3]表示遍历第0,1,2个元素。


Strings and Characters

这一小节主要是讲解了Swift中的字符串,有几个知识点在上一篇文章Swift之旅_Welcome to Swift有提到过,主要是一个字符串可以用”””来表示多行,还有新增了一个Substring类型,具体的可以参考上一篇文章。


Collection Types

这一小节讲解的是Swift中的集合类型,主要是Array,Dictionary,Set。都比较简单,而且Swift中用起来很方便。但是因为Set平时用的很少,我就着重看了一下。

A type must be hashable in order to be stored in a set—that is, the type must provide a way to compute a hash value for itself. A hash value is an Int value that is the same for all objects that compare equally, such that if a == b, it follows that a.hashValue == b.hashValue.

Set是一个无序不重复的集合,Set中的元素必须是可哈希的。直接一直不知道哈希是什么鬼。最近一次学习的时候知道了哈希其实就是一个单向散列函数。单项散列函数可以将一串数据计算得到一个独一无二的值,所以也不难理解为啥Set中的元素必须是可哈希的了,因为Set是不重复的。


Set.png

Set集合中一些常用的操作和对应的效果,官方文档中的这幅图非常形象。


Control Flow

控制流,讲解了一些循环和控制语句。

  • for循环

常见的都很简单,不过文档中倒是提到了一种比较特别的循环方式,可以以一个数的倍数进行循环:

let minutes = 60
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}

//以下写法可以到达hours,相当于小于等于吧
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    // render the tick mark every 3 hours (3, 6, 9, 12)
}

此外我们都知道Swift中的switch语句是不用手动写break的,Swift也提供了一个修饰符可以让switch像c一样, fallthrough,这个修饰符可以让swift的switch语句匹配到对应的值之后继续向下落。

let a = 5
switch a {
    case 5,6,7,8,9:
        print("case 1")
        fallthrough
    case 1,2,3,4,5:
        print("case 2")
        fallthrough
    default:
        print("default")
}

Functions

这个小节就比较重要了,讲解的是Swift中的函数。

  • Variadic Parameters-可变参数

可变参数在绝对大多数开发语言中都存在,Swift也不列外。Swift中使用可变参数也是非常简单。

func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
  • In-Out Parameters

在函数内部是无法直接更改参数的值的,或者这样说比较贴切,参数在函数内部是let形式的。若想要达到更改调用函数时传入的参数就需要用到inout这个修饰符。

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

Closures

block的作用和oc的大致相同,但是这里有多个注意点。

  • $0

Swift中的block可以直接用$0,$1...表示第1,2…参数,这样可以直接不用写明参数 和 in,这样写起来代码似乎是清爽了很多。

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
reversedNames = names.sorted(by: { $0 > $1 } )
  • 可以直接将函数赋值给block

只要参数和返回值都相同,就可以直接将函数赋值给block

var block: (()->Int) = test
func test() -> Int {
    return 0
}
  • Escaping Closures

逃逸闭包,相信很多同学都碰到过Xcode自动帮你在函数参数中的block前面加上@escaping修饰符。

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

当闭包作为函数参数传递,但是是在函数返回后调用时,闭包被称为逃逸闭包。当你声明一个有闭包参数的函数时,你可以在参数前面写上@escaping表明闭包时允许逃逸的。

One way that a closure can escape is by being stored in a variable that is defined outside the function. As an example, many functions that start an asynchronous operation take a closure argument as a completion handler. The function returns after it starts the operation, but the closure isn’t called until the operation is completed—the closure needs to escape, to be called later.For example:

闭包可以逃避的一种方法是存储在函数之外定义的变量中。例如,启动异步操作的许多函数都使用闭包参数作为完成回调。函数在启动操作后返回,但是在操作完成之前不会调用闭包-为了可以延迟调用,闭包需要时可逃逸的。例如:

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

The someFunctionWithEscapingClosure(_:) function takes a closure as its argument and adds it to an array that’s declared outside the function. If you didn’t mark the parameter of this function with @escaping, you would get a compile-time error.

someFunctionWithEscapingClosure函数有一个闭包参数并且把它添加到了函数外部的一个数组中。如果你不在函数参数前面加上@ escaping,你会在编译的时候得到一个error。

其实大概的意思就是如果这个block是作为函数参数使用,并且是在函数执行完毕之后调用的,就需要在函数的block参数前面加上@escaping修饰符。其实这个大多时候XCode帮我们做了。大家了解一下就行。

  • @autoclosure

这个原文有点难翻译= =,直接上一段代码吧。

func test3(tBlock: @autoclosure ()->String) {
    print(tBlock())
}
test3(tBlock: "World")

大概意思就是和代码一样吧,感觉就是加上@autoclosure之后,调用函数的时候可以直接传入和闭包返回值相同类型的参数。


推荐阅读更多精彩内容