Swift 语言快速入门

  • 简单值
  • 控制流
  • 函数闭包
  • 对象和类
  • 枚举和结构体
  • 协议和拓展
  • 错误处理
  • 泛型

由于项目原因,需要做一款 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 = [:]

控制流

使用 ifswitch 进行条件操作,for-inwhilerepeat-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}
}

可以使用iflet 处理缺失值的情况。这些值由 “可选值” 来代表,一个 “可选值” 是一个具体的值或者是 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.")// 将匹配等式的值赋给常量xcase 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)

使用 whilerepeat-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 = 0for 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 = 10func 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 inlet result = 3 * numberreturn 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]
  • 可以通过参数位置而不是参数名字来引用参数(这在很短的闭包中非常有用)

    1. 当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在圆括号后面
    2. 当一个闭包是传给函数的唯一参数,可以完全忽略圆括号
var numbers = [1, 2, 3, 4]let sortedNumbers1 = numbers.sorted(by: {(v1: Int, v2:Int) -> Bool inreturn 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 = 0var name: Stringinit(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)

不使用计算属性,在设置一个新值之前或之后运行代码:使用 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) // 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 访问原始值

也可以使枚举类继承 StringFloat 等类型,都有各自的默认值以及原始值赋值规则

enum Rank: Int {case ace = 1 // 显示的更改第一个值为1case two, three, four, five, six, seven, eight, nine, tencase 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, clubsfunc 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: Rankvar suit: Suitfunc 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 = 69105func 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 outOfPapercase noTonercase 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 = truedefer {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 nonecase 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> 的写法是等价的

Swift 语言快速入门相关推荐

  1. Swift教程Swift语言快速入门(内部资料)

    Swift语言快速入门(内部资料) 试读下载地址:http://pan.baidu.com/s/1eQCGRHw 前言Swift教程Swift语言快速入门(内部资料)Swift教程Swift语言快速入 ...

  2. Swift语言快速入门

    Swift语言快速入门(首部同步新版官方API文档和语法的Swift图书,确保代码可编译,作者专家在线答疑,图书勘误实时跟进) 极客学院 编著   ISBN 978-7-121-24328-8 201 ...

  3. Swift 1.1语言快速入门7.2使用无参函数

    Swift 1.1语言快速入门7.2使用无参函数 无参数函数是指没有参数列表的函数.以下对它的声明定义.调用以及最简单的函数--空函数进行详细的讲解 7.2.1  无参函数的声明定义 函数在使用之前必 ...

  4. Common Lisp语言快速入门

    zhezhelin Common Lisp语言快速入门 Lisp是软件领域的分裂力量.一方面,Lisp爱好者誓言Lisp比软件领域内的其它语言都更加快捷.整洁和强大:而反对者则辩称,不可靠的执行和库支 ...

  5. sql语言和php,SQL语言快速入门(三)_php

    我们日常使用SQL语言的工作过程中,使用最多的还是从已经建立好的数据库中查询信息.下面,我们就来详细介绍一下如何使用SQL语言实现各种数据库查询操作. SELECT-FROM 为方便讲解,我们在数据库 ...

  6. Swift语言高速入门

    Swift语言高速入门(首部同步新版官方API文档和语法的Swift图书,确保代码可编译,作者专家在线答疑,图书勘误实时跟进) 极客学院 编著   ISBN 978-7-121-24328-8 201 ...

  7. JAVA程序设计-头歌实训-------# 第一阶段 Java语言快速入门

    第一阶段 Java语言快速入门 第1关:Java程序的基本框架:Hello Java World! 任务描述 本关的任务是编写你人生中第一个Java程序,从此开启你的Java实训之旅,显示效果如下: ...

  8. Python语言快速入门(上)

    前言 首先需要说明的是,本期图文仅涉及Python语言的基础入门内容,由于我也不是计算机本专业的学生,这些内容完全是出于强烈的学业兴趣而撰写的,其中难免会出现一些表述不恰当的地方,如果存在问题,欢迎我 ...

  9. Python语言快速入门上

    目录 1.前言 2.变量和常量 1)Python对象模型 2)Python变量 二.运算符和表达式 [运算符和表达式] [位运算符] [逻辑运算符] [成员运算符] [身份运算符] [常用内置函数] ...

最新文章

  1. 计算机c1 c语言答题,全国计算机级考试二级C语言上机答题技巧.doc
  2. ubuntu安装openssl命令
  3. CentOs7中安装sonarQube服务系列------1.CentOS7中Docker的安装
  4. redhat yum 安装 mysql_RedHat/CentOS 为yum安装remi源 | 学步园
  5. C和C++栈stack
  6. 用户画像标签维度_神策数据钟秉哲:一文了解用户标签画像,从洞察到突破
  7. 斐波那契查找(Fibonacci Search)和折半查找
  8. Twitter Storm 序列化
  9. 使用 NodeJS+Express+MySQL 实现简单的增删改查
  10. 双线性插值算法c 语言实现,双线性插值 - CristianoC的个人空间 - OSCHINA - 中文开源技术交流社区...
  11. php post json请求参数传递_php post json参数的传递和接收处理方法
  12. 实体框架 5.0:空间数据类型、性能增强、数据库提升
  13. 洛谷1005 【NOIP2007】矩阵取数游戏
  14. Linux命令执行顺序
  15. shell变量、函数和数组以及字符串的截取
  16. hdu1527取石子游戏---(威佐夫博弈)
  17. 由系统函数求零极点图、频率响应(幅频特性、相频特性)的 Matlab 和 Python 方法
  18. 判断一个三位数是否为水仙花数
  19. centos6.5重置密码
  20. 3、TM4之GPIO的输入输出

热门文章

  1. 只要掌握了这条法则,你就有赚不完的钱
  2. 做生意,没亏过钱,自然也没赚过钱
  3. 中国程序员最应该感谢的几家公司
  4. 监视器(monitor)
  5. sql azure 语法_什么是Azure SQL Cosmos DB?
  6. sql server新增列_SQL Server 2017中的新增功能
  7. [Swift]LeetCode480. 滑动窗口中位数 | Sliding Window Median
  8. 2018.3.12 Leecode习题 给定一个整数数列,找出其中和为特定值的那两个数。
  9. 将美化进行到底,把 PowerShell 做成 oh-my-zsh 的样子
  10. 看看我们的程序猿寻常都爱看些什么好书?