2

开始学习 Swift

 3 years ago
source link: http://blog.danthought.com/programming/2016/01/30/get-started-with-swift/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

苹果公司在 2014 年 6 月 2 日发布了 Swift 编程语言,你可以使用 Swift 编写在 OS X、iOS、watchOS 和 tvOS 上运行的应用,2015 年 12 月 4 日,苹果公司宣布 Swift 编程语言开放源代码,让 Swift 有更广阔的想象空间。

请注意,示例代码都来自 The Swift Programming Language - A Swift Tour,如果你有更多疑问可以参考这本苹果官方教程,我都是自己敲的代码,不是复制粘贴的,所以你最好也一起跟着这篇文章敲代码来开始学 Swift,所谓 Muscle Memory。

Swift Opensource

Playground

目前来看,大家学习 Swift 主要是为了开发苹果平台的应用,你应该有一台 Mac,Mac 上应该装了 Xcode,打开 Xcode,在欢迎界面点击 Get started with a plaground

Get Started with a Playground

在下面的界面中,输入你新建的 Playground 的 名称平台

New Playground

从上一步点击 Next 过后,你就会看到 Playground 的主界面了,在这里敲代码学习 Swift,可以不用操心很多你不熟悉的平台 SDK,学习语法,打好基础。

Tour Playground
var myVariable = 42 // 一个变量
myVariable = 50 // 所以可以改变它的值
let myConstant = 42 // 一个常量,所以不能改变它的值

变量和常量都需要明确类型

let implicitInteger = 70 // 编译器从初始值知道 implicitInteger 是整数类型
let implicitDouble = 70.0 // 编译器从初始值知道 implicitDouble 是浮点类型
let explicitDouble: Double = 70 // 编译器不能从初始值知道 explicitDouble 是浮点类型,所以显示地声明类型

值不能自动地转换为其他类型,需要自己来转换

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."

创建数组和词典

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]()

ifswitch 作为条件控制,for-inwhilerepeat-while 作为循环控制

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
  if score > 50 {
    teamScore += 3
  } else {
    teamScore += 1
  }
}
print(teamScore)

可选类型要么有值、要么没有值

var optionalString: String? = "Hello" // ? 表明可选
print(optionalString == nil)

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName { // optionalName 有值才会运行 {} 中的内容
  greeting = "Hello, \(name)"
}

?? 可以为可选类型提供默认值

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

注意 switch 中每一个 case 中的语句运行完后不会再运行下一个 case 中的语句

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 x where x.hasSuffix("pepper"):
  print("Is it a spicy \(x)?")
default:
  print("Everything tastes good in soup.")
}

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)

whilerepeat-while

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

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

for,注意 ..< 不包含最大值, 包含最大值

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

函数和闭包

通过 func 来定义函数,() 里面的是参数列表,-> 是返回类型

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

函数可以通过元组 tuple 返回多个值

func calculateStatistics(_ scores: [Int]) -> (min: Int, max: Int, sum: Int) {
  var min = scores[0]
  var max = scores[1]
  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(11, 22, 33)

函数可以嵌套,里面的函数可以访问外面函数中声明的变量

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(list: 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.sorted(by: { $0 > $1 })
print(sortedNumbers)

通过 class 定义一个类来瞧瞧

class Shape {
  var numberOfSides = 0
  func simpleDescription() -> String {
    return "A shape with \(numberOfSides) sides."
  }
}

定义一个 Shape 对象来瞧瞧

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

加上初始化方法 init 来瞧瞧,所有的属性都需要初始化,可以像 numberOfSides 通过初始值,还是像 name 通过 init 方法,这里为了和参数 name 区分,所以用了 self 表明属性 name

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

定义一个子类,注意重写了父类的方法需要加上 override,通过 super 可以调用父类的方法

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()

一个有 gettersetter 的子类

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

还可以通过 willSetdidSet 在属性值改变的前或后来执行一些代码,类似于监听属性值变化

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)

枚举和结构体

enum 定义一个枚举来瞧瞧,可以有方法的哦,还有一点需要注意,可以指明枚举的 Raw Value 的类型,下例中就是 Int 类型

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

通过 Raw Value 来创建枚举

if let covertedRank = Rank(rawValue: 3) {
  let threeDescription = covertedRank.simpleDescription()
}

定义枚举时 Raw Value 的类型不是必须的,没有也行

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()

struts 定义结构体,结构体和类很像,记住最大的一点不同是,在传递时,结构体是整体拷贝一份,也就是值传递,而类是引用传递

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 threeOfSpadeDescription = threeOfSpades.simpleDescription()

枚举的实例可以有关联值 Associated Value,要区别于 Raw Value,针对一个枚举 case,Raw Value 都是一样的,Associated Value 可以不同

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):
  print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .error(error):
  print("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 = 68105
  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
  }
}
print(7.simpleDescription)

<> 中定义类型的名称来创建泛型方法或类型

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

泛型还可以用在定义类,枚举和结构体中

enum OptionalValue<Wrapped> {
  case none
  case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)

使用 where 可以给泛型限制一些条件,如必须实现某些协议,或者必须有特定的父类等

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

如果你有其他编程语言的开发经验,或多或少你都可以在 Swift 中看到它们的影子,map 这些函数在 Ruby 中是不是也有啊,所谓的函数式编程,protocol 这种面向协议编程方式,像不像早年 Java 中强调的面向接口编程,看来 Swift 是取众家所长。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK