Swift学习总结(第一阶段)

一、基础语法

  • swift语句结束不需要分号(写了也没有问题),有一种情况需要分号,如果一行代码中有多条语句,这时候就必须要分号隔开

  • swift字符串,数组,字典不需要@标示

  • swift是类型安全的语言,所有的类型都不会自动转换(如:Int和UInt类型不能直接运算),同时swift具有强大的类型推测,所以很多时候我们不需要声明类型

  • swift的==布尔值使用小写true和false,而不用YES/NO==,++判断语句只能使用==Bool==类型++

二、数据类型

  • 与objc一样,swift支持以前(objc)使用的所有数据类型,swift的类型名字首字母大写,如Int, Float, NSInteger

  • swift支持可选类型(Optionals)类型,标识变量可能为空,基础数据类型也可为空,可选类型不能直接赋非可选类型


  var a: Int? = 10

  var b: Int = a          // 报错,不同类型不能赋值

  • swift支持使用_来分割数值来增强可读性而不影响值,如一亿可以表示为下面形式

  let oneMillion = 1_000_000

  • swift数值类型进行运算符计算的时候不会自动进行类型转换,通常可以通过类型的构造方法进行类型转换

  var a: Int = 12

  var b: Float = 23

  var c = a + b          // 报错

  var d = Float(a) + b    // 正确

三、常量变量

  • 与C/Obj-C不同,swift的常量更为广义,支持任意类型,==常量只能赋值一次==

  • swift的变量和常量在声明的时候类型就已经确定(由编译器自动识别或开发者指定)

  • 使用let声明的集合为可变集合,使用var声明的集合为不可变集合


// 常量:使用let声明,赋值后就不能再修改

let a = NSMutableArray()

let b = 12

let c: Float = 12      // 类型标注(type annotation)

let d = b + 12

a.addObject(11)        // str == [11]

let e = a              // str == [11], d == [11]

a.addObject(12)        // str == [11, 12], d == [11, 12]

// 变量:使用var声明

var f: Double? = 12

var g = "hello world"

四、序列和集合

1. 字符串String

swift字符串是由Character字符组成的集合,==支持+操作符==,字符串与值类型(与Int, Float)一样,是值类型,在传值的时候都会进行拷贝,当然这回带来一定的性能损耗,swift编译器在编译的时候会进行优化,保证只在必要的情况下才进行拷贝


// 1. 与NSString不同,声明不需要@前缀,支持转移字符

let name1 = "bomo\n"

// 2. 空串(下面两种方式等价)

let name2 = ""

let name3 = String()

// 3. 字符串由字符Character组成,定义字符

let character1: Character = "!"

// 4. 常见属性,方法

name1.isEmpty                  // 判空

name1.characters.count          // 获取字符串的字符数

name1.uppercased()              // 转大写

name1.lowercased()              // 转小写

name1.hasPrefix("bo")          //前缀

name1.hasSuffix("mo")          //后缀

// 5. 加法运算

let hello = "hello " + name1  // hello bomo\n

// 6. 比较(比较值,而不是地址)

let name4 = "b" + "omo\n"

name4 == name1                // True

// 7. 字符串插值(使用反斜杠和括号站位)

let city = "广州"

let hello2 = "I'm \(name1) from \(city)"

// 8. 格式化字符串

let f = 123.3233

var s = String(format: "%.2f", f)    //123.32

1. 数组Array

swift的数组可以是有类型的(泛型),存放同类型的数据,如果添加一个错误的类型会报编译错误,默认情况下编译器会自动识别


//1. 数组的写法为:Array,也可以简写成[Int]

//2. 数组初始化与NSArray类似,直接用中括号括起来,里面值用逗号隔开

var array0 = [Int]()

var array1: [Int] = [1, 3, 5, 7, 9]

var array2: Array = array1

array1.append(11)            // [1, 3, 5, 7, 9, 11]

array1.insert(0, atIndex: 0)  // [0, 1, 3, 5, 7, 9, 11]

array1.isEmpty                // False

array1.count                  // 7

// 3. 如果初始化时不指定类型,而编译器也不能识别出类型,这时候,会被当成NSArray处理

var array3 = []                      // array3 为 NSArray类型的空数组

// 4. 如果声明的时候使用不同的类型,编译器会把数组识别为NSObject类型

var array4 = ["fdsa", 121]            // array4 为 Array 类型

// 5. 集合支持加法运算,相当于NSMutableArray的addObjectsFromArray

array1 += [2, 4, 6, 8, 10]    // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

// 6. 使用let声明的数组不可变,不能修改数组array3

let array5: [Int] = [1, 3, 5, 7, 9]

//array5.append(2)              // 报编译错误

// 7. 集合使用下标索引,支持区间索引,区间不可越界

var array6: [Int] = [1, 3, 5, 7, 9]

array6[1] = 4                      // [1, 3, 5, 7, 9]

array6[1...3] = [2, 3, 4]          // [1, 2, 3, 4, 9]

array6[0...2] = array6[1...3]      // [2, 3, 4, 4, 9]

// 8. 迭代数组的时候,如果需要索引,可以用enumerate方法

for (index, value) in array4.enumerated() {

    //do something

}

2. 字典Dictionary

与数组类型一样,字典也支持泛型,其键值类型都可以指定或有编译器识别,其中Key的类型,==必须遵循Hashable协议(哈希表==),swift中基础数据类型都是可hash的(String、Int、Double和Bool)


// 1. 用法与oc类似,初始化不需要@

var dict1 = ["key1": 1, "key2": 2, "key3": 3]

// 2. 声明方式

var dict2: Dictionary = dict1        //dict2与dict1不是一个对象

var dict3: [String: Int] = dict1                  //通常采用这种方式声明类型

// 3. 不声明类型,编译器又无法识别,则为NSDictionary

var dict4 = [:]

var dict5: [Int: String] = [:]

// 4. 修改或添加键值对

dict1["key3"] = 4

// 5. 删除键

dict1["key3"] = nil

// 6. key不存在不报错,返回可空类型nil

let value4 = dict1["key4"]

// 7. 字典迭代返回key/value元组,类似python

for (key, value) in dict1 {

    print("\(key) = \(value)")

}

3. Set集合

Set集合用于存放无序不重复的对象,用法与数组类似,重复的项会被忽略


var s: Set = [1, 3, 5, 6, 7, 4, 3, 7]    // [1, 3, 4, 5, 6, 7]

s.count

s.isEmpty

s.insert(3)

s.remove(3)

s.contains(3)

集合操作

  • 使用“是否相等”运算符( == )来判断两个 合是否包含全部相同的值。

  • 使用 isSubset(of:) 方法来判断一个 合中的值是否也被包含在另外一个 合中。

  • 使用 isSuperset(of:) 方法来判断一个 合中包含另一个 合中所有的值。

  • 使用 isStrictSubset(of:) 或者 isStrictSuperset(of:) 方法来判断一个 合是否是另外一个 合的子 合或 者父 合并且两个 合并不相等。

  • 使用 isDisjoint(with:) 方法来判断两个 合是否不含有相同的值(是否没有交 )

4. 元组Tuple

swif也支持元组,可以很方便的使用元组包装多个值,也使得函数返回多个值变得更加方便,特别是临时组建值得时候

  • 支持任意类型

  • 支持同时赋值

  • 支持自定义key,支持索引

  • ==元组不是对象,不是AnyObject类型,由于swift是强类型的,所以元组有时不能当做普通的对象使用,例如不能把元组加到数组里面,元组内的所有类型必须是明确的==


// 1. 声明一个元组,元组支持任意类型

let httpError1 = (404, "Not Found")

let point = (100, 50)

// 2. 可以分别赋值

let (x, y) = point

print(x)      // 100

print(y)      // 50

// 3. 使用下标取元组元素,下标从0开始

print(httpError1.0)      // 404

print(httpError1.1)      // Not Found

// 4. 可以给数组元素取名

let httpError2 = (code: 404, errorMessage: "Not Found")

print(httpError2.code)              // 404

print(httpError2.errorMessage)      // Not Found

// 5. 可以用下划线表示忽略部分值

let (a, _) = point

==6. 集合的赋值和拷贝行为==

swift的集合通常有Array和Dictionary,他们在赋值或传递的时候,行为上有所不同,==字典类型Dictionary或数组类型Array在赋值给变量或常量的时候,只要有做修改,就会进行值拷贝==,并且不会作用到原来变量上


var dict1 = ["a": 1, "b": 2]

var dict2 = dict1

print(dict1 == dict2)        // true

dict2["a"] = 3                // 修改dict2

print(dict1 == dict2)        // false

var arr1 = ["a", "b"]

var arr2 = arr1

print(arr1 == arr2)          // true

arr1[0] = "c"                // 修改arr1

// arr1.append("c")

print(arr1 == arr2)          // false

五、可选类型(可空类型)

swift加入了可空类型让我们使用数据的时候更为安全,我们需要在可空的地方使用可选类型声明该变量可为空,==不能给非可选类型设值nil值==,在使用的时候可以明确的知道对象是否可能为nil,有点像ObjC的对象,对象可以为nil,也可以不为nil,而swift得可选类型范围更广可以作用于任何类型(基础类型,类,结构体,枚举)

1. 声明


// 1. 声明可选类型,在类型后面加上?

var obj1: NSObject?

obj1 = NSObject()

obj1 = nil

// 2. 不能给一个可选类型赋nil,下面会报错,

var obj = NSObject()

obj = nil

// 3. 如果声明可选变量时没有赋值,则默认为nil

var i: Int?

// 4. 一个函数返回一个可选类型

func getdog() -> String? {

    return "ErHa"

}

// 5. 不能把可选类型赋值给非可选类型,下面会报错

let cat: String = dog

2. 强制解析

可选类型不能直接使用,需要通过取值操作符!取得变量的值,才能使用,如果变量有值,则返回该值,如果变量为空,则会运行时错误


var b: Int?

var a: Int

a = 12

b = 13

let c = a + b!              // 先对b取值,再运算

var b: Bool? = nil

if b! {                    // b为空,编译不报错,运行时报错

    print("true")

} else {

    print("false")

}

3. 可选绑定

使用可选绑定可以判断一个可选类型是否有值,如果有值,则绑定到变量上,如果没有值,返回false,使用if-let组合实现


var i: Int? = nil

if let number = i {

    print("\(number)")

} else {

    print("nil")

}

可选绑定还支持绑定条件


var i: Int? = nil

if let number = i where i > 10 {

    print("i不为空且大于10 \(number)")

} else {

    print("nil")

}

4. 隐式解析

声明类型的时候可以使用隐式解析,即在使用可选变量的时候自动取值,不需要调用!操作符,


// 一个函数返回一个可选类型

func getdog() -> String? {

    return "wangcai"

}

//假定我们通过getdog方法返回的值一定不为空

var dog: String? = getdog()

let cat: String = dog!          // 使用前需要通过!强制取值

使用dog的时候都需要取值我们觉得太麻烦了,可以声明成隐式可选类型,使用的时候自动取值


var dog: String! = getdog()    // 实际上dog还是可选类型,只是使用的时候回自动取值

let cat: String = dog          // 在使用dog的时候会自动进行取值,不需要取值操作符

5. 可选类型自判断链接

在使用可选类型之前,需要进行判断其是否有值,才能使用,通过!操作符取值后使用(保证有值的情况下),或通过if-let可选绑定的方式,swift提供了一种类似C#语言的语法糖可以让代码更为简洁,可以自动判断值,如果有值,则操作,无值则不操作,并返回nil,在使用前加上?


class Person {

    var favDog: Dog?

}

class Dog {

    var name: String?

}

var p = Person()

var d = Dog()

// p.favDog = d

p.favDog?.name = "tobi"  // 如果p.favDog为空,不设置name

if let name = p.favDog?.name {

    // p.favDog不为空且p.favDog.name不为空

} else {

    // p.favDog为空或p.favDog.name为空

}

自判断链接还支持多连接如


let identifier = john.residence?.address?.buildingIdentifier

6. 可选关联运算符

可选关联运算符可对可选类型进行拆包,如果可选类型对象为nil,返回第二个操作数,第二个操作数类型必须和第一个操作数同类型(可选或不可选)


let defaultColorName = "red"

var userDefinedColorName: String?  // defaults to nil

var colorNameToUse = userDefinedColorName ?? defaultColorName

  • defaultColorName和userDefinedColorName必须是同类型(String或String?)

  • 如果userDefinedColorName不为空,返回其值,如果userDefinedColorName为空,返回defaultColorName

  • 返回值colorNameToUse的类型同??的第二个操作数的类型,为String

六、运算符

swift运算符在原有的基础上做了一些改进,还添加了一下更高级的用法,还有新的运算符

  • =运算符不返回值

  • 符合运算符+=, -=等不返回值

  • 比较运算符可以用于元组的比较(逐个比较,如果遇到不等的元素,则返回,默认最多只能比较7个元素的元组,超过则需要自定义)


(1, "zebra") < (2, "apple")    // true,因为 1 小于 2

  • 字符串String,字符Character支持+运算符

  • 浮点数支持%求余运算

  • ++/--运算在swift3被抛弃,用+=/-=代替

  • 支持溢出运算符(&+, &-, &*),可以在溢出时进行(高位)截断

  • 支持位运算符(>>, <<)

  • 支持三目运算符(a ? b : c)

  • 支持逻辑运算符(&&, ||, !)

  • 恒等于/不恒等于


===:这两个操作符用于引用类型,用于判断两个对象是否指向同一地址

!==:与===相反,表示两个变量/常量指向的的地址不同

==:表示两个对象逻辑相等,可以通过重载运算符实现相等的逻辑,两个值相等的对象可以是不同地址的对象

!=:与==相反,表示两个对象逻辑不等

  • 区间运算符

可以使用a...b表示一个范围,


a...b: 从a到b并包含a和b

a..

范围运算符也可以作用于字符串


let az = "a"..."z"      // 返回的是CloseInteval或HalfOpenInterval

az.contains("e")        // True

  • 空合运算符??

//可选类型取值,如果不为空则返回该值,如果为空则去第二个操作数

let result = a ?? b

七.控制流

swift使用三种语句控制流程:for-in、for、switch-case、while和repeat-while,且判断条件的括号可以省略

guard-else

翻译为保镖模式,在执行操作前,进行检查,如果不符合,则拦截,使用方式与if有些类似,如果与let结合使用,可以对可选类型解包


guard let i = i where i > 0 else {

        // 在这里拦截,处理不符合条件的情况

        return

    }

// 符合条件的处理,这个时候已经对i进行了拆包,i是非可选类型,可以直接使用

switch-case

  • itch语句支持更多数据类型(String,Int, Float, 元组, 枚举),理论上switch支持任意类型的对象(需要实现~=方法或Equatable协议,详情参见这里)

  • se可以带多个值,用逗号隔开

  • se可以支持区间(a...b),支持元组,区间可以嵌套在元组内使用

  • se多条语句不需要用大括号包起来

  • se语句不需要break,除了空语句,如果需要执行下面的case,可以使用fallthrough

  • case不能命中所有的情况,必须要default,如Int,String类型,否则编译会失败

  • 用fallthrough关键字声明接着执行下一条case语句,注意,如果case语句有赋值语句(let),则fallthrough无效

带标签的语句

如果有多层嵌套的情况下,有时候我们需要在某处直接退出多层循环,在objc下并没有比较好的方式实现,需要添加退出标识,然后一层一层退出,而在swift可以很方便的退出多层循环,首先需要使用标签标识不通的循环体,形式如下


labelName : while condition { statements }

看下面例子


outerLoop1 : for i in 1...10 {

    outerLoop2 : for j in 1...10 {

        outerLoop3 : for k in 1...10 {

            if j > 5 {

                // 1. 跳出一层循环(默认)继续outerLoop2的循环

                break

                // 2. 跳出两层循环,继续outerLoop1的循环

                // break outerLoop2

                // 3. 跳出三层循环,退出整个循环,继续后面的语句

                // break outerLoop1

            }

        }

    }

}

八、函数

1. 基本形式


//有返回值

func 函数名(参数名1:参数类型1, 参数名2:参数类型2) -> 返回值类型 {

    // 函数体

}

//多个返回值(元组)

func getPoint() -> (x: Int, y: Int) {

    return (1, 3)

}

var p = getPoint()

p.x

//无参数无返回值

func sayHello() {

    // 函数体

}

//egg

func add(a: Int, b: Int) -> Int {

    return a + b

}

// 调用

add(12, b: 232)

2. 可变参数

可变参数只能作为最后一个参数,一个方法最多只有一个可变参数,通过在变量类型名后面加入(...)的方式来定义可变参数。


func sum(numbers: Int...) -> Int {

    var sum = 0

    for number in numbers {

        sum += number

    }

    return sum

}

3. 外部参数名

默认情况下,如果不指定外部参数名,swift编译器会自动为函数参数声明与内部参数名同名的外部参数名(格式为:外部参数名 内部参数名: 类型名)


//默认情况下,外部参数名与内部参数名一样

func add(first a: Int, second b: Int) -> Int {

    return a + b

}

// 调用

add(first: 10, second: 20)

如果函数在第一个参数定义外部参数名,必须显示指定,当然我们还可以通过下划线_让函数忽略参数名


func add(a: Int, _ b: Int) -> Int {

    return a + b

}

add(1, 2)

4. 函数默认值

函数还支持声明默认值,(格式为:外部参数名 内部参数名: 类型名 = 默认值)


func log(msg: String, isDebug: Bool = true) {

    if isDebug {

        print(msg)

    }

}

log("fail")

log("success", isDebug: false)

5. 闭包

  • 闭包作为变量

  • 闭包作为函数参数

  • 闭包作为函数返回值

  • 闭包函数声明


func add(a: Int, b: Int) -> Int {

    return a + b

}

//函数作为变量,函数hello赋给somefunc,并调用

let somefunc: (Int, Int) -> Int = add

somefunc(10, 20)      // 30

//函数作为参数

func logAdd(a:Int, b:Int, function: (Int, Int) -> Int) {

    // 函数内容

    print("begin")

    function(a, b)

    print("end")

}

logAdd(12, b: 23, function: add)

//函数作为返回值(包装一个函数,在执行前后输出信息),函数作为参数又作为返回值

func addWrapper(addFunc: (Int, Int) -> Int) -> ((Int, Int) -> Int) {

    // 函数内容

    func wrapper(a: Int, b: Int) -> Int {

        print("begin")

        let res = addFunc(a, b)

        print("end")

        return res

    }

    return wrapper

}

var newAdd = addWrapper(add)

newAdd(12, 32)

//闭包函数声明形式

{ (parameters) -> returnType in

    statements      // 可以有多行

}

闭包函数


//定义一个函数变量

var addfunc: (Int, Int) -> Int

//闭包的写法

// 1. 完整写法

addfunc = {(a: Int, b: Int) -> (Int) in

    //var c = a + 1      //函数体可以有多条语句,如果在同一行,需要用分号隔开,函数体不需要大括号

    return a + b

}

// 2. 前面的addfunc变量可以推断出后面函数的参数类型和返回值类型,故可以省略

addfunc = {(a, b) in return a + b}

// 3. 参数列表括号可以省去,函数只有一条语句时,return可以省略

addfunc = {a, b in a + b}

// 4. 参数和in可以省去,通过$和索引取得参数

addfunc = {$0 + $1}

// 操作符需要的参数与函数参数一致,可以省去参数,并使用括号括起来,作为参数时,可不用括号

addfunc = (+)

6. Trailing(尾行)闭包

如果函数作为另一个函数的参数,并且是最后一个参数时,可以通过Trainling闭包来增强函数的可读性


func someFunctionThatTakesAClosure(a: Int, closure: () -> ()) {

    // 函数体部分

}

// 1. 一般形式

someFunctionThatTakesAClosure(10, closure: {

    // 闭包主体部分

})

// 2. Trainling闭包的方式

someFunctionThatTakesAClosure(10) {

    // 闭包主体部分

}

// 3. 如果没有其他参数时,可以省略括号

someFunctionThatTakesAClosure {

    // 闭包主体部分

}

7. Escaping(逃逸)闭包

如果一个闭包/函数作为参数传给另外一个函数,但这个闭包在传入函数返回之后才会执行,就称该闭包在函数中"逃逸",需要在函数参数添加@escaping声明,来声明该闭包/函数允许从函数中"逃逸",如下


var completionHandlers: [() -> Void] = []

// 传入的闭包/函数并没有在函数内执行,需要在函数类型钱添加@escaping声明

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {

    completionHandlers.append(completionHandler)

}

九、枚举

swift的枚举比C语言的枚举更为强大,支持更多特性,swift的枚举更像类和结构体,支持类和结构体的一些特性,与ObjC不同,如果不声明枚举的值,编译器不会给枚举设置默认值

1. 声明和使用


// 1. 定义枚举

enum CompassPoint {

    case North

    case South

    case East

    case West

}

// 2. 可以把枚举值定义在一行,用逗号隔开

enum CompassPoint2 {

    case North, South, East, West

}

// 3. 像对象一样使用枚举,代码结构更为清晰,枚举更为简短

let direction = CompassPoint.East

// 4. 如果编译器可以识别出枚举的类型,可以省略枚举名

let direction2: CompassPoint

direction2 = .East

// 5. 如果编译器能确定case命中所有的情况,可以不需要default

switch direction {

case .East:

    print("east")

case .West:

    print("west")

case .South:

    print("south")

case .North:

    print("north")

    //所有值都被枚举,则不需要default

}

2. 嵌套枚举

swift的枚举定义支持嵌套,在使用的时候一层一层引用


enum Character {

    enum Weapon {

        case Bow

        case Sword

        case Lance

        case Dagger

    }

    enum Helmet {

        case Wooden

        case Iron

        case Diamond

    }

    case Thief

    case Warrior

    case Knight

}

let character = Character.Thief

let weapon = Character.Weapon.Bow

let helmet = Character.Helmet.Iron

3. 递归枚举

枚举的关联值的类型可以设为枚举自身,这样的枚举称为递归枚举


enum ArithmeticExpression {

    case number(Int)

    indirect case addition(ArithmeticExpression, ArithmeticExpression)

    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)

}

带递归类型的枚举需要在case前面添加关键字声明indirect,也可以在enum前面加上声明,表示所有的成员是可以递归的


indirect enum ArithmeticExpression {

    case number(Int)

    case addition(ArithmeticExpression, ArithmeticExpression)

    case multiplication(ArithmeticExpression, ArithmeticExpression)

}

其实感觉这种嵌套多层的用法可读性并不是特别好,而且在取值的时候还需要递归,通常来说,嵌套一层就够了

4. 原始值

与C语言一样,可以为每个枚举指定值,并且可以支持更多类型(Int, Float, Character, String)


// 定义枚举,并初始化原始值

enum ASCIIControlCharacter: Character {

    case Tab = "\t"

    case LineFeed = "\n"

    case CarriageReturn = "\r"

}

// 2. 通过两个属性获得原始值

var ch = ASCIIControlCharacter.Tab

ch.hashValue    // 获取是否有原始值

ch.rawValue    // 获得原始值

// 3. 通过原始值构造枚举,如果不存在,则返回nil

var tab = ASCIIControlCharacter.init(rawValue: "\t")

// 4. 如果是原始值是整形值,后面的值默认自增1,如果不指定,则默认为空,而不是从0开始

enum Planet: Int {

    case Mercury = 1, Venus        // Venus = 2

    case Neptune                    // Neptune = 3

}

// 5. 如果没有指定枚举原始值的类型,则默认为空,而不是整型

enum CompassPoint {

    case North

    case South

    case East

    case West

}

//swift 不会为North, South, East, West设置为0,1,2,3,并且CompassPoint没有原始值(rawValue)

// 6. 有原始值的枚举可以通过原始值构造(构造器返回可选类型)

let lineFeed = ASCIIControlCharacter(rawValue: "\n")

5. 关联值

swift的枚举可以给不同的枚举值绑定关联值,如下


enum Barcode {

    case UPCA(Int, Int, Int)        //条形码,关联一个元组

    case QRCode(String)            //二维码,关联一个字符串

}

var productBarcode = Barcode.UPCA(8, 85909_51226, 3)

// var productBarcode = .QRCode("http://www.baidu.com")

switch productBarcode {

case .UPCA(let a, let b, let c):        //在枚举的时候可以取得关联值

    print("barcode: \(a)\(b)\(c)")

case let .QRCode(value):

    print("qrcode: \(value)")

}

你可能感兴趣的:(Swift学习总结(第一阶段))