×

Swift 4.0 基础学习总结(一)

96
xx_cc
2017.11.20 22:21* 字数 3708

Swift简介

作者:Chris Lattner
Swift 2010年7月开始设计,1年时间完成基本架构,经历4年开发期于WWDC 2014 苹果2014年开发者大会发布,用于撰写OS X和iOS应用程序。

特点:
语法简单,代码简洁,使用方便。
可与OC混合使用。
提供了类似JAVA的命名空间,泛型,运算符重载。
提供了很多方便的工具方法,例如元组、guard、可变参数等等

Swift从2014年发布开始,到现在已经更新到4.0版本,Swift替代OC的路走的比我想象的快很多,我认为Swift在日渐趋于稳定的状态下,代码风格不会再有太大的变化,并且Xcode对Swift语法错误的检测纠错敏感度很高,帮助我们很快找到语法错误并修改。Swift的语法简单灵活给我们带来了很多方便,只要是我们认为可以省略的代码基本上都可以省略,但是这对新手可能不是太友好,看起来差异很大的代码,其实是省略掉一部分内容转化而来的。这就需要很熟悉Swift的语法,并且对那些代码可以省略有清晰的认识。

swift代码

常量和变量

Swift中定义标识符必须明确指明该标识符是常量还是变量。常量和变量将名字(标识符)和一个特定类型的值关联起来。

使用let定义变量,表示该值不可再被更改
使用var定义变量 ,表示将来可以被修改为不同的值

语法格式:

// let / var 标识符名称 : 标识符类型 = 赋值
let score : Double = 89.5
var age : Int = 20
// Swift中如果一行只有一条语句的时候,那么语句结束时的;可以省略

注意:

  1. 在开发中,我们一般优先使用常量,只有发现标识符需要修改时,在使用变量,这样做防止我们在不希望它值修改的情况下,在其他地方被修改。
  2. 常量的本质:标识符指向的内存地址不可以被修改,但是可以通过内存地址找到对应的对象,修改对象内部的属性。
  3. 省略:上面例子中:Double 和 :Int是可以省略的,事实上,我们也不需要经常使用类型标注,如果在定义一个变量或常量的时候就初始化一个值,那么Swift就可以推断出这个变量或常量的类型, 可以通过option + 鼠标左键来查看标识符类型。
  4. 标识符名称,也就是变量和常量的名字不能包含空白字符、数学符号、箭头、保留的或者无效的Unicode码位、连线和制表符,也不能以数字开头。

输出变量和常量

Swift中使用print()打印变量和常量中的值

print("hello swift")
// 当需要输出变量或常量的时候,可以直接使用字符串插值的方式把变量名或常量名当做占位符加入到字符串当中,Swift会用常量或变量的当前值来替换这些占位符
// 通过将常量或变量放入()中并在前面加上\将其转义
let version : Double = 4.0
print("hello swift \(version)")

类型安全和类型推到

Swift是一门类型安全的语言,类型安全的语言可以让我们清楚的知道值得类型,帮助我们规避很多错误。Swift会在编译代码的时候进行类型检查,如果我们错误的将不匹配的类型进行赋值,编译器就会标记为错误。
同时,如果没有为值进行类型声明,Swift会通过检查我们给变量赋的值并在编译阶段就自动推断出值得合适的类型,这个过程叫做类型推导。
我们可以通过option + 鼠标左键来查看标识符类型。

let score  = 89.5
var age  = 20
// 编译器会在赋值时自动根据其值推断出 score为Double类型,age为Int类型
let sum = 55.5 + 30
// 甚至编译器会推断出算式的值得类型为Double

基本运算

Swift中相同类型的之间才可以进行运算,Swift中没有类似OC中的隐式类型转换,所以当两个变量类型不同时,需要进行强制类型转换。前面提到过Swift是一门强类型语言,即使Int8类型和Int类型也不能直接进行运算,同样需要强制转换类型才可以。

强制类型转换 Int(标识符a) 、Double(标识符b)

let n = 10
let x : Int8 = 5
let m = 10.5

let result = Double(n) + m // Int转化为Double
let result1 = Int(m) + n // Double转化为Int
let retule2 = Int(x) + n // Int8 与 Int进行运算同样需要转化

除了不同类型之间运算需要进行强制类型转换外,Swift中其他基本运算符与OC中一样,这里不再赘述,同时Swift中还添加了一些非常方便的运算符

闭区间运算符 (a...b),定义了从a到b的一组范围,并且包含a和b。要求 a <= b
半开区间运算符 (a..<b),定义了从a到b但不包括b的区间,要求a<=b,当a=b时那么返回的区间为空
单侧区间运算符[2...],一般用于数组中,例如list[2...]即表示从list数组索引2开始一直到结束。注意这时2必须小于数组的count,否则编译器将会报错
当然也可以写成[...2]表示从0开始到2的索引,[..<2]表示从0开始到1的索引

Swift作为强制类型语言还添加了 !强制解包运算符和??合并空值运算符这些在之后讲到可选类型的时候再做详细解释

逻辑分支

Swift提供了多种多样的控制流语句,使我们在日常使用中可以根据不同的情况进行使用,除了 if while之外Swift提供了guard语句来应对需要对多个条件进行判断的情况,使代码逻辑显得更加清晰

if 条件语句
Swift中 if 后面的条括号可以省略掉,同时需要注意的是,Swift中没有像OC中非0(nil)即真的判断方式,也就是说在Swift中我们必须给出明确的判断条件

let score = 88

if score > 0 {
    print("score > 0")
}
if score != 0 {
    print("score != 0")
}
// 这里不能像OC中 if (a) {} 即可判断a是否为0,Swift中必须给出明确的判断条件

if条件判断语句同OC中一样,只不过if后面括号去掉也许会让我们稍有些不习惯,不过很快就会熟悉

// if else
if score < 0 || score > 100 {
    print("不合理分数")
}else if score < 60 {
    print("不及格")
}else if score >= 60{
    print("及格")
}

guard ,guard的使用与if非常类似,可以同if进行无缝转化,为了提高代码的可读性,我们可以把guard看做一个看门人,不符合条件的统统不许通过,最后通过的也就是我们想要的结果

let idCard = true
let ticket = true

func Ontrain (idCard : Bool , ticket : Bool) {
    // 判断有没有带身份证
    guard idCard else {
        print("没有带身份证,不可以乘车")
        return
    }
    // 判断有没有车票
    guard ticket else {
        print("没有车票,不可以乘车")
        return
    }
    
    print("身份证和车票都带了,可以乘车")
    // 满足以上所有条件才可以 使用场景:满足所有条件才可以达成某些功能
}

Switch , Swift对Switch进行了进行了大大的增强,使其拥有其他语言中没有的特性

  1. switch后面的()可以省略
  2. case语句结束,可以不加break,系统默认帮我们加上了break,如果希望case结束时产生穿透, 去除break效果需要加上 fallthrough。
  3. 在每条case中必须包含至少一条可执行语句,不能为空。
  4. case语句后面可以判断多个条件,每个条件之间用逗号隔开,如果任何一个条件匹配了,则会执行case下的语句
  5. Switch可以判断多种类型,包括 浮点型,字符串类型,区间类型,元组
  6. Switch可以将匹配到的值临时绑定为一个变量或者一个常量,来给case中的执行语句使用
  7. 同时可以在case语句后面添加额外的where判断
  8. Switch可以被打上标签循环使用

Switch基本使用

let sex = 0
switch sex {
case 0:
    print("M")
    break // 可省略
case 1:
    print("F")
    fallthrough // 如果想要产生穿透
default:
    print("?")
}

Switch语句后面判断多个条件

switch sex {
case 0,1: // 任何一个条件匹配了,就会执行case下的语句
    print("正常")
default:
    print("???")
}

既然Switch语句可以判断多个条件,那么一定可以在case后面进行区间匹配

let score = 88
switch score {
case 0..<60:
    print("不及格")
case 60..<80:
    print("及格")
case 80...100:
    print("优秀")
default:
    print("不合理分数")
}

Switch中也可以判断多种类型,字符串类型,元组,浮点型

// 这里用元组举例
let point = (1,1) // 类型为(Int,Int)的元组
switch point {
case (0, 0):
    print("(0, 0) is at the origin")
case (_, 0): // 如果不想匹配元组中的某一个值,可以用_代替
    print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
    print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2): // 元组中使用区间匹配
    print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
    print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
// prints "(1, 1) is inside the box"

Switch可以将匹配到的值临时绑定为一个变量或者一个常量,来给case中的执行语句使用

let personInfo = (name : "xx_cc" , age : 20)

switch personInfo {
case (let x , 1..<18):
    print("未成年的人有\(x)")
case (let x , 18..<100):
    print("成年的人有\(x)")
default:
    print("没有匹配的人")
}
// print 成年的人有xx_cc

Switch在case语句后面添加额外的where判断

let personInfo = (name : "xx_cc" , age : 20)

switch personInfo {
case (let x , 1..<18) where x == "xx_cc": // 只是举例,并没有意义
    print("未成年的人有\(x)")
case (let x , 18..<40) where x == "xx_cl":  // 只是举例,并没有意义
    print("成年的人有\(x)")
default:
    print("没有匹配的人")
}
// print "没有匹配的人\n"

给语句打标签

var times = 0
timeLoop : while times < 10 {
    times += 1
    switch times {
    case 6:
        break timeLoop
    case 4:
        continue timeLoop
    default:
        print("default")
    }
}
// print :  default default default default
// 这里我们给while循环打上timeLoop标签,可以发现,times等于1...3时打印了三遍defult,当times等于4时,continue结束了当前while循环并开始下一次循环,times等于5然后打印第四遍default,times等于6时bread timeLoop结束了while循环不在进行打印。

循环语句

Swift中循环语句与OC中相同,for ,while ,repeat while循环。使用方法也与OC中基本相同

for i in 0..<10 {
    print(i)
}

// 在 Swift中如果一个变量/常量暂时不会使用,那么可以使用_来代替,_不会占用内存空间
for _ in 0...10 {
    print("hello Swift")
}

repeat while也就是OC中的do while

var m = 0

while m < 10 {
    m += 1
    print(m)
}

// repeat 无论是否成立都要先走一次
repeat {
   m -= 1
    print(m)
} while m > 0

集合

Swift 提供了三种主要的集合类型,所谓的数组、合集还有字典,用来储存值的集合。数组是有序的值的集合。合集是唯一值的无序集合。字典是无序的键值对集合。
Swift中的集合都是泛型集合,也就是说集合需要明确存储的值得类型,保证我们不会意外的插入一个错误类型的值到集合中去,同时保证我们可以从集合中取回确定类型的值。


Swift中的集合

数组

数组以有序的方式来储存相同类型的值。相同类型的值可以在数组的不同地方多次出现。
Swift中的数组是Array类型的,不想OC中区分NSArray与NAMutableArray,而是通过我们创建方式的不同来定义数组

不可变数组 :let修饰
可变数组 :var修饰

定义数组语法

// Array类型 <>里表示数组中存放的数据类型
let array : Array<String> = ["a","b","c"] // 不可变数组不可操作
// 数组类型简写 推荐使用简写写法,更加简单清晰
let array : [String] = ["a","b","c"]
// 创建空数组
var arrayM2 = Array<String>()
// 同样可以进行简写
var arrayM : [String] =  Array()
var arrayM1 = [String]()

/* 
* 同时数组还为我们提供了初始化方法创建
* repeating:表示数组存储对应类型的默认值
* count:表示数组内元素的个数
*/
var arr = Array(repeating: "a", count: 5)

对可变数组的基本操作

// 获取数组元素个数
let count = arrayM.count
// 同时也可以使用isEmpty Bool值属性 来对数组是否为空进行快速判断
if arrayM.isEmpty {
}

// 1. 添加元素
arrayM.append("xx_cc")
// 也可以使用 +=运算符在数组末尾添加新的同类型元素
arrayM += ["xx","cc"]
// 使用 insert方法添加元素到指定位置
arrayM.insert("js", at: 1)

// 删除元素
arrayM.remove(at: 0) // 返回被删除的元素
arrayM.removeAll() // 删除所有的
arrayM.removeLast() // 删除最后一个
arrayM.removeFirst() // 删除第一个

// 提取元素
let name = arrayM[0]

// 修改元素
arrayM[0] = "xx_cc"
// 也可以使用区间类型修改多个元素
arrayM[2...3] = ["haha","xixi"]

需要注意的是,同OC一样,当我们访问或者修改一个超出数组元素个数的值将会引发崩溃。

数组的遍历
同OC一样,我们可以使用 for in 来遍历整个数组中的值

for item in arrayM {
    print(item)
}
// 如果我们想要拿到数组中每个元组的值以及它的索引,我们可以使用enumerated()方法来返回数组中每一个元素的元组,元组中包含了元素的索引和值
for (index , item) in arrayM.enumerated(){
    print(index)
    print(item)
}

集合

Swift中集合的类型是Set,同OC中NSSet一样,集合是将同一类型且不重复的值无序地储存在一个集合当中。
定义集合语法

// 使用 Set<Element>创建并初始化一个集合
var letters = Set<Int>()
// 注意Set没有同数组一样对应的简写方式
// 也可以通过数组来创建集合
var name: Set<String> = ["cc", "xx", "xx_cc"]

集合的访问和修改

// 通过count来访问集合内元素的个数
print(name.count)
// 也可以通过 isEmpty快速判断count属性是否为0
if name.isEmpty {
    print("As far as music goes, I'm not picky.")
}

// 通过insert为集合新增加一个元素
name.insert("enen")

// 删除集合中的元素
if let nameInfo = name.remove("cc") {
    print("\(nameInfo)")
}
// 如果集合中有cc这个元素那么就删除它,并且返回被删除的元素,如果没有就返回nil
// 我们这里使用 if let 来对返回的值进行判断,如果集合中存在被删除的参数,那么返回值不为nil,然后就会执行大括号里面的内容,如果集合中不存在该参数,那么就会返回nil,大括号中的内容就不会执行

// 使用contains()方法检查集合中是否包含了特定的元素
if name.contains("xx") {
    print("xx is in set")
}

集合的遍历

for nameStr in name {
    print("\(nameStr)")
}

// 可以通过使用 sorted方法将合集的元素进行排序
for nameStr in name..sorted() {
    print("\(nameStr)")
}

集合基本操作
Swift提供了很多便捷的方法让我们对集合进行基本操作来获取两个集合相交的集合或者其他等等。

  1. 使用 intersection(_:) 方法来创建一个只包含两个集合共有值的新合集。
  2. 使用 symmetricDifference(_:) 方法来创建一个只包含两个集合各自有的非共有值的新集合。
  3. 使用 union(_:)方法来创建一个包含两个集合所有值的新集合;
  4. 使用 subtracting(_:)方法来创建一个两个集合当中不包含某个集合值的新合集。
    集合基本操作图例
  5. 使用“相等”运算符 ( == )来判断两个集合是否包含有相同的值;
  6. 使用 isSubset(of:) 方法来确定一个集合的所有值是被某合集包含;
  7. 使用 isSuperset(of:)方法来确定一个集合是否包含某个合集的所有值;
  8. 使用 isStrictSubset(of:) 或者 isStrictSuperset(of:)方法来确定是个集合是否为某一个集合的子集或者超集,但并不相等;
  9. 使用 isDisjoint(with:)方法来判断两个集合是否拥有完全不同的值。

字典

Swift中字典的类型是 Dictionary,同样是泛型集合,Int Double类型均为结构体,都可以放入数组和字典中。
字典的定义

let 定义不可变字典
var 定义可变字典

// 字典也用[]表示,编译器会自动区分[]中是一个个元素(数组)还是键值对(字典)
let dic : Dictionary<String , Any> = ["string" : "xx_cc", "age" : 18 , "height" : 1.88]
// 与数组一样,同样可以对字典进行简写
let dic2 : [String : Any] = ["string" : "xx_cc", "age" : 18 , "height" : 1.88] // 推荐 这种写法更简便一些

//定义可变字典 var 修饰
var dicM = Dictionary <String ,Any> ()
var dicM1 = [String : Any]()

// 与数组一样,如果你用一致类型的字典字面量初始化字典,就不需要写出字典的类型了
var dicName = ["xx_cc":"xx","xx_cl":"cl"]

对可变字典的基本操作

// 同数组一样字典也拥有count属性和isEmpty属性来得到字典的元素个数和快速判断字典元素个数是否为0

给字典添加元素
dicM["name"] = "xx_cc"
dicM["age"] = 18
dicM["height"] = 1.88

// 删除元素
dicM.removeValue(forKey: "name") // 如果删除成功则返回被删除的值,如果字典没有这个键值对则返回nil
dicM.removeAll() 
// 我们也可以使用下标脚本语法给一个键赋值 nil来从字典当中移除一个键值对
dicM["name"] = nil

// 修改元素 字典会自动索引字典中没有相同的key,如果没有就添加,如果有就修改其值
dicM["name"] = "xx_ccha"
// updateValue的功能同上,同样会增加或修改元素的值
// 如果updateValue是更新已经存在键的值,则会返回原来旧值的可选类型
// 如果updateValue为字典新增加了一个元素,则返回nil
dicM.updateValue("xx_ccha", forKey: "name")

遍历字典

// 遍历字典中所有的key
for key in dic.keys {
    print(key)
}
// 遍历字典中所有的value
for value in dic.values {
    print(value)
}
// 遍历字典中所有的key - value
for (key,value) in dic{
    print(key, value)
}
// 拿到所有key 或value组成的数组
let keyArr = [String](dict.keys)
let valueArr = [String](dict.values)

// 我们可以通过遍历其中一个字典,为第二个字典赋值,来合并两个字典
let dicStr : [String : Any] = ["name" : "cl" , "age" : 18]
var dicStr2 : [String : Any] = ["name":"xx","height" : 1.88 , "phone" : "110"]
for (key , value) in dicStr {
    dicStr2[key] = value
}

同样,Swift 的 Dictionary类型是无序的。要以特定的顺序遍历字典的键或值,则需要使用sorted() 方法。

元组

元组是Swift中新添加的可以将多个值合并成单一的复合型的值,并且元组内的值可以是任何类型,且不必是同一类型。
任何类型的排列都可以被用来创建一个元组,他可以包含任意多的类型。元组非常方便的帮助我们解决了数组和字典的缺陷。在Swift中经常使用。

元组的创建

// 元组创建写法一
let infoTuple = ("xx_cc",18,1.88)
// 可以通过从零开始的索引访问元组中的单独元素
let tupleName = infoTuple.0

// 写法二 为元组中单个元素命名
let infoTuple1 = (name:"xx_cc", age:18, height:1.88)
let nameLength = infoTuple1.name.characters.count
// 如果只想取出元组中部分数据,不想要取出的可以用_代替
let (tupleName2, age2, _) = infoTuple1
print("我叫\(tupleName2)今年\(age2)")

// 写法三 一一对应
let (name,age,height) = ("cl",18,1.88)
print(name)

⭐️⭐️⭐️可选类型

Swift中只有可选类型才能被赋值为nil,其他类型都不能赋值为nil

OC中当一个值不在使用时,我们可以将它赋值为0(基本数据类型)或者赋值为nil(对象类型),但是在Swift中,nil是一个特殊的类型,同String,Int相同。而Swift是一门强类型语言,类型不匹配无法赋值。所以只有可选类型才能被赋值为nil。被可选类型修饰的值则表示该值有可能为nil。

可选类型的定义

// 首先我们尝试给String变量赋值为nil编译器报错
// Nil cannot initialize specified type 'String'
// var name : String = nil

// 定义可选类型 Optional表示可选类型 泛型集合
var name : Optional<String> = nil
// 简写 String?表示可选类型的string 
var name1 : String? = nil
// 问号明确了它储存的值是一个可选项,意思就是说它可能包含某些 String  值,或者为nil。

// 给可选类型进行赋值
name1 = Optional("xx_cc") // 方式一
name1 = "xx_cc" // 方式二 编译器会自动加上 Optional()

我们通过option查看name为String?可选类型
可选类型String?

同时当我们在类中创建暂时不需要使用的变量的时候,定义为可选类型并且不提供默认值,变量会被自动设置为nil。

对可选类型进行取值

print(name1)
// 取出可选类型中的值 可选类型! --> 强制解包
print(name1!)
// print  Optional("xx_cc")
// print  xx_cc

可以发现直接打印name1输出 Optional("xx_cc")可选类型,并不是我们想要拿到的字符串,使用!对可选类型进行强制解包,可以拿到可选类型中的值。但是我们只有非常确定可选类型有值才可以使用强制解包,一旦可选类型为nil,强制解包程序就会立即崩溃,这在开发中是非常危险的

所以我们可以在使用的时候优先对可选类型进行判断

// 判断可选类型不为nil,然后才强制解包
if name1 != nil {
    print(name1!)
}

但是我们在开发中会大量的使用可选类型,如果每一个都需要进行判断就会非常麻烦,所以Swift推出可选绑定语法,该语法用于可选类型,使我们使用可选类型更加方便。

可选绑定语法

if let tempName = name1 {
    print(tempName)
}

可选绑定会优先判断name1是否有值,如果没有值,则直接不执行{}中的内容,如果name有值,那么系统会自动对可选类型进行解包,并且将解包后的结果赋值给前面的tempName。

同时为了方便,并且避免同一个变量有多个名字,我们可以使用相同的名字进行命名,在大括号内直接使用相同的变量名即可。

if let name1 = name1{
    print(name1)
}

类型转化

我们通过as将实例转化为一种类型

将String转化为NSString
let str = "askkdsfjksdfffff" as NSString
str.substring(to: 6)

通过 as? 将实例转化为可选类型

let dict : [String : Any] = ["name":"xx_cc" , "age" :18]
let tempName = dict["name"]
print(tempName!)
// 通过as?转成可选类型 这里将 any 类型 转成 string类型
// as? 转成的类型是一个可选类型,系统会自动判断tempName是否可以转成String类型,如果可以转成,那么获取字符串,如果转化不成功,则返回nil
let name = tempName as? String
if let name = name {
    print(name)
}
if let name = dict["name"] as? String {
    print(name)
}

通过 as! 转成具体类型

// 注意:如果转化不成功则程序会崩溃
// 建议:如果确定转化成功再用as! 进行转化
let tempName1 =  dict["name"]
let name1 = tempName1 as! String

使用as?转化的name为String?类型,使用as!转化的name1为String类型
可选类型不仅增加了代码的可读性,并且使Swift代码更加严谨和安全。

参考:Swift编程语言


文中如果有不对的地方欢迎指出。我是xx_cc,一只长大很久但还没有二够的家伙。

Swift 学习总结
Web note ad 1