Skip to content

Swift 初见(A Swift Tour)

cheyiliu edited this page Jul 21, 2016 · 1 revision

Swift 初见(A Swift Tour)

  • 全局作用域中的代码会被自动当做程序的入口 点,所以你也不需要 main() 函数。你同样不需要在每个语句结尾写上分号。

简单值

  • 使用 let 来声明常量,使用 var 来声明变量。一个常量的值,在编译的时候,并不需要有明确的值,但是你只能 为它赋值一次。也就是说你可以用常量来表示这样一个值:你只需要决定一次,但是需要使用很多次。
  • 你不用明确地声明类型,声明的同时赋值的话,编译器 会自动推断类型。
  • 值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。let width = 94; let widthLabel = label + String(width)
  • 有一种更简单的把值转换成字符串的方法:把值写到括号中,并且在括号之前写一个反斜杠。let apples = 3; let appleSummary = "I have (apples) apples."
  • 使用方括号 [] 来创建数组和字典,并使用下标或者键(key)来访问元素。最后一个元素后面允许有个逗号。

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
var occupations = [
   "Malcolm": "Captain",
   "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
要创建一个空数组或者字典,使用初始化语法。
let emptyArray = [String]()
let emptyDictionary = [String: Float]()
如果类型信息可以被推断出来,你可以用 [] 和 [:] 来创建空数组和空字典——就像你声明变量或者给函数传参 数的时候一样。
shoppingList = []
occupations = [:]

控制流

  • 使用 if 和 switch 来进行条件操作,使用 for-in 、 for 、 while 和 repeat-while 来进行循环。包裹条件和循环 变量括号可以省略,但是语句体的大括号是必须的。
  • 你可以一起使用 if 和 let 来处理值缺失的情况。这些值可由可选值来代表。一个可选的值是一个具体的值或者 是 nil 以表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。(前面几句倒着来看)。如果变量的可选值是 nil ,条件会判断为 false ,大括号中的代码会被跳过。如果不是 nil ,会将值赋给 let 后 面的常量,这样代码块中就可以使用这个值了。

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

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

let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"

  • switch 支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。运行 switch 中匹配到的子句之后,程序会退出 switch 语句,并不会继续向下运行,所以不需要在每个子句结尾 写 break 。

let vegetable = "red pepper"
switch vegetable {
case "celery":
   print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
   print("That would make a good tea sandwich.")
case let y where y.hasSuffix("pepper"):
   print("Is it a spicy \(y)?")
default:
   print("Everything tastes good in soup.")
}

  • 你可以使用 for-in 来遍历字典,需要两个变量来表示每个键值对。字典是一个无序的集合,所以他们的键和值以 任意顺序迭代结束。使用 while 来重复运行一段代码直到不满足条件。循环条件也可以在结尾,保证能至少循环一次。你可以在循环中使用 ..< 来表示范围,也可以使用传统的写法,两者是等价的

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)


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


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


var secondForLoop = 0
for var i = 0; i < 4; ++i {
    secondForLoop += i
}
print(secondForLoop)

函数和闭包

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

func greet(name: String, day: String) -> String {
   return "Hello \(name), today is \(day)."
}
greet("Bob", day: "Tuesday")

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

func calculateStatistics(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 statistics = calculateStatistics([5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)

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

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

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

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

  • 函数是第一等类型,这意味着函数可以作为另一个函数的返回值。

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 lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, condition: lessThanTen)

  • 函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包所建作用域中能得到的变 量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数例子中所看到。你可以使用 {} 来创建 一个匿名闭包。使用 in 将参数和返回值类型声明与闭包函数体进行分离。

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 来创建一个构造器。
  • 注意 self 被用来区别实例变量。
  • 如果你需要在删除对象之前进行一些清理工作,使用 deinit 创建一个析构函数。
  • 子类的定义方法是在它们的类名后面加上父类的名字,用冒号分割。
  • 子类如果要重写父类的方法的话,需要用 override 标记——如果没有添加 override 就重写父类方法的话编译器 会报错。
  • 属性可以有 getter 和 setter
  • 如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用 willSet 和 didSet 。
  • 处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加 ? 。如果 ? 之前的值是 nil , ? 后面 的东西都会被忽略,并且整个表达式返回 nil 。否则, ? 之后的东西都会被运行。在这两种情况下,整个表达式 的值也是一个可选值。

class NamedShape {
   var numberOfSides: Int = 0
   var name: String
   init(name: String) {
       self.name = name
   }
   func simpleDescription() -> String {
       return "A shape with \(numberOfSides) sides."
   }
}


class Square: NamedShape {
   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()


class EquilateralTriangle: NamedShape {
   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 triagle with sides of length \(sideLength)."
   } }
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 12;
print(triangle.perimeter)

triangle.perimeter = 9.9
print(triangle.sideLength)
print(triangle.perimeter)


class TriangleAndSquare {
   var triangle: EquilateralTriangle {
       willSet {
           square.sideLength = newValue.sideLength
       } }
   var square: Square {
       willSet {
           triangle.sideLength = newValue.sideLength
       }
   }
   init(size: Double, name: String) {
       square = Square(sideLength: size, name: name)
       triangle = EquilateralTriangle(sideLength: size, name: name)
   }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
print(triangleAndSquare.square.sideLength)


let optionalSquare: Square? = nil//Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

枚举和结构体

  • 使用 enum 来创建一个枚举。
  • 使用 struct 来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就 是结构体是传值,类是传引用。

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)
       }
   }
   
   static func cmp(a:Rank, b:Rank) -> Bool {
       return a.rawValue == b.rawValue
   }
   
   func cmp(a:Rank) -> Bool {
       return self.rawValue == a.rawValue
   }
}
let ace = Rank.Ace
let aceRawValue = ace.rawValue
let two = Rank.Two.rawValue
let jack = Rank.Jack.simpleDescription()
let three = Rank.Three.simpleDescription()

print(Rank.cmp(Rank.Ace, b: Rank.Three))
print(Rank.Ace.cmp(Rank.Ace))


// if let语法, 赋值之后不是nil则走if逻辑
if let convertedRank = Rank(rawValue: 100) {
   let threeDescription = convertedRank.simpleDescription()
}else{
   print("else")
}




struct Card {
   var rank: Rank
   var suit: Suit
//    init(rank:Rank, suit:Suit){
//        self.rank = rank
//        self.suit = suit
//    }
   func simpleDescription() -> String {
       return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
   }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()


协议和扩展

  • 使用 protocol 来声明一个协议。
  • 类、枚举和结构体都可以实现协议。
  • 使用 extension 来为现有的类型添加功能,比如新的方法和计算属性。你可以使用扩展在别处修改定义,甚至是 从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。
  • 注意声明 SimpleStructure 时候 mutating 关键字用来标记一个会修改结构体的方法。 SimpleClass 的声明不需要 标记任何方法,因为类中的方法通常可以修改类属性(类的性质)。

protocol ExampleProtocol {
   var simpleDescription: String { get }
   mutating func adjust()
}

class SimpleClass: ExampleProtocol {
   var simpleDescription: String = "A very simple class."
   var anotherProperty: Int = 69105
   func adjust() {
       simpleDescription += "  Now 100% adjusted."
   }
}

var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription


struct SimpleStructure: ExampleProtocol {
   var simpleDescription: String = "A simple structure"
   mutating func adjust() {
       simpleDescription += " (adjusted)"
   }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

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

let eight:ExampleProtocol = 8
8.simpleDescription

泛型

  • 在尖括号里写一个名字来创建一个泛型函数或者类型。 你也可以创建泛型函数、方法、类、枚举和结构体。
  • 在类型名后面使用 where 来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者 限定某个类必须有一个特定的父类。
  • <T: Equatable> 和 <T where T: Equatable> 是等价的。

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


// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
   case None
   case Some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)


func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool {
for lhsItem in lhs {
   for rhsItem in rhs {
       if lhsItem == rhsItem {
           return true }
   } }
return false
}
anyCommonElements([1, 2, 3], [5])

参考资料

Clone this wiki locally