Swift学习总结

2019-08-04 11:07:35 浏览数 (1)

1、var 声明变量,let 声明常量,变量会根据上下文识别类型。

2、溢出操作符——& ,&-,&*,Swift的哲学是触发陷阱比潜在安全漏洞要好

3、字符串支持插值(var)占位符,支持复合赋值操作符 =连接字符串

4、swift必须手动转换类型,不会自动类型转换,譬如int8 int16会报错

5、switch语法与objc差别很大,执行一个分支马上停止,不需要break语句跳出,反而想要穿透到下面分支还要用fallthrough语句。

switch语句每一个分支不能空操作。

Switch既支持一个值与给定值比较,也支持多个值与给定值比较,还支持区间值与给定值比较,多个值用逗号分隔,区间则用三个点…来界定。

switch支持值绑定,在case语句中声明变量或常量,其值绑定为状态码的值。switch绑定值可以配合where语句使用,能额外检查一些条件,这个特性可以创建动态筛选条件。

譬如:

case let boy as Boy://Boy is class

Case let name where name.contain(“wan”)

6、元组——具有逻辑关联的两个或多个值的有限组合,支持索引访问,也可以命名访问,如:

var statusCode

var errorString

let error = (code:statusCode, description:errorString)

元组可以在switch的区间匹配中应用,下划线_可用作通配符,匹配任何值。

7、if-case 语法 为只有一个分支的switch语句提供优雅的替代品。

8、区间既可在switch语句中匹配使用,也可以在for-in循环中使用。

9、通配符_既可以在switch语句使用,也可以在for-in语句中替代迭代器,这样就可以忽略迭代器。

10、for-in循环支持where子句,如 for i in 1…100 where I%3==0 { }

Where子句是用来替代for-in中的if条件判断,使代码看起来更优雅,是可替代的。

11、类型推断——根据上下文推断出实例的类型,不需要显式声明。有些情况下需要明确声明类型,但一般来说,建议尽量利用类型推断。

12、repeat-while替代了do-while

13、导入framework方式如: import Cocoa

不需要#符号,也不需要双引号“”或者尖括号<>

14、字符串双引号前不需要@符号

15、每个字符串都是一个String对象,虽然其本身不是一个集合,但是其底层内容确实以集合的形式存在,字符串的characters属性表示组成这个字符串字符集合。

组成字符串的每个字符也是一个Character对象。

16、u{}表示Unicode标量,十六进制数放在花括号里。每个字符都有一个或多个Unicode标量构成。一个Unicode标量对应一个基本字符。

多个 Unicode标量构成的字符为组合标量,通常也有有单个标量表示形式,而且其等价。

对于两个字符或者两个字符串,如果它们具有相同的语言学含义和外观,无论是否用相同的Unicode标量创建,都认为两者相等。

标准等价意味着无论用组合标量还是预组合标量,结果都会被当做单个字符。

17、swift不允许直接用索引下标[]来访问字符串中的指定字符,因为swift无法在不遍历前面每个字符的情况下知道指定的索引对应于哪个Unicode标量,该索引必须要先获取到start索引,再通过编译计算到指定索引。

18、swift任何类型都可以用可空类型来说明一个实例可能是nil,而objc-c只允许对象是nil

19、声明实例变量时,在类型后跟随问号?,则表示该变量是可空类型,可以为nil,也可以赋了值。

20、实例变量后跟随感叹号!,其作用是强制展开,无论实例变量是否有值,都会访问封装的值。感叹号!假设有这样一个值,如果没有,这样展开会产生运行时错误。

string实例变量和string!实例变量不是相同的类型——如果你有一个String变量,就无法在不展开可空实例的情况下将String?的值赋予给这个变量。

21、可空实例绑定——如果有值,就将其值赋予一个临时常量或变量,并且使这个常量或变量在条件语句第一个分支代码中可用。其语法与在条件语句中创建常量或变量一致,只是不需要再强制展开可空实例了,这个操作自动完成。

If let Instance = nilInstance, let Instance = nilInstance {}

22、声明实例变量时,在类型后跟随感叹号!,则表示该变量是隐藏式展开可空类型。隐式展开可空类型与普通可空类型重要区别就是不需要展开,使用上不需要if条件语句了。如果这种隐式展开可空类型没有值的话,访问其值会直接导致运行时错误。建议如果某个实例可能为nil,就不要使用隐式展开可空类型。

23、实例后面跟问号?——可空链式调用,与可空实例绑定类似,两者重要区别是,可空链式调用允许把多个查询串联为一个可空实例的值。

24 、感叹号跟在实例后面,其作用就是断言实例不为空,若真为空就崩溃,相当断言效果。

25、nil合并运算符??类似三目操作符?:

26、guard语句和if语句有点类似,都是根据其关键字之后的表达式的布尔值决定下一步执行什么。但与if语句不同的是,guard语句只会有一个代码块,不像if语句可以if else多个代码块。

那么guard语句的作用到底是什么呢?顾名思义,就是守护。guard语句判断其后的表达式布尔值为false时,才会执行之后代码块里的代码,如果为true,则跳过整个guard语句。guard语句中声明的实例可以在整个方法作用域中使用。

其语法:

guard 布尔表达式 case{ 执行代码 }

——————————————————————————————————————————————————————————

1、swift的Array类型可以持有任何类型的值——对象和非对象都可以。

2、声明数组主要有两种语法,数组实例也支持类型推断

var bucketList:[String]

var friends:Array<String>

var weeks = ["Monday","Tuesday"] ——字面量语法

var array = Array<String>()

var array = [Int]()

集合Set语法与数组一致

3、声明字典语法,支持类型推断

var dict1:Dictionary<String,Double> = [:]

var dict2 = Dictionary<String,Double>()

var dict3:[String:Double] = [:]

var dict4 = [String:Double]()

var dict5 = [“name”:”shelton”,”age”:]

Swift与obj-c的字典区别,就是简化赋值objc用大括号{},swift用中括号[]

4、集合Set是无序,储存元素唯一,数组有序,储存元素可以重复,字典无序,但键值唯一。

5、Swift函数支持嵌套,用元组语法来支持返回多个值,支持变长参数,支持参数带默认值。函数还允许有外部名与内部名。元组也可以作为函数参数

6、闭包就是objc中的Block

闭包语法:

{(参数)->返回类型 in

//代码

}

闭包可以利用swift的类型推断系统去除参数和返回值的类型信息来简化闭包。

如果闭包代码只有一行表达式,return关键字也可以省略。

闭包甚至可以利用快捷参数名来替代显式声明的参数,做到剔除参数声明来简化闭包。$0表示第一个参数,$1表示第二个参数。

尾部闭包语法甚至可以省略掉方法的圆括号。

闭包和函数能记录在其闭合作用域中定义的变量所封装的内部信息。

闭包是引用类型。

func greeting(_ greeting:String) -> (String)->String {

return { (name:String) -> String in

return "(greeting) (name)"

}

}

上面函数,有一个String参数,结果返回一个函数,不需要通过嵌入函数来实现,用闭包可以简化之。

7、枚举Enum语法上多了case关键字,支持一系列类型,包括所有的内建数值类型和字符串。在swift中,方法可以和枚举关联,即在枚举中添加方法。

在swift中,枚举是值类型,而值类型的方法不能对self进行修改,如果希望值类型方法能修改self,需要标记这个方法为mutating。

Swift的枚举,除了像其它语言那样定义一些静态的成员值来枚举可能的状态,还提供了一种强大的特性,可以定义带关联值的成员。关联值能让你把数据附在枚举实例上,不同的成员可以有不同类型的关联值。

在枚举中,如果带有递归关联值成员,需要关键字indirect,否则报错。

枚举语法:

enumname.enumvalue. 或者 .enumvalue

8、创建项目中,入口文件main.swift没有main函数

在main.swift 创建其它文件的结构或类,不需要import对应文件

9、结构体Struct也是可以声明自己的方法,但是若方法要修改结构体的属性,需要用关键字mutating标志,因为结构体和枚举都是值类型。

10、类与结构体的区别之一,支持继承,在重载父类方法的时候要添加override关键字

Swift的类有个新特性,可以禁止子类重载某个函数,在该函数增加final关键字。如果我们在声明一个类时,在class前加上final修饰符,则表示禁止类被继承。

11、结构体和类都支持类方法(c 的静态方法),但是结构体需要在方法前用static标记,而类则用class标记该方法。用class标记的类方法可以由子类重载,想要子类不能重载,则需要用static标记。也就是类和结构体都支持static关键字标记类方法,但该方法不支持重载。当然也可以用final class来标记类方法。

12、属性分存储属性和计算属性。

存储属性用来存储数据,可以是只读属性,也可以是读写属性,只读属性用let声明。

swift增加一个惰性存储属性概念,用lazy关键字修饰,惰性储存属性的值只在第一次访问的时候才会出现,因为它依赖类型的外部因素,必须要满足某些前提条件才能计算得到。注意,标记为lazy的属性只会被计算一次。

只读的计算属性用var来定义,与只读得存储属性不同。计算属性的类型必须显式声明

针对objc的kvo,swift提供一种特性——属性观察,但计算属性不能用。

13、与类方法类似,有类属性,用class标记的属性子类可以重载自定义,用static标记的子类不可以重载修改。枚举,结构体和类都支持类属性。

类计算属性与类方法很类似,区别是一个用关键字var,另一个用func,类计算属性没有圆括号。

计算属性如果没有写入方法,可以省略定义中的get。

子类要重载类计算属性,同样要标记override。

14、swift提供了 open、public、internal、fileprivate、private五层访问控制。objc没有这些访问限制。

private:访问级别所修饰的属性或者方法只能在当前类里访问.(注意:Swift4 中,extension 里也可以访问 private 的属性。)

fileprivate:访问级别所修饰的属性或者方法在当前的 Swift 源文件里可以访问。

internal 访问级别所修饰的属性或方法在源代码所在的整个模块都可以访问。如果是框架或者库代码,则在整个框架内部都可以访问,框架由外部代码所引用时,则不可以访问。如果是 App 代码,也是在整个 App 代码,也是在整个 App 内部可以访问。

public: 可以被任何人访问。但其他 module 中不可以被 override 和继承,而在 module 内可以被 override 和继承。

open: 可以被任何人使用,包括 override 和继承。

15、swift中的初始化方法意义和c 的构造函数类似。与普通方法类似,但是没有func关键字标记,也没有返回值,用init关键字做方法名,参数个数可变。

结构体提供了一种默认的初始化方法,会将所有属性都作为参数。与结构体不同,类没有默认的成员初始化方法。

在方法实现中,属性如果遇到同名参数才需要指定self,不然则不需要。

在初始化方法中允许给常量赋值,其它方法则不允许。

16、初始化增加了两个概念:指定初始化和便捷初始化,便捷初始化需要在init前加上关键字convenience。还有一个required关键字不能忽略,表示子类必须重载该指定初始化方法,只是不用override关键字,而是用required关键字。便捷初始化参数可以灵活不需包含所有属性,且内部必须要调用到一个指定初始化方法。

在创建Swift对象,可以类似:var obj = classname.init(), 也可以类似:var obj = classname(),括号里的参数与init实现一致。

17、反初始化就是c 中的析构函数思想,也相当于objc的dealloc,在实例被清除出内存钱会调用到。

18、初始化还有个概念可失败初始化,其实就是可空实例关联的东西,其语法需要再init后跟随一个问号,也可以跟随一个感叹号。感叹号表示返回隐式展开可空类型实例,其使用起来方便(不需要写可空类型展开的语法),但不如跟随问号安全。

19、在被赋给另一个实例或是作为参数传递给函数时,值类型总是被复制。

String,Int, Array, Dictionary等是Struct,是值类型。

Class是引用类型。

声明为常量的值类型不能改变属性,即使属性在类型实现中是用var声明也一样。但是声明为常量的引用类型实例的属性却可以被修改。

建议不要在值类型内使用引用类型,确实需要再结构体内使用引用类型,最好使用不可变的实例。

我们预期,当把值类型实例赋给新变量、常量或传递给函数时,实例会被复制。但是,若值类型内有引用类型,副本实例和原来实例中引用类型实例还是指向同一个实例,改变这个引用实例,副本实例中的引用实例也会被影响。

值类型内的引用类型属性在复制时只是浅拷贝,并不会创建新的实例,而是指向同一个实例。

20、理解相等与同一的概念,相等通常是指俩实例的内容相等,同一是指俩实例指向内存中同一个实例。基本数据类型String、Int、Double、Float、Array、Dictionary都可以检查相等性。新增语法===来检测俩实例的同一性,但值类型不能用同一性检查。类实例若要判断相等性==,则需要实现协议Equatable。

21、关于值类型的复制行为,swift语言提供了一种写时复制的特性。在某个实例需要修改或写入存储时,该实例才会产生一份自己的副本,在这之前,它们会维护自己对同一份存储的引用。

因此我们在赋值或将实例作为参数传递时,在它们没被修改或写入存储时,它们还是对同一份存储进行引用。这样值类型就可以避免创建多余的数据副本。

——————————————————————————————————————————————————————————

1、协议关键字protocol

与objc的协议区别,多了计算属性支持,不过计算属性也可以理解成方法。

某个类型符合协议,或者说遵循某个协议,其语法与继承一样,都是类型名称后 冒号 协议名称。

当我们添加协议后,会有错误警告,通过Fix修复错误会自动把协议需要实现的方法自动列出来,不需要我们一一写。

所有类型都支持协议,枚举,结构,类都可以符合协议

与objc一样,可以符合多个协议

与objc不一样的地方是,swift的协议支持继承,还可有多继承。

如果协议中声明的方法在值类型实现中需要修改self,注意要方法前标记mutating。

这里的多继承协议,有点类似c 的抽象类了

你可以在协议的继承列表中,通过添加 class 关键字来限制协议只能被类类型遵循,而结构体或枚举不能遵循该协议。class 关键字必须第一个出现在协议的继承列表中,在其他继承的协议之前 。

2、assert与precondition的区别是,assert在release版本被编译器优化删除,而precondition在debug版本和release版本效果一致

3、Swift在可能抛出异常的函数声明中加上关键字throws,该关键字在函数参数与->之间。

遇到可能会抛出异常的方法,必须要用do catch语法来捕获异常。

do {

try//用try关键字标记存在异常的方法

} catch {

//在这里我们可以

}

Swift允许二次抛出异常,在调用可能存在异常的方法时,用try标记,但是可以不用do catch,而是让当前方法声明上标记throws达到可以支持二次抛出异常。

总结如下:必须用try标记每次调用可能抛出异常的函数,而任何try标记的调用必须要么在do catch语句块内,要么在一个本身被标记为throws的函数内。

不过也有个例外,可以在try后面跟随感叹号。与强制展开可空实例一样,一旦出现错误程序就会崩溃。该用法谨慎使用,少用。

还有第三种情况,可以用try? 调用一个可能抛出异常的函数,得到函数原本的返回值对应的可空类型返回值。发生错误的时候忽略错误而不触发陷阱。常用于有后备函数可用,且配合guard关键字使用。

4、swift对应objc中的category的语法关键字是extension,枚举、结构体、类都支持extension。其用法基本跟objc的category类似,都不支持存储属性,但是支持计算属性,支持protocol,支持增加方法,支持添加嵌套类型,支持新初始化方法。

5、swift把c 中的模板特性引入,称为泛型。其语法如下

type TypeName <Element> { //type可以是struct,也可以是class或者enum等

}

用法:

var a = TypeName <Element>()

枚举、结构体、类和函数都支持泛型。

在实现泛型的时候,对于一些无法预知的类型,其某些功能不知道是否支持的情况下,通过增加泛型约束来实现。有两种类型约束:一种是类型必须是给定类的子类,还有一种是类型必须符合一个协议或者协议组合,具体语法:尖括号里的类型 冒号 协议。

协议是不可以直接支持泛型的,不过协议支持一个相关特性:关联类型,参考如下:

protocol IteratorProtocol {

associatedtype Element

mutating func next() -> Element?

}

protocol Sequence {

associatedtype Iterator: IteratorProtocol

func makeIterator() -> Iterator

}

在实现泛型的时候,有可能一个泛型约束不够,还需要where子句来进行更多的约束。代码参考:

mutating func pushAll <S:Sequence> (_ seq:S) where S.Element==Element {

for item in seq {

self.push(item)

}

}

关于多态的特性,用继承,或协议,或泛型都可以支持。

6、swift新特性,除了枚举、结构、类支持extension扩展,协议也可以进行扩展,可以添加有实现的计算属性和方法,只是不能添加存储属性。

之前提过协议是支持继承,且多继承,在实践过程中,我们给一个协议A补充从另外一个协议B继承,这个时候我们发现我们已经有多个实现是从符合A协议的,正常情况,需要我们在每个实现中补充B协议的实现。但是有了协议扩展extension的特性,我们不需要这么做,仅仅通过extension A来补充B的方法实现则可。当然,如果我们硬是要在单个实现中补充B协议的实现是可以的,且其优先级会高于协议的扩展,会覆盖协议的扩展方法。

有个地方要小心了,如果我们单纯扩展协议增加一个计算属性或方法,又在具体类型实现了同名的计算属性和方法,这些方法和计算属性因为不是协议必须的,所以会有预期不到的结果,编译器并不会检测实际的类型,而是遵循我们我们提供的类型信息。

7、swift内存管理采用ARC(自动引用计数)

默认情况下创建的类实例是强引用,可用关键字week声明为弱引用。弱引用必须是var声明,且可空。

在实际应用中,注意避免循环引用。在使用闭包的时候也要注意循环引用。闭包增加分逃逸闭包和非逃逸闭包两个概念,通常闭包作为参数传给函数时,是非逃逸闭包,这种闭包编译器可以确定不会造成循环引用,而作为类型的属性这种闭包是逃逸闭包,这种闭包就容易引起循环引用。在使用的时候要使用捕获列表来避免循环引用。捕获列表语法如下:[weak self]:

注意,混合使用逃逸闭包和非逃逸闭包是会编译错误的,如果要避免错误,可以用

@escaping属性来修复这个错误。

Swift是不可以访问实例的引用计数,但是有个函数(isKnownUniquelyReferenced)可以判断一个实例是否是对某个实例的唯一引用。

————————————————————————————————————————————————————————————————————

1、Object C中 @selector语法换成#selector

2、Swift中类型转换用关键字as,as?, as!三种语法

3、ObjC中的 #pragma mark 用 #MARK替代

4、ObjeC中有id表示任意对象,Swift中AnyObject 可以代表任何 class 类型的实例,Any 可以表示任意类型,甚至包括方法(func)类型

5、.self 用在类型后面取得类型本身,用在实例后面取得实例本身

其中第2个用法,获取实例本身一般直接通过init方法创建即可,所以这种用法不推荐使用。

此外,这里的 .self 和协议、类方法中的 Self 完全不同。注意区分

6、通过 AnyObject.Type 这种方式得到的就是一个元类型,也就是 AnyClass。

0 人点赞