Swift初见
Hello World 就这么来了
print("Hello World")
这就是一个完整的程序。不需要main函数,甚至不用;号。
简单值
使用let声明常量,使用var声明变量。
- 通过一个值来声明变量和常量时,编译器会自动推断其类型,不用明确的声明类型
let myConstant = 12
var myVariable = 7
myVariable = 10
- 如果没有初始值,需要在变量后面声明类型,使用冒号分割
var explicitDouble : Double = 70
将值转换为字符串的方式
- 直接使用
String()显示转换
let label = "The width is"
let width = 12
let widthLabel = label + String(width)
- 使用
\()来转换
print("The width is \(width)")
多行字符串的使用
使用三个双引号"""来包含多行字符串内容。双引号不能与字符串内容在同一行出现,会报错。
let quotation = """
白日依山尽,
黄河入海流。
欲穷千里目,
更上一层楼。
"""
print(quotation)
数组和字典
使用方括号[]来创建数组和字典。
var shoppingList = ["catfish","water","tulips"]
var occupations = [
"Malcolm" : "Captain",
"Kaylee" : "Mechanic",
]
shoppingList.append("blue paint")
occupations["Jayne"] = "Public Relations"
创建一个空数组或空字典
let emptyArray = [String]()
let emptyDictionary = [String: Float]()
如果类型信息可以推测出来,可以直接使用[]或[:]来创建空数组和空字典
shoppingList = []
occupations = [:]
控制流
使用if和switch来进行条件操作,使用for-in、while和repeat-while来进行循环。包裹条件和循环变量的括号可以省略,但是语句体的大括号是必须得的。
if和for语句
let individualScores = [75,23,56,56,85]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
}else{
teamScore += 1
}
}
print(teamScore)
在if语句中,条件必须是一个布尔表达式。
也可以使用if和let一起来处理值缺失的情况。一个可选的值是一个具体的值或者是nil以表示值缺失。在类型后面加一个问号?来标记一个变量是可选的。
也可使用??操作符来提供一个默认值,如果值缺失就是用默认值来替代。
let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"
print(informalGreeting)
switch语句
switch支持任意类型的数据以及各种比较操作,不仅仅是整数以及测试相等
let vegetable = "red pepper"
switch vegetable {
case "cellery":
print("cellery")
case "cucumber":
print("cucumber")
case let x where x.hasSuffix("pepper"):
print("pepper")
default:
print("default")
}
default语句是必须有的。switch语句在匹配到case语句之后,程序会退出switch语句,不会继续执行,所以不需要在每个子句之后写break。
while语句
- 使用
while来重复运行一段代码直到条件改变
var n = 2
while n < 100 {
n *= 2
}
print(n)
- 循环条件可以在结尾,保证至少循环一次
var m = 2
repeat{
m *= 2
}while m < 100
print(m)
下标范围的表示:使用..<不包含上界;使用...包含上界
函数和闭包
使用func来声明一个函数,使用名字和参数来调用函数。使用->来指定函数的返回值类型。
func greet(person: String, day: String) -> String{
return "Hello \(person), today is \(day)"
}
greet(person: "Bob", day: "Tuseday")
默认情况下,函数使用参数名称作为参数的标签,也可以在参数的前面自定义参数标签,或者使用_表示不使用参数标签。
func greet(_ person: String, on day: String) -> String{
return "Hello \(person), today is \(day)"
}
greet("Bob", on: "Tuseday")
函数可以嵌套。被嵌套的函数可以访问外侧函数的变量,可以用来重构一个太长或太复杂的函数。
func returnFifteen() -> Int{
var y = 10
func add(){
y += 5
}
add()
return y
}
returnFifteen()
函数作为一等类型,是可以作为另一个函数的返回值或者参数的
- 作为返回值
func makeIncrementer() -> ((Int) -> Int){
func addOne(number: Int) -> Int{
return number + 1
}
return addOne
}
var incrementer = makeIncrementer()
incrementer(8)
- 作为参数
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool{
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(num: Int) -> Bool{
return num < 10
}
var nums = [20,12,4,56]
hasAnyMatches(list: nums, condition: lessThanTen)
函数实际上是一种特殊的闭包:他是一段能之后被调用的代码。闭包中的代码能访问闭包作用域中的变量和函数,及时闭包是在一个不同的作用域被执行的。
使用一个{}来创建一个匿名的闭包,使用in将参数和返回值类型的声明与闭包函数体分离。
var nums = [20,12,4,56]
nums.map({
(num: Int) -> Int in
let result = 3 * num
return result
})
对象和类
使用class和类名来创建一个类。类中属性的声明和常量、变量一样,唯一的区别就是上下文是类。
class Shape{
var numberOfSides = 0
func simpleDEscription() -> String {
return "A shape with \(numberOfSides) sides"
}
}
创建一个类的实例,在类名后面加上括号(),使用点语法来访问实例的属性和方法。
let shape = Shape()
shape.numberOfSides = 3
var shapeDescription = shape.simpleDEscription()
print(shapeDescription)
构造函数,使用init来创建一个构造器,来初始化类实例。使用deinit来创建一个析构函数,来在对象释放之前进行一些清理工作。
class NameShape{
var numberSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape named \(self.name) with \(numberSides) sides"
}
}
创建一个子类,是在类名后面加上父类的名字,中间用冒号:分开。重写父类的方法需要使用override标记。编译器同样也会检测使用override标记的方法是否在父类中。
class Square: NameShape{
var sideLength: Double
init(sideLength: Double,name: String) {
self.sideLength = sideLength
super.init(name: name)
numberSides = 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()
还可以使用setter和getter设置计算属性。
class EquilateralTriangle: NameShape{
var sideLength: Double = 0.0
init(sideLength: Double,name: String) {
self.sideLength = sideLength
super.init(name: name)
numberSides = 3
}
var perimeter: Double {
get{
return 3.0 * sideLength
}
set{
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangel with sides of length \(sideLength)"
}
}
在 setter中,新值的名字是newValue。或者在set之后的圆括号中显式的设置一个名字。
var perimeter: Double {
get{
return 3.0 * sideLength
}
set(myNewValue){
sideLength = myNewValue / 3.0
}
}
如果不需要计算属性,但仍需要在设置一个新值之前或之后运行代码,可以使用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 triangelAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangelAndSquare.square.sideLength)
print(triangelAndSquare.triangle.sideLength)
triangelAndSquare.square = Square(sideLength: 50, name: "lager square")
print(triangelAndSquare.triangle.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.rawValue)
}
}
}
let ace = Rank.ace
let aceRawValue = ace.rawValue
print(ace) //"ace\n"
默认情况下,Swift按照从0开始每次加1的方式为原始值就行赋值,也可以通过显示的赋值进行改变,如Ace被显示赋值为1,则剩下的原始值会按照顺序赋值。也可以使用字符串或者浮点数作为枚举的原始值。
可以使用init?(rawValue:)初始化构造器来从原始值创建一个枚举实例。
if let covertedRank = Rank(rawValue: 3) {
let threeDescription = covertedRank.simpleDescription()
print(threeDescription) //"3\n"
}
给枚举成员设置关联值
let failure = ServerResponse.result("5:00 am", "9:00 pm")
let failure = ServerResponse.failure("Out of cheese")
switch failure {
case let .result(sunrise, sunset):
print("Sunrise is \(sunrise) and sunset is at \(sunset)")
case let .failure(message):
print("Failure... \(message)")
}
结构体
使用struct来创建一个结构体。结构体和类有很多相同的地方,包括方法和构造器,最大的一个区别就是结构体是传值,类是传引用
构造一个结构体
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"
}
}
}
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 threeOfSapdesDescription = threeOfSpades.simpleDescription()
print(threeOfSapdesDescription)//The 3 of spades
var clone = threeOfSpades
clone.rank = Rank.ace
print(clone.simpleDescription())//The ace of spades
print(threeOfSpades.simpleDescription())//The 3 of spades
通过引用一个类和结构体来解释 结构体是传值,类是传引用,重点看注释
class Shape{
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides"
}
}
let shape = Shape()
shape.numberOfSides = 3
print(shape.simpleDescription())//A shape with 3 sides
var clone = shape
clone.numberOfSides = 5
print(clone.simpleDescription())//A shape with 5 sides
print(shape.simpleDescription()) //A shape with 5 sides
协议protocol
使用protocol来声明一个协议。类型、枚举和结构体都可以遵循协议。
protocol ExampleProtocol{
var simpleDescription: String {
get
}
mutating func adjust()
}
class SimpleClass: ExampleProtocol{
var simpleDescription: String = "A very simple calss"
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjust"
}
}
var a = SimpleClass()
a.adjust()
print(a.simpleDescription)
struct SimpleStruct: ExampleProtocol{
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStruct()
b.adjust()
print(b.simpleDescription)
注意: 声明SimpleStruct时候mutating关键字来标记一个会修改结构体的方法。SimpleClass不需要标记,因为类中的方法通常是可以修改类属性的。
拓展: 可以像使用其他命名类型一样使用协议名。可以创建一个有不同类型,但是都实现了同一个协议的对象集合。处理的对象是协议的值时,协议外定义的方法不可用。
扩展extension
使用extension 声明一个扩展,用来给现有的类型添加新的功能。可以使用扩展让某个在别处声明的类型来遵守某个协议。
extension Int: ExampleProtocol{
var simpleDescription: String{
return "The number is \(self)"
}
mutating func adjust() {
self += 12
}
}
print(7.simpleDescription)
错误处理
通过实现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。在do代码块中,使用try来标记可以抛出错误的代码。在catch代码块中,错误默认命名为error,也可以自定义名字。
do {
let printerResponse = try send(job: 12, toPrinter: "Never Has Toner")
print(printerResponse)
} catch {
print(error)
}
也可以使用多个catch来处理不同的错误类型。
do {
let printerResponse = try send(job: 12, toPrinter: "Never Has Toner")
print(printerResponse)
} catch PrinterError.outOfPaper {
print("outOfPaper")
} catch PrinterError.onFire{
print("onFire")
} catch{
print(error)
}
- 使用
try?将结果转化为可选的。如果函数抛出错误,该错误会被抛弃并且结果为nil,否则,结果会是一个包含函数返回值的可选值。
let printerSuccess = try? send(job: 11, toPrinter: "Test")
拓展:
使用defer代码块来表示在函数返回前,函数最后执行的代码。无论函数是否会抛出错误,这段代码都会被执行。
var fridgeIsOpen = false
let fridgeCountent = ["milk","eggs","leftovers"]
func fridgeContains(_ food: String) -> Bool{
fridgeIsOpen = true
defer{
fridgeIsOpen = false
}
let result = fridgeCountent.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: "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], [2])