标签:sid swift float row ota width lis ams 参数
一个最简单的完整的 Swift 程序如下所示:
print("Hello, world!")
Swift 使用 let
声明常量,使用 var
声明变量:
let myConstant = 21;
var myVariable = 30;
myVariable = 40;
常量的值不需要在编译时已知,可以在运行时才确定,但需要确保只能赋值一次,不能多次赋值。
Swift 是强类型语言,每个常量和变量在定义时都必须有一个固定的类型,后续也不能再改变。当提供了初始值时,编译器可以根据初始值推断类型,比如上面的 myConstant
和 myVariable
都是 Int
类型。
也可以在定义时指定数据类型:
let explicitDouble: Double = 70
尽管初始值 70
是 Int
类型,但显式指定了数据类型为 Double
。
Swift 没有隐式类型转换,必须进行显式转换:
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
由于上面的语法在拼接字符串时很麻烦,Swift 也支持 \()
语法来拼接字符串:
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
类似于 Python 中的 f-string。
Swift 使用 3 个双引号 """
来表示多行字符串:
let quotation = """
I said "I have \(apples) apples."
And then I said "I have \(apples + oranges) pieces of fruit."
"""
开头和结尾的空白符会被去掉。Python 也有相同的用法。
Swift 支持基本的数据结构:数组 Arrays
和字典 Dictionaries
。
var shoppingList = ["catfish", "water", "tulips"]
shoppingList[1] = "bottle of water"
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
数组和字典都使用方括号 []
,并使用方括号语法来访问列表中的元素和字典中的值。
Python 中的数组叫做列表 list
,使用方括号 []
;字典 dict
使用花括号 {}
。
向数组中添加元素用 append
方法:
shoppingList.append("blue paint")
声明一个空数组或空字典时,需要指定数据类型,再次印证 Swift 是强类型语言:
let emptyArray = [String]()
let emptyDictionary = [String: Float]()
如果类型已知,比如给已有数组赋新值、给函数传入数组实参,可以使用简化语法表示空数组或空字典:
shoppingList = []
occupations = [:]
这里的类型推断为变量的类型。
控制流主要包括条件和循环。分支结构可以用 if
和 switch
,循环结构可以用 for-in
、while
和 repeat-while
。
与 C/C++ 不同,if
和 switch
后面的条件可以不加圆括号 ()
。但是分支和循环结构的代码体都必须加花括号 {}
,即使只有一条语句。
循环结构类似于 Python,但多出一个 repeat-while
,类似于 C/C++ 中的 do-while
。
for-in
循环的使用,必须加花括号:
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)
值得注意的是,由于 Swift 没有隐式类型转换,在其他语言中常用的 if score { ... }
语法是会报错的,条件必须是一个 Bool
值。
Swift 用 nil
表示一个空对象,但是一个普通变量不能是非空的,除非在定义时的类型后面加上一个问号 ?
,此时变量的类型是 Optional
。
var optionalString: String? = "Hello"
print(optionalString == nil)
为了尽可能减少空指针引用的错误,Swift 将可能为空的类型统一定义为 Optional
,当需要使用或输出一个 Optional
变量时,需要先拆包 (unwrap) 为普通类型。
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
除了通过 if
条件判断并拆包,也可以在使用 Optional
变量时提供一个默认值来妥善处理可选类型。
let nickname: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickname ?? fullName)"
Swift 中的 switch 语句更加强大,支持所有数据类型、匹配多个值、复杂条件匹配等。
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.")
}
通过使用 let
可以实现部分分支匹配复杂的条件,而不仅仅是值相等。每个 case 的结尾也不需要写 break。
使用 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 (_, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
Swift 中的 while
和 repeat-while
与其他语言类似。
var n = 2
while n < 100 {
n *= 2
}
print(n)
var m = 2
repeat {
m *= 2
} while m < 100
print(m)
Swift 提供了类似于 Python 中 range 的写法,但更加简洁:
var total = 0
for i in 0..<4 {
total += i
}
print(total)
..<
不包括上界,比如 0..<4
表示 0, 1, 2, 3;...
包括上界,比如 0...4
表示 0, 1, 2, 3, 4。
函数声明和调用的语法如下:
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")
函数定义时参数列表里的是参数名 (name),函数调用时使用的是参数标签 (label),函数定义时如果没有给出 label,默认使用 name 作为 label。
也可以额外指定参数的 label,函数调用使用 label 能使代码的可读性更好 (调用语句就像一个完整的句子)。下划线 _
表示没有 label。
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")
参数类型也可以是数组或字典,声明语法和定义变量相同。返回值可以有多个,用圆括号 ()
括起来作为一个元组 tuple
,可以给每个返回值指定一个名字 (也可以不指定),访问时用名称或索引。
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(scores: [5, 3, 100, 3, 9])
print(statistics)
// Prints "(min: 3, max: 100, sum: 120)"
print(statistics.sum)
// Prints "120"
print(statistics.2)
// Prints "120"
函数的定义可以嵌套,内部函数可以使用外部的变量。
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
Swift 中函数是一等公民,就像 JavaScript 一样,函数可以作为参数、返回值,表达方式与函数定义类似。
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
makeIncrementer 函数返回了一个函数 addOne,这个函数接收一个 Int 参数,返回一个 Int 值。
函数作为参数的一个例子:
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)
Swift 拥有闭包 (closure) 的概念,类似于其他语言中的匿名函数。闭包是一段可执行的代码块,用花括号 {}
包含,用 in
分隔类型声明与代码体。函数是一种特殊的闭包。闭包可以访问创建时所在作用域的变量和函数。
numbers.map({ (number: Int) -> Int in
let result = 3 * number
return result
})
Swift 作为一门现代语言,尽可能简化了开发者的编码,能省略的地方几乎都可以省略不写。当闭包的参数和返回值的类型都已知时,可以省略类型,只写参数名,使代码更加简洁。
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
// Prints "[60, 57, 21, 36]"
甚至可以省略参数名,只根据参数的位置来引用参数 (从 0 开始)。
let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)
// Prints "[20, 19, 12, 7]"
Swift 支持面向对象,拥有类 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()
Swift 中类的构造函数有一个统一的名字 init
,与 Python 类似。在类内用 self 引用当前对象。
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
类的每个属性都必须被初始化,可以在定义变量时初始化,也可以在构造方法中初始化。
Swift 也提供了析构方法 deinit
。
如果需要从另一个类继承,则在类名后写出父类名称,用冒号 :
分隔。Swift 不要求所有的类都有一个最终的祖先,比如 Java 中的 Object,如果一个类定义时没有给出父类,那么这个类就不继承任何一个类。
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()
在子类中重写父类方法必须加上 override
,重写了方法但没有加 override
或加了 override
但没有重写方法都会报错,这也避免了一个常见的编码错误。
可以为属性添加 get
和 set
方法,实现更复杂的逻辑。set
方法的参数名默认是 newValue
,也可以自己指定。
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)
// Prints "9.3"
triangle.perimeter = 9.9
print(triangle.sideLength)
// Prints "3.3000000000000003"
Swift 还提供了 willSet
和 didSet
方法,用于在 set
之前和之后执行。
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)
// Prints "10.0"
print(triangleAndSquare.triangle.sideLength)
// Prints "10.0"
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
// Prints "50.0"
与基本类型相同,对象也有可选类型 Optional
,当可选对象为 nil
时,在该对象上的一切操作都会被忽略;如果不是 nil
,则拆包并执行相应操作,但最终结果仍是 Optional
类型。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
print(sideLength)
// Prints "Optional(2.5)"
Swift 中使用 enum
创建一个枚举类型,枚举类型中的每个枚举量都对应一个 rawValue
,这个 rawValue
可以是整型、浮点型和字符串。如果是整型,默认从 0 开始。除了枚举量,枚举类型还可以包含方法。
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
switch 语句中的 .ace
其实是 Rank.ace
的简化写法,由于已经知道 self 是 Rank 类型的枚举值,因此可以省略。
上面的代码直接使用枚举量的名称来实例化一个枚举类型,也可以用 rawValue
来实例化一个枚举类型,但是需要注意返回值是 Optional
类型,需要拆包后使用。
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
枚举量也可以包含参数化的多个 rawValue
,作为枚举量的内部属性。
enum ServerResponse {
case result(String, String)
case failure(String)
}
let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")
switch success {
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
print("Failure... \(message)")
}
// Prints "Sunrise is at 6:00 am and sunset is at 8:09 pm."
Swift 中的结构 struct
与类 class
很相似,都可以包含属性和方法,区别是 struct
在赋值时总是深拷贝,即复制全部数据,而 class
是浅拷贝,只复制引用。
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()
Swift 中的协议 protocol
类似于其他语言中的接口,定义了需要包含的属性和方法,包括各种数据类型。mutating
表示该方法可以修改内部的属性。
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
类或结构都可以采用 adopt
协议,即包含协议要求的属性和方法。
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
除了在定义新的类型时 adopt 协议,也可以通过 extension
让已有的类型支持协议。
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)
// Prints "The number 7"
协议可以用来实现多态。无法访问协议定义之外的属性和方法。
let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
// Prints "A very simple class. Now 100% adjusted."
// print(protocolValue.anotherProperty) // Uncomment to see the error
Swift 中的 Error
类似于其他语言中的异常,可以用一个 adopt 了 Error
协议的类型表示,比如枚举类型。
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}
用 throw
抛出一个错误,用 throws
标记可能抛出错误的函数,由调用函数的作用域负责处理错误。
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
}
return "Job sent"
}
可以用 do-catch
和 try
来处理错误,在可能抛出错误的函数调用前加上 try
,并将语句放在 do
代码块内,如果抛出了错误,错误对象将使用 error
作为名称,并进入到 catch
代码块内。
do {
let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
print(printerResponse)
} catch {
print(error)
}
// Prints "Job sent"
可以使用多个 catch
代码块,对不同类型的错误进行不同的处理。
do {
let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
print(printerResponse)
} catch PrinterError.onFire {
print("I‘ll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
// Prints "Job sent"
如果需要简化错误处理,比如出错直接返回 nil
,可以用 try?
。如果未抛出错误,函数的返回值将被转换为 Optional
类型,否则转换为 nil
。
let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")
函数中可以加入 defer
代码块,无论是否抛出错误,在函数返回前都会执行,用于做一些善后工作。
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}
let result = fridgeContent.contains(food)
return result
}
fridgeContains("banana")
print(fridgeIsOpen)
// Prints "false"
在函数名后用尖括号 <>
添加泛型。
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
makeArray(repeating: "knock", numberOfTimes: 4)
在函数体之前用 where
表示对泛型的约束和要求。
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Element: Equatable, T.Element == U.Element
{
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])
上面的函数使用 2 个类型参数 T
和 U
,要求两者都采用了 Sequence 协议,并且元素都采用了 Equatable 协议。
标签:sid swift float row ota width lis ams 参数
原文地址:https://www.cnblogs.com/huzheyu/p/ios-a-swift-tour.html