Swift2 学习笔记 1 - 概述

这一章主要是通过一些实际的代码让大家对 Swift 有一个初步的感觉,具体的语法以及技术细节会在之后的章节中详细介绍。


简单值

  • 使用 let 来声明常量
  • 使用 var 来声明变量
  • 大部分情况下不用明确声明类型
  • 显式声明的话,用冒号分割 let explicitDouble: Double = 70
  • 值永远不会被隐式转换成其他类型,需要时请显式转换
  • 一个简单的值转字符串的方法是把值写到括号中,并且在括号前加一个反斜杠,括号中可以是一个表达式
  • 使用方括号来创建数组和字典,用下标或者 key 来访问元素,最后一个元素后面允许有逗号
  • 创建空数组或者字典要用初始化语法
// variable and constant value
var myVariable = 42
myVariable = 50
let myConstant = 33
// explicit declare
let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
// cast type
let label = "The width is"
let width = 94
let widthLabel = label + String(width)
// easy cast from value to string
let apples = 3
let appleSummary = "I have \(apples) apples."
// array
var shoppingList = ["catfish", "water", "tulips"]
shoppingList[1] = "bottle of water"
// dict
var occupations = ["Da": "Captain", "Wang": "Warrior",]
occupations["WDX"] = "Hero"
// init empty array / dict
let emptyArray = [String]()
let emptyDict = [String: Float]()

控制流

  • 条件操作 ifswitch
  • 循环操作 for-in, for, whilerepeat-while
  • 包裹条件和循环变量的括号可以省略,但是语句体必须要大括号
// for loop with if-else statment
let Scores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in Scores{
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)

可以使用 iflet 来处理值缺失的情况。这些值可由可选值来代表。一个可选的值是一个具体的值或者是 nil 以表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。在下面的例子中,如果变量的可选值是 nil,条件会被判断为 false,大括号中的代码会被跳过

var optionalString: String? = "Hello"
print(optionalString == nil)
var optionalName: String? = "Da Wang"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}

另一种方法是通过 ?? 操作符来提供一个默认值。如果可选值缺失,用默认值代替

let nickName: String? = nil
let fullName: String = "Da Wang"
let greeting = "Hi \(nickName ?? fullName)"

switch 支持任意类型的数据以及各种比较操作,而且不需要写 break,执行完子句会自动退出

let vegetable = "cucumber"
swtich vegetable{
case "carrot":
print("Oh Carrot!")
case "cucumber":
print("Oh Cucumber!")
case let x where x.hasSuffix("pepper"):
print("Oh Special One!")
default:
print("Nothing happened")
}

可以使用 for-in 来遍历字典

let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers{
for number in numbers{
if number > largest {
largest = number
}
}
}
print(largest)

使用 while 来重复运行一段代码直到不满足条件,循环条件也可以在末尾,保证至少循环一次

// while loop
var n = 2
while n < 100 {
n = n * 2
}
print(n)
// repeat while loop
var m = 2
repeat {
m = m * 2
} while m < 100
print(m)

也可以在循环中使用 ..< 来表示范围,注意 ..< 创建的范围不包含上界,如果要包含的话,使用 ...

var forLoop = 0
for i in 0..<4 {
forLoop += i
}
print(forLoop)
forLoop = 0
for i in 0...4 {
forLoop += i
}
print(forLoop)

函数和闭包

使用 func 来声明一个函数,使用名字和参数来调用函数。使用 -> 来指定函数返回值的类型

func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)"
}
greet("Da", day: "Friday")

使用元组来让一个函数返回多个值。该元组的元素可以用名称或数字来表示

func calStats(scores: [Int]) -> (min: Int, max: Int, sum: Int){
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores{
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
let stats = calStats([5, 3, 100, 2, 9])
print(stats.sum)
print(stats.2)

函数可以带可变个数的参数,这些参数在函数内表现为数组的形式

func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf()
sumOf(42, 583, 12)

函数可以嵌套。被嵌套的函数可以访问外侧函数的变量,可以用来重构一个太长或者太复杂的函数

func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFiftee()

函数是第一等类型,也就是说,函数可以作为另一个函数的返回值

func makeIncrementer() -> (Int -> Int){
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)

函数也可以当做参数传入另一个函数

func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThenTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, condition: lessThanTen)

函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。比保重的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的

numbers.map({(number: Int) -> Int in let result = 3 * number return result})

如果一个闭包的类型已知,,可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当作结果返回

let mappedNumbers = numbers.map({number in 3 * number })
print(mappedNumbers)

也可以通过参数位置而不是参数名字来引用参数。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。当一个闭包是传给函数的唯一参数时,可以完全忽略括号

let sortedNumbers = numbers.sort { $0 > $1}
print(sortedNumbers)

对象和类

使用 class 和类名来创建一个类,其他的语法和前面说的一致。

  • 使用 init 来初始化一个构造器
  • 使用 deinit 来创建一个析构函数
class Shape {
var numberOfSides: Int = 0
var name: String
init(name: String){
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

要创建一个类的实例,在类名后面加上括号。使用点语法来访问实例的属性和方法

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

子类的定义方法实在它们的类名后面加上父类的名字,用冒号分割。如果要重写父类的方法,需要用 override 标记,否则会报错,编译器同样会检测 override 标记的方法是否确实在父类中

class Square: Shape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
}

除了储存简单的属性之外,属性可以有 gettersetter

class EquilateralTriangle: Shape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String){
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a tragnle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)

如果不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用 willSetdidSet

枚举和结构体

使用 enum 来创建一个枚举,枚举也可以包含方法

enum Rank: Int{
case Ace = 1
case Two, Three, FOur, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self{
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.rawValue

枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,以防原始值没有意义,我们不需要设置。

使用 struct 来创建一个结构体。结构体和类最大的区别是结构体是传值,类是传引用。

struct Card {
var rank: Rank
func simpleDescription() -> String {
return "The \(rank.simpleDescription())"
}
}

协议和扩展

使用 protocol 来声明一个协议,类、枚举和结构体都可以实现协议,在结构体中 mutating 关键字用来标记一个会修改结构体的方法

protocol ExampleProtocal {
var simpleDescription: String { get }
mutating func adjust()
}
// class
class simpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 15213
func adjust() {
simpleDescription += " Now 100% adjusted"
}
}
// struct
struct Simple Structure: ExampleProtocol {
var simpleDescription: String = "A simple strucure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}

使用 extension 来为现有的类型添加功能

extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print (7.simpleDescription)

泛型

在尖括号里写一个名字来创建一个泛型函数或者类型

func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item]{
var result = [Item]()
for _ in 0..<numberOfTimes{
result.append(item)
}
return result
}
repeatItem("knock", numberOfTimes: 4)

在类型名后面使用 where 来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类。

func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool){
...
}
捧个钱场?