由于项目原因,需要做一款 iOS 应用。。。然后这个任务就落到了我身上…emm
经过我的全方面了解,目前打算采用 SwiftUI 进行开发,对于 Swift 语言本身需要在实战中慢慢积累。。
这里是一篇快速入门,教程来源于:http://www.swift51.com
let
声明常量,var
声明变量
值不会被隐式转换为其他类型,如果需要进行不同类型的运算,需要显示的转换:
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."
let quotation = """
I said "I have \(apples) apples."
And then I said "I have \(apples + oranges) pieces of fruit."
"""
数组和字典:使用方括号[]
来创建数组和字典(最后一个元素后面允许有逗号,
)
// 创建数组
var shoppingList = ["catfish", "water", "tulips",]
shoppingList[1] = "bottle of water"
// swift中的数组是动态数组,可以动态扩容
shoppingList.append("blue paint")
// 创建字典
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
// 创建空数组、空字典
shoppingList = []
occupations = [:]
使用 if
和 switch
进行条件操作,for-in
、while
、repeat-while
进行循环
if 语句中,条件必须是一个布尔表达式,而不能是其他,就算是数字也不会和 0 比较
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
可以使用if
和 let
处理缺失值的情况。这些值由 “可选值” 来代表,一个 “可选值” 是一个具体的值或者是 nil
表示缺失值
?
来标记这个变量是可选的// optionalString是一个可选值,如果该值缺失则为nil
var optionalString: String? = "Hello"
print(optionalString == nil)
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
} else {
greeting = "Hello, Stranger"
}
??
可以为可选值提供一个默认值,如果可选值缺失则用默认值代替let nickname: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickname ?? fullName)"
switch
支持任意数据类型的数据以及各种比较操作,不仅仅是整数以及测试相等
Swift 中必须有
default
语句作为默认值,且每个匹配的语句执行后自动跳出,无需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.")
// 将匹配等式的值赋给常量x
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 (_, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
使用 while
或 repeat-while
重复运行一段代码直到条件改变
var n = 2
while n < 100 {
n *= 2
}
print(n)
var m = 2
repeat {
m *= 2
} while m < 100
print(m)
在循环中使用 ..<
表示下标范围:
for i in 0..<4 {
print(i) // 0 1 2 3
}
函数声明和调用:
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")
默认情况下,函数使用参数名称作为参数的标签,在参数名称前可以自定义参数标签,或者使用 _
表示不使用参数标签
func greet(_ person: String, _ day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", "Wednesday")
使用元组来生成复合值,比如让一个函数返回多个值,该元组的元素可以用名称或数字来获取:
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.sum) // 120
print(statistics.2) // 120
函数可以嵌套,被嵌套的函数可以访问外侧函数的变量
一般使用嵌套函数来重构一个太长或太复杂的函数
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
Swift 中,函数是第一等类型: first-class type 的含义
// (Int) -> Int 表示一个函数类型: 它参数是Int,返回值也是Int
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
func hasAnyMatch(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, 11, 9]
hasAnyMatch(list: numbers, condition: lessThanTen)
函数实际上是一种特殊的闭包,闭包中的代码仅能访问闭包作用域中的变量和函数,使用 {}
创建一个匿名闭包
var numbers = [20, 19, 11, 9]
// 使用 in 将 [参数]、[返回值类型的声明] 与 [闭包函数体] 分离
let newNumbers = numbers.map({ (number: Int) -> Int in
let result = 3 * number
return result
})
print(numbers) // [1, 2, 3, 4]
print(newNumbers) // [3, 6, 9, 12]
var numbers = [1, 2, 3, 4]
let newNumbers = numbers.map({ number in 3 * number })
print(newNumbers) // [3, 6, 9, 12]
var numbers = [1, 2, 3, 4]
let sortedNumbers1 = numbers.sorted(by: {
(v1: Int, v2:Int) -> Bool in
return v1 - v2 > 0
})
print(sortedNumbers1) // [4, 3, 2, 1]
let sortedNumbers2 = numbers.sorted { $0 > $1 }
print(sortedNumbers2) // [4, 3, 2, 1]
// 父类
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 EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
// 构造函数
init(sideLength: Double, name: String) {
// 设置子类声明的属性
self.sideLength = sideLength
// 调用父类的构造器
super.init(name: name)
// 改变父类定义的属性值
numberOfSides = 3
}
// 计算属性(含有get和set)
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)
不使用计算属性,在设置一个新值之前或之后运行代码:使用 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")
print(triangleAndSquare.square.sideLength) // 10.0
print(triangleAndSquare.triangle.sideLength) // 10.0
// 修改了正方形,同时三角形的边长也变了
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength) // 50.0
处理变量的可选值:
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
如果枚举类继承 Int
,默认从 0 开始每次加 1 的为原始值进行赋值(可以显示的赋值改变),使用 rawValue
访问原始值
也可以使枚举类继承
String
、Float
等类型,都有各自的默认值以及原始值赋值规则
enum Rank: Int {
case ace = 1 // 显示的更改第一个值为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
// 根据原始值创建一个枚举实例,存在则返回该枚举成员,否则返回nil
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
如果没有比较有意义的原始值,可以不提供原始值(不继承任何类,无法通过 rawValue
访问枚举对象)
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()
枚举成员除了可以有原始值,还可以拥有关联值:
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 failure {
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
print("Failure... \(message)")
}
Swift 中结构体和类最大的区别:结构体是传值,类是传引用
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()
使用 protocol
声明协议:类、枚举、对象都可以遵守协议
// 声明一个协议
protocol ExampleProtocol {
var simpleDescription: String { get }
// mutating关键字用来标记一个会修改结构体的方法
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 // "A very simple class. Now 100% adjusted."
// 结构体中使用mutating标记一个会修改结构体的方法
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription // "A simple structure (adjusted)"
使用 extension
来为现有的类型添加功能,比如新的方法和计算属性:
// 为Int类型添加新的方法
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)
即使 protocolValue
变量运行时的类型是 simpleClass
,编译器还是会把它的类型当做 ExampleProtocol
:
(声明变量时,左侧是它的真实类型)
这表明对于某个协议类型的对象,无法调用在协议之外的方法或属性
let protocolValue: ExampleProtocol = SimpleClass()
print(protocolValue.simpleDescription)
// print(protocolValue.anotherProperty) // 去掉这个注释会报错
采用 Error
协议的类型来表示错误:
// 遵循Error协议,表示是一个错误
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}
使用 throw
来抛出一个错误,使用 throws
来表示一个可以抛出错误的函数
// throws 表示一个可以抛出错误的函数
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
}
return "Job sent"
}
使用 do-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 {
// 错误默认命名为error变量
print(error)
}
使用 try?
将结果转换成可选的:
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 {
// 使用defer可以把函数调用之初就要执行的代码
// 和函数调用结束时的扫尾代码写在一起
// 虽然这两者的执行时机截然不同
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}
let result = fridgeContent.contains(food)
return result
}
fridgeContains("banana")
print(fridgeIsOpen)
在 <>
中写一个名字来创建一个泛型函数:
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result: [Item] = []
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
makeArray(repeating: "a", numberOfTimes: 4)
函数、方法、类、枚举和结构体,都可以使用泛型:
// 重新实现 Swift 标准库中的可选类型
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
// 限定T的元素是可比较的,T的元素和E的元素类型相等
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])
<T: Equatable>
和 <T> ... where T: Equatable>
的写法是等价的