简单值
使用let来声明常量,使用var来声明变量。一个常量的值在编译时并不需要获取,但是你只能为它赋值一次。也就是说你可以用常量来表示这样一个值:你只需要决定一次,但是需要使用很多次。
- var myVariable = 42
- myVariable = 50
- let myConstant = 42
如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。
- let implicitInteger = 70
- let implicitDouble = 70.0
- let explicitDouble: Double = 70
值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。
- let label = "The width is"
- let width = 94
- let widthLabel = label + String(width)
有一种更简单的把值转换成字符串的方法:把值写到括号中,并且在括号之前写一个反斜杠。例如:
- let apples = 3
- let oranges = 5
- let appleSummary = "I have \(apples) apples."
- let fruitSummary = "I have \(apples + oranges) pieces of fruit."
使用方括号[]来创建数组和字典,并使用下标或者键(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 = Dictionary<String,Float>()
- shoppingList = [] // 去逛街并买点东西
控制流
使用if和switch来进行条件操作,使用for-in、for、while和do-while来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。你可以一起使用if和let来处理值缺失的情况。有些变量的值是可选的。一个可选的值可能是一个具体的值或者是nil,表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。
- var optionalString: String? = "Hello"
- optionalString == nil
- var optionalName: String? = "John Appleseed"
- var greeting = "Hello!"
- if let name = optionalName {
- greeting = "Hello,\(name)"
- }
如果变量的可选值是nil,条件会判断为false,大括号中的代码会被跳过。如果不是nil,会将值赋给let后面的常量,这样代码块中就可以使用这个值了。
switch支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。
- let vegetable = "red pepper"
- switch vegetable {
- case "celery":
- let vegetableComment = "Add some raisins and make ants on a log."
- case "cucumber","watercress":
- let vegetableComment = "That would make a good tea sandwich."
- case let x where x.hasSuffix("pepper"):
- let vegetableComment = "Is it a spicy \(x)?"
- default:
- let vegetableComment = "Everything tastes good in soup."
- }
你可以使用for-in来遍历字典,需要两个变量来表示每个键值对。
- let interestingNumbers = [
- "Prime": [2,3,5,7,11,13],
- "Fibonacci": [1,1,2,8],
- "Square": [1,4,9,16,25],
- ]
- var largest = 0
- for (kind,numbers) in interestingNumbers {
- for number in numbers {
- if number > largest {
- largest = number
- }
- }
- }
- largest
使用while来重复运行一段代码直到不满足条件。循环条件可以在开头也可以在结尾。
- var n = 2
- while n < 100 {
- n = n * 2
- }
- n
- var m = 2 do {
- m = m * 2
- } while m < 100
- m
- var firstForLoop = 0
- for i in 0..3 {
- firstForLoop += i
- }
- firstForLoop
- var secondForLoop = 0
- for var i = 0; i < 3; ++i {
- secondForLoop += 1
- } secondForLoop
函数和闭包
使用func来声明一个函数,使用名字和参数来调用函数。使用->来指定函数返回值。
- func greet(name: String,day: String) -> String {
- return "Hello \(name),today is \(day)."
- }
- greet("Bob","Tuesday")
使用一个元组来返回多个值。
- func getGasPrices() -> (Double,Double,Double) {
- return (3.59,3.69,3.79)
- }
- getGasPrices()
- 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,12]
- hasAnyMatches(numbers,lessThanTen)
- numbers.map({
- (number: Int) -> Int in
- let result = 3 * number
- return result
- })
有很多种创建闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。
sort([1,12,2]) { $0 > $1 }
对象和类
使用class和类名来创建一个类。类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类。同样,方法和函数声明也一样。
- class Shape {
- var numberOfSides = 0
- func simpleDescription() -> String {
- return "A shape with \(numberOfSides) sides."
- }
- }
- var shape = Shape()
- shape.numberOfSides = 7
- var shapeDescription = shape.simpleDescription()
- class NamedShape {
- var numberOfSides: Int = 0
- var name: String
- init(name: String) {
- self.name = name
- }
- func simpleDescription() -> String {
- return "A shape with \(numberOfSides) sides."
- }
- }
如果你需要在删除对象之前进行一些清理工作,使用deinit创建一个析构函数。
子类的定义方法是在它们的类名后面加上父类的名字,用冒号分割。创建类的时候并不需要一个标准的根类,所以你可以忽略父类。
子类如果要重写父类的方法的话,需要用override标记——如果没有添加override就重写父类方法的话编译器会报错。编译器同样会检测override标记的方法是否确实在父类中。
- 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()
属性可以有 getter 和 setter 。
- 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")
- triangle.perimeter
- triangle.perimeter = 9.9
- triangle.sideLength
注意EquilateralTriangle类的构造器执行了三步:
如果你不需要计算属性但是需要在设置一个新值之前运行一些代码,使用willSet和didSet。
比如,下面的类确保三角形的边长总是和正方形的边长相同。
- 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")
- triangleAndSquare.square.sideLength
- triangleAndSquare.triangle.sideLength
- triangleAndSquare.square = Square(sideLength: 50,name: "larger square")
- triangleAndSquare.triangle.sideLength
- class Counter {
- var count: Int = 0
- func incrementBy(amount: Int,numberOfTimes times: Int) {
- count += amount * times
- }
- }
- var counter = Counter()
- counter.incrementBy(2,numberOfTimes: 7)
- let optionalSquare: Square? = Square(sideLength: 2.5,name: "optional square")
- let sideLength = optionalSquare?.sideLength
枚举和结构体
使用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.toRaw())
- }
- }
- }
- let ace = Rank.Ace
- let aceRawValue = ace.toRaw()
使用toRaw和fromRaw函数来在原始值和枚举值之间进行转换。
- if let convertedRank = Rank.fromRaw(3) {
- let threeDescription = convertedRank.simpleDescription()
- }
- enum Suit {
- case Spades,Hearts,Diamonds,Clubs
- func simpleDescription() -> String {
- switch self {
- case .Spades:
- return "spades"
- case .Hearts:
- return "hearts"
- case .Diamonds:
- return "diamonds"
- case .Clubs:
- return "clubs"
- }
- }
- }
- let hearts = Suit.Hearts
- let heartsDescription = hearts.simpleDescription()
使用struct来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们结构体之间最大的一个区别就是 结构体是传值,类是传引用。
- struct Card {
- var rank: Rank
- var suit: Suit
- func simpleDescription() -> String {
- return "The \(rank.simpleDescription()) of \
- (suit.simpleDescription())"
- }
- }
- let threeOfSpades = Card(rank: .Three,suit: .Spades)
- let threeOfSpadesDescription = threeOfSpades.simpleDescription()
例如,考虑从服务器获取日出和日落的时间。服务器会返回正常结果或者错误信息。
- enum ServerResponse {
- case Result(String,String)
- case Error(String)
- }
- let success = ServerResponse.Result("6:00 am","8:09 pm")
- let failure = ServerResponse.Error("Out of cheese.")
- switch success {
- case let .Result(sunrise,sunset):
- let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
- case let .Error(error):
- let serverResponse = "Failure... \(error)"
- }
接口和扩展
使用protocol来声明一个接口。
- 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来为现有的类型添加功能,比如添加一个计算属性的方法。你可以使用扩展来给任意类型添加协议,甚至是你从外部库或者框架中导入的类型。
- extension Int: ExampleProtocol {
- var simpleDescription: String {
- return "The number \(self)"
- }
- mutating func adjust() {
- self += 42
- }
- }
- 7.simpleDescription
- let protocolValue: ExampleProtocol = a // protocolValue.anotherProperty
- protocolValue.simpleDescription // Uncomment to see the error
泛型
在尖括号里写一个名字来创建一个泛型函数或者类型。
- func repeat<ItemType>(item: ItemType,times: Int) -> ItemType[] {
- var result = ItemType[]()
- for i in 0..times {
- result += item
- }
- return result
- }
- repeat("knock",4)
- // Reimplement the Swift standard library's optional type
- enum OptionalValue<T> {
- case None case Some(T)
- }
- var possibleInteger: OptionalValue<Int> = .None
- possibleInteger = .Some(100)
- func anyCommonElements <T,U where T: Sequence,U: Sequence,T.GeneratorType.Element: Equatable,T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T,rhs: U) -> Bool {
- for lhsItem in lhs {
- for rhsItem in rhs {
- if lhsItem == rhsItem {
- return true
- }
- }
- }
- return false
- }
- anyCommonElements([1,3],[3])