1、面向协议
从Swift基础库的角度来说,定义的协议数量远比定义的类多很多
Swift协议可扩展,并通过扩展提供默认实现,这也是Swift面向协议的核心
Swift协议支持泛型
Swift协议使用范围更广,并非像OC中只有类才能遵循协议,Swift中结构体(Int、String、Array等也是结构体)、枚举、类都能遵循协议
2、枚举增强
OC中枚举,枚举量的值都是整型数据,而Swift中,枚举量的值,除了Int,可以是字符串等类型(只能是字符串、整形、浮点型这三种)
Swift中枚举,可以增加参数
Swift中枚举,可以增加方法
Swift中枚举,可以遵循协议
很多功能的设计都依赖于Swift的枚举,如可选型的定义、Alamofire中的Result等
经常会遇到枚举量想跟字符串绑定,OC中枚举,需要单独去定义字典去映射,Swift则不需要,甚至可以在定义枚举的时候就处理好这种映射
3、Swift中的类有更多特性
swift中的类就是普通的一个类,并没有特定意义的基类(OC中是NSObject)
swift中的类一般不具备运行时特性,除非是继承自NSObject的类,或者是增加@objc修饰
swift类支持内部类
swift类泛型支持远比OC灵活和强大
swift类支持方法重载
4、函数更灵活
支持函数嵌套
支持多返回值(新增了元组数据类型)
支持参数默认值
5、for\switch等语句更灵活
for遍历方式更多
switch不仅仅只支持整型,也支持字符串等类型
代码示例:
1、for循环
//在swift之前的版本中,声明一个空数组变量,如果不指定元素类型,会默认为元素是NSObject类型,但是现在不行,必须指定元素类型
var arr:[Int] = [1,3,2,8,6,7]
//直接遍历
for i in arr{
print(i)
}
//元组方式遍历
for (indexTemp,valueTemp) in arr.enumerated(){
print("元素下标:\(indexTemp),元素值:\(valueTemp)")
}
//常规方式遍历,在swift3之中已经被移除了
//for var i = 0; i<arr.count; i += 1 {
//
//}
//for in可以通过区间遍历
for i in 3...21{
print(i)
}
2、while循环与repeat while(swift中不再叫do while,而是repeat while)
print("\n\nwhile循环")
var whileInt = 10
while whileInt>0 {
print(whileInt)
whileInt -= 1
}
print("\n\nrepeat while循环")
whileInt = 10
repeat{
print(whileInt)
whileInt -= 1
}while whileInt>0
/*
控制转移语句:
break,终止本层循环,如一个for循环,会直接终止这个for循环
continue,只终止本轮循环,不会终止整层循环,不再执行continue之后的语句,直接开启下一次循环
return 函数体中调用,结束整个函数
*/
6、if else
// if条件语句,必须是bool值,并不是像其他语言那样非0就默认为true
if cConditon1 == 1{
print(cConditon1)
}
else{
}
//例外的情况是,if let解包可以用=号,否则必须传入bool值或进行逻辑运算</pre>
### Switch
swift中switch语句十分强大,并不限定于整型,
swift中switch的case语句会默认break,不需要显示写
但可以通过fallthrough,取消掉break中断,达到OC中继续执行之后的case的效果
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n61" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; position: relative !important;">/*
*/
var switchInt = 4
switch switchInt {
//case 3.1..<10.2:
// print("5")
case let x where x<10:
print("case let 的形式直接取值")
case 1..<5,6...10:
print("switchInt = \(switchInt)")
case 2:
print("condition = \(2)")
default:
print("No Result")
}
var switchStr = "Abc"
switch switchStr {
case "a","AB":
print("a")
//case 5:
// print("5tt")
case "Abc":
print(switchStr)
default:
print("default")
}
var switchTuple = (5,"tuple")
switch switchTuple {
case (1..<6,_):
print("YES")
fallthrough
case (_ , _):
print(switchTuple)
fallthrough
case (4 , _):
print("fallthrough 4")
case (let x,"tuple")://可以用var修饰
print(x)
case (let x, let y):
print(x,y,"YES")
default:
print("default")
}
var switchArray = ["A","ab","AG"]
switch switchArray {
case ["A"]:
print("A")
default:
print("default")
}
var switchRange = 1..<10
switch switchRange {
case 2..<5://必须为半开区间,不能为闭区间
print(switchRange)
default:
print("default")
}
//需要注意的是
//但case变量需要和条件变量类型一致(或者case 变量所表示的区间值类型,需要和条件变量类型一致),这点是相同的,
//并且比如switch条件为区间,那么case条件必须为区间,而switch条件为整型,case条件可以为整型也可以为整型区间
//switch的case下,必须携带一句可执行的语句,可以是空语句
//switch的每个case,默认包含break,不会因为漏写break而继续执行下一个case,如果想像其他语言一样,达到某个case条件时继续向下执行case,那么可以使用fallthrough关键字
//switch中的的每个case语句,条件可以是多个,表示或,用逗号隔开,只要满足其中一项,就达到这个case执行的条件
//switch中,default语句可以不写,但是必须满足所有列出的case已经穷举出switch条件所有的可能性
7、类型安全
强类型语言:swift在变量声明的时候,必须显示的声明类型,类型一旦确定,不可更改
可选性的定义,使得开发者必须去考虑空的情况
Swift是静态语言,有类型诊断,OC是动态语言
8、注重值类型
swift与OC很大的区别之一在于,Array、Dictionary、Set等,不再是引用类型,而是基于结构体的值类型
我们在OC看到的绝大多数常见的数据类型,在Swift中都是值类型,甚至针对UIKit,还出了一套侧重于值类型的SwiftUI框架
9、支持运算符重载
10、变量名称更灵活
var 名字 = "name"
var 🏷 = "表情"
print(名字,🏷)
//变量名不局限于字母
11、强大的泛型支持
协议支持泛型
泛型约束更灵活多样
12、支持多种派发方式
swift支持静态派发(效率高)、动态派发(函数表派发、消息派发)方式,OC支持动态派发(消息派发)方式。
1、直接派发(Direct Dispatch)
静态派发,又叫做早期绑定,是指在编译期将方法调用绑定到方法的实现上,这种派发方式非常快。在编译期,编译器可以看到调用方和被调方的所有信息,直接生成跳转代码,这样在运行期就不会有其它额外的开销。并且编译器可以根据自己知道的信息进行优化,比如内联,可以极大提高程序运行效率。
在 Swift 中,结构体和枚举的方法调用,以及被 final
标记的类和类的方法,都会采用这种派发方式。
直接派发是最快的,不止是因为需要调用的指令集会更少,并且编译器还能够有很大的优化空间,例如函数内联等,直接派发也有人称为静态调用。 然而,对于编程来说直接调用也是最大的局限,而且因为缺乏动态性所以没办法支持继承和多态。
- 2、函数表派发(Table Dispatch)
函数表派发是编译型语言实现动态行为最常见的实现方式函数表使用了一个数组来存储类声明的每一个函数的指针.大部分语言把这个称为virtual table"虚函数表), Swift里称为“witness table.每一个类都会维护一个函数表,里面记录着类所有的函数,如果父类函数被 override的话,表里面只会保存被 override之后的函数.一个子类新添加的函数,都会被插入到这个数组的最后运行时会根据这一个表去决定实际要被调用的函数 查表是一种简单,易实现,而且性能可预知的方式.然而,这种派发方式比起直接派发还是慢一点从字节码角度来看,多了两次读和一次跳转,由此带来了性能的损耗.另一个慢的原因在于编译器可能会由于函数内执行的任务导致无法优化(如果函数带有副作用的话) 这种基于数组的实现,缺陷在于函数表无法拓展.子类会在虚数函数表的最后插入新的函数,没有位置可以让 extension安全地插入函数.
- 3、消息机制派发(Message Dispatch)
消息机制是调用函数最动态的方式.也是Ccoa的基石,这样的机制催生了kvo, UIAppearence和 CoreData等功能.这种运作方式的关键在于开发者可以在运行时改变函数的行为.不止可以通过 swizzling来改变,甚至可以用 isa-swizzling修改对象的继承关系可以在面向对象的基础上实现自定义派发. Swift运行时 纯 Swift类的函数调用已经不再是 Objective-c的行时发消息,而是类似C++的vtable,在编译时就确定了调用哪个函数,所以没法通过 runtime获取方法、属性。 而Swift为了兼容 Objective-C,凡是继承自 NSObjec的类都会保留其动态性,所以我们能通过 runtime拿到他的方法。这里有一点说明:老版本的 Swift(如2.2)是编译期隐式的自动帮你加上了@objc,而40以后版本的 Swift编译期去掉了隐式特性,必须使用显式添加。 不管是纯Swift类还是继承自 NSObject的类只要在属性和方法前面添加@objc关键字就可以使用 runtime. 项目 原始定义 扩展 值类型 直接派发 直接派发 协议 函数表派发 直接派发 类 函数表派发 直接派发 继承自NSObject的类 函数表派发 消息机制派发
- 小结
值类型总是会使用直接派发,简单易懂 而协议和类的 extension都会使用直接派发 NSObject的 extension会使用消息机制进行派发 NSObject声明作用域里的函数都会使用函数表进行派发. 协议里声明的,并且带有默认实现的函数会使用函数表进行派发 一些关键字的派发机制
关键字 机制 final 直接派发 dynamic 消息机制派发 @objc & @nonobjc 改变在OC里的可见性 @inline 告诉编译器可以直接派发 Swift 运行时-final @objc
可以在标记为 final的同时,也使用@objc让函数可以使用消息机制派发.这么做的结果就是,调用函数的时候会使用直接派发,但也会在 Objective-c的运行时里注册响应的 selector.函数可以响应 perform(selector)以及别的 Objective-C特性但在直接调用时又可以有直接派发的性能.
13、能更灵活地支持函数式编程(链式调用)
14、有命名空间
- 使得一个项目中同名文件或者同名类可以存在
15、有更严格的访问控制
open>public>internal(默认,无需显示声明)>fileprivate>private
OC中,我们一般很少去使用访问控制符,并且OC的类,都是能被子类继承的
Swift中,如果一个类不想被子类继承,可以用final修饰
Swift中,open修饰的类,才能在其他模块中被访问和继承,并且open修饰的类中,如果方法或者属性有open修饰,也才能被重写
Swift中,public修饰的类型,在其他模块只能访问,如果是类,只能被访问,不能被继承
Swift中,internal修饰的类型(默认修饰符,无须显示声明)是默认权限. 在模块内的文件,其类定义和函数定义是互相可见的,但是模块外是不可见的,所以它又可理解为:对模块私有.
Swift中,private修饰的类型,只能在当前private修饰类型(或者变量、方法)的所在作用域内被访问
Swift中,fileprivate修饰的类型,只能当前文件内被访问
15、代码简洁
16、写时拷贝特性
简而言之,写时拷贝,就是指只是单纯赋值时,不进行拷贝,而只有发生内容修改的时候才拷贝
如a、b、c三个变量,指向同一个值类型,此时并不会发生内容拷贝,三个变量都用的同一份内存地址
但是当修改b的时候,只有b的内存地址发生变化,a、c保持不变
17、区间符
[n,m]闭区间,用...表示
[n,m)半开区间,用..<表示</pre>
18、语法糖
[1, 2, 3].map{ $0 * 2 }
[1, 2, 3] 使用了, Array 实现的ExpressibleByArrayLiteral 协议, 用于接收数组的字面值 map{xxx} 使用了闭包作为作为最后一个参数时, 可以直接写在调用后面, 而且, 如果是唯一参数的话, 圆括号也可以省略 闭包没有声明函数参数, 返回值类型, 数量, 依靠的是闭包类型的自动推断 闭包中语句只有一句时, 自动将这一句的结果作为返回值 [图片上传失败...(image-ce962-1708916876270)] 0, 后续参数以此类推
19、print函数(打印更灵活)
比OC的NSLog函数跟灵活
(1)支持直接打印一个变量,不局限于NSLog只能格式化输出
(2)支持,分割打印多个变量
(3)支持"(变量名)",快速格式化打印变量
(4)支持多个变量打印时分隔控制(separator:默认空格 terminator:默认换行)
var a = 12
print(a)
print("BGG"+"66666")
print("fafjfk","affdf",separator:"---")
print("rrrr","hhhhh",separator:"~~",terminator:"!!!")
//separator:默认空格 terminator:默认换行
print("新行吗?")
print("a=\(a)是整数")</pre>