即将入职新公司,换工作的间歇期有时间看一看知识点,新项目里swift和oc混编,所以趁着机会看了下swift语法,由于UIKit oc和swift一样,所以只进行swift语法的学习,翻了一遍《swift 4从零到精通iOS开发》,第一遍阅读用笔标出了语法不同点,然后边写代码实验边做笔记的形式记录。
一、基本数据类型
1.元组
//元组允许一些不相关的类型进行自由组合,成为新的集合类型,适用于简单数据结构,复杂数据结构依然推荐用类或结构体。
var pen:(name:String, length:NSInteger) = ("我的钢笔", 15)
//访问属性
print("名字:"+pen.name+" 长度:\(pen.length)”)
2.可选值类型
swift中基本数据类型不能为nil,如果想可以为nil需要用?进行包装成可选值类型,用该值时需要用!进行拆包操作。
?!是打包和拆包
举例:
//编译报错,'nil' cannot be assigned to type 'String'
var str:String = "hello"
str =nil
//编译报错,Variable 'str1' used before being initialized,因为swift中String是普通类型,普通类型不能等于nil
var str1:String
if str1==nil {
}
/*
编译通过,str2是String?类型
swift里在普通类型后面加?可以把普通类型包装成Optional类型。Optional类型不会单独存在,总会附着于某个具体数据类型之上,具体数据类型可以是基本数据类型、结构体或者类等。
Optional类型只有两种值:
1.如果附着类型对应的量值有具体的值,则为具体值得包装。
2.如果附着类型对应的量值没有具体的值,则其为nil
*/
var str2:String?
if str2==nil {
print("str2为空")
}
输出str2为空
//编译报错,Value of optional type 'String?' must be unwrapped to a value of type 'String'
//如果使用str2必须进行拆包
var str3 = "hello" + str2
print(str3)
?可以出现在类型后面和实例后面,如果出现在类型后面,代表此类型对应的Optional类型,如果出现在实例后面则代表可选链的调用。*暂时没弄懂
!也可以出现在类型后面和实力后面,出现在类型后面代表一种隐式解析的语法结构。*暂时没弄懂
//运行报错,Fatal error: Unexpectedly found nil while unwrapping an Optional value: file
//不能对nil拆包,所以拆包之前需要判断是否为空
var str2:String?
var str3 = "hello" + str2!
print(str3)
//不会走进判断条件里面
var str2:String?
if str2 != nil {
var str3 = "hello" + str2!
print(str3)
}
//用if let结构判断要拆包的变量是否为nil
if let tmp = str2 {
var str3 = "hello" + str2!
print(str3)
}else{
str2 ="world"
var str3 = " hello" + str2!
print(str3)
}
//typealias关键字可以为类型取别名
typealias Price = NSInteger
var price:Price = 12;
二、字符串与集合
+和\()结构
var str1 = "hello"
var str2 = "world"
var str3 = str1 + " " + str2
print(str3)//打印hello world “+”操作符可以实现字符串拼接
var str4 = "hello \(str2)"
print(str4)//打印hello world 如果str2未定义会报错,
2.isEmpty方法
//打印空字符串 ""认为是空字符串
var str5 = ""
if str5.isEmpty {
print("空字符串")
}else{
print(str5)
}
//编译报错,Value of optional type 'String?' must be unwrapped to refer to member 'isEmpty' of wrapped base type 'String'
var str5:String?
if str5.isEmpty {
print("空字符串")
}else{
print(str5)
}
//打印str3 str4相等 = < >可以进行字符串判断
if str3 == str4 {
print("str3 str4相等")
}
3.数组
Array有别于NSArray
//以下两种写法一样,都是声明一个NSInteger数组
var array1:[NSInteger] = [0,1,2,3,4,5]
var array2:Array<NSInteger> = [0,1,2,3,4,5]
//数组判空
if array1.isEmpty {
print("空数组")
}
//访问数组元素
var a = array1[1]
var subArray = array1[0 ... 3]
//访问第一个元素
var b = array1.first
//访问最后一个元素
var c = array2.last
array1[2] = 15;
var array3:NSArray = [NSArray (array: array1)]
//array3[2] = 3;编译会报错,Array有别于NSArray
var array4:NSMutableArray = [NSMutableArray (array: array1)]
array4.firstObject
//Array取第一个元素是.first NSMutableArray取第一个元素是.firstObject
array1.insert(20, at: 0)
array1.remove(at: 3)
array1.removeFirst()
array1.removeLast()
array1.removeAll()
4.set类型
Set类型可以实现去重,同样有别于NSSet,NSMutableSet。
可以取两个set的交集、并集、补集等
var set:Set<NSInteger> = Set.init()
set.insert(1);
set.insert(3);
set.insert(3);
print(set)//[3, 1]
5.Dictionary类型
//下面两种声明方式等价,都表示声明一个key为String类型,value为NSInteger类型的字典
//Dictionary也不等价于NSDictionary NSMutableDictionary
var dic1:[String:NSInteger]
var dic:Dictionary<String, NSInteger> = Dictionary.init()
dic["tom"] = 1
dic["lucy"] = 2
dic["kate"] = 3
var dic2:NSDictionary = NSDictionary.init(dictionary: dic)
var dic3:NSMutableDictionary = NSMutableDictionary.init(dictionary: dic)
dic3.setValue(6, forKey: "lily")
dic3.removeObject(forKey: "tom")
print(dic3)
三、运算符与控制流程
合并运算符??
合并运算符??是一个二元运算符,需要两个操作数,第一个操作数必须为一个optional值,如果此optional值不为nil,则将其进行拆包操作,并作为空合并运算的运算结果。
//打印"字符串",因为str有值,所以对str拆包
var str:String? = "字符串"
var str1 = str ?? "啦啦啦"
print(str1)
区间运算符
/*输出
true
false
1
2
3*/
let range1 = 1...3
let range2 = 1..<3
print(range1 ~= 3)
print(range2 ~= 3)
for index in range1 {
print(index)
}
for-in循环结构
swift删除了for(int i; i<5; i++)这种for循环结构,只有for-in。也删除了++, —运算符。
for循环接收两个参数,第二个参数可以是一个集合类型的实例也可以是范围区间,第一个参数为捕获参数,每次从第二个参数中遍历出的元素会赋值给它,可供开发者在循环结构中直接使用。
let range1 = 1...3
for index in range1 {
print(index)
}
//输出123
let array:Array = [0,1,2]
for value in array {
print(value)
}//输出012
repeat-while
repeat-whiled会先执行一次循环体再进行条件判断,类似do while
var i:NSInteger = 0
while i<3 {
print(i)
i+=1
}//输出012
var j:NSInteger = 3
repeat{
print(j)
j+=1
}while j<3
//输出3
if-else
if关键字后面跟的条件必须为严格意义上的逻辑值或者结果为逻辑值的表达值
var i:NSInteger = 0
if i {
print(i)
}//编译报错Cannot convert value of type 'NSInteger' (aka 'Int') to expected condition type ‘Bool’
var i:NSInteger = 0
if i != 0 {
print(i)
}//编译不报错,但是记住!=需要跟i中间有个空格,否则被认为是拆包操作编译报错
switch-case
swift中的switch不像oc中只能进行int类型的值匹配,swift中的switch可以进行任意数据类型的匹配,case子句的语法和扩展更加灵活。
oc中case语句不会因为匹配成功而中断,如果不break,case的语句会依次执行,swift一个case语句匹配成功后会自动跳出switch结构,如果不加特殊处理,switch每个分支只会执行一次或者不执行。
case中的值有三种匹配方式:完全匹配、选择匹配、范围匹配
//case的写法更灵活
let charac:Character = "a"
switch charac {
case "a","b":
print("charac is a||b")
case "c":
print("charac is c")
case "d":
print("charac is d")
default:
print("no charac")
}
//输出charac is a||b
//case的写法
var value:NSInteger = 3
switch value {
case 1...3:
print("value <= 3")
case 4...6:
print("value >=4 <=6")
default:
print("value > 6")
}//输出value <= 3
//三种匹配方式
var tuple = (4,1)
switch tuple {
case (0,1)://完全匹配:两个值都匹配上才算匹配上
print("完全匹配")
case (_,1)://选择匹配:忽略某个值,只进行另一个值的匹配
print("选择匹配")
case (0…3,0...3)://范围匹配:值符合范围就算匹配上
print("范围匹配")
default:
print("no")
}//输出 范围匹配
//case中捕获变量
var value2 = (0,0)
switch value2 {
case (let a, 1)://如果第二个元素为1,则会走进这个case,并且把第一个值捕获到a中
print(a)
case (let b, 0):
print(b)
case (let a, let b)://捕获到元组中两个元素,等价于这种写法let(a,b)
print(a, b)
default:
print("")
}
fallthrough
如果需要case中结构执行完之后不自动进行中断,用fallthrough会继续向下执行
var tuple = (0,1)
switch tuple {
case (0,1)://两个值都匹配上才算匹配上
print("完全匹配")
fallthrough
case (_,1)://忽略某个值,只进行另一个值的匹配
print("选择匹配")
fallthrough
case (0...3,0...3)://值符合范围就算匹配上
print("范围匹配")
default:
print("no")
}
//输出 完全匹配 选择匹配 范围匹配
Guard-else
var value3:NSInteger = 3
guard value3 < 4 else {
print("value3 < 4")
}
print("value3 >= 4")
//打印value3 >= 4
四、函数
函数的定义
关键字func声明函数,后面加上参数列表,->后面加上返回值,没有返回值可以省略。
参数名可以指定名字和类型,无参函数传空即可。
func funName(<#parameters#>) -> <#return type#> {
<#function body#>
}
—————————————————————————————————————————————————————————
返回多个值可以返回元组。
func getPerson() -> (name:String, age:NSInteger) {
return ("张三", 20);
}
func testFunAndBlock(){
let person = getPerson()
print(person)
}//打印(name: "张三", age: 20)
—————————————————————————————————————————————————————————
可以为参数取别名,函数内部用str、value
func setPerson(name str:String, age value:NSInteger){
print("\(str),\(value)")
}
func testFunAndBlock(){
//调用者调用外部名字
setPerson(name: "张三", age: 20)
}//打印 张三,20
—————————————————————————————————————————————————————————
函数参数支持默认值,如果参数设置了默认值,可以传可以不传此参数,传的话用传的值,没传则用默认值。
func add(value1:NSInteger, value2:NSInteger, value3:NSInteger = 10){
print(value1+value2+value3)
}
func testFunAndBlock(){
add(value1: 1, value2: 2)
add(value1: 1, value2: 2, value3: 3)
}//打印 13 6
—————————————————————————————————————————————————————————
某个参数后面加上… 就会在此参数设置为数量可变,传递的类型必须相同,可以传多组可变的参数
func add2(values:NSInteger...){
var sum:NSInteger = 0
for value in values {
sum+= value
}
print(sum)
}
func testFunAndBlock(){
add2(values: 1,2,3)
}//打印 6
—————————————————————————————————————————————————————————
swift函数如果传递值类型的蚕食,在传递过程中会复制一份常量,且在函数内不可修改,
func fun(value:NSInteger){
value +=1
}//编译报错:Left side of mutating operator isn't mutable: 'value' is a 'let' constant
—————————————————————————————————————————————————————————
inout关键字可以再函数内修改传递参数真正的值,如下所示,声明为inout类型的参数传递参数时需要使用&
func fun(value:inout NSInteger){
value+= 1
}
func testFunAndBlock(){
var param = 10
fun(value: ¶m)
print(param)
}//打印 11
函数嵌套
swift支持函数嵌套,在函数内部再创建函数,子函数只能在父函数内部调用,不能被其他函数调用。但可以作为返回值传递到父函数外部。
func fun2(value:NSInteger, value2:NSInteger){
func add(val1:NSInteger, val2:NSInteger)->NSInteger{
return val1+val2
}
print(add(val1: value, val2: value2))
}
func testFunAndBlock(){
fun2(value: 1, value2: 2)
}//打印 3
Block
swift的block结构如下,{(参数列表)->返回值 in block实现},首先外边由大括号包围,内部由关键字in分隔参数和实现部分
let addBlock = {(value1:NSInteger, value2:NSInteger)->NSInteger in
return value1 + value2
}
print(addBlock(1,2))
//打印 3
—————————————————————————————————————————————————————————
当block实现只有一行代码时,可以省略return关键字,默认将此行代码执行结果返回。
let addBlock = {(value1:NSInteger, value2:NSInteger)->NSInteger in
value1+ value2
}
print(addBlock(1,2))
//等价于上面代码
—————————————————————————————————————————————————————————
func fun3(value1:NSInteger, value2:NSInteger, addBlock:(NSInteger, NSInteger)->NSInteger) -> NSInteger {
return addBlock(value1, value2)
}
func testFunAndBlock(){
let number = fun3(value1: 1, value2: 2) { (val1:NSInteger, val2:NSInteger) -> NSInteger in
return val1+val2
}
print(number)
}//打印 3
—————————————————————————————————————————————————————————
//定义一个函数,参数传入两个int值,和一个操作block
func fun3(value1:NSInteger, value2:NSInteger, addBlock:(NSInteger, NSInteger)->NSInteger) -> NSInteger {
return addBlock(value1, value2)
}
//调用函数传参
let number1 = fun3(value1: 1, value2: 2, addBlock: { (val1:NSInteger, val2:NSInteger) -> NSInteger in
return val1+val2
})
//可以省略addBlock的参数名字
let number = fun3(value1: 1, value2: 2) { (val1:NSInteger, val2:NSInteger) -> NSInteger in
return val1+val2
}
//再简化写可以写成如下效果完全一样
let number2 = fun3(value1: 1, value2: 2){$0+$1}
逃逸block
当一个block作为参数传入一个函数中,但是这个block在函数返回后才执行,我们称这个block从这个函数中逃逸。
在参数名前标记@escaping来标记闭包,表示这个闭包是可以逃逸的。
有一个使用场景:
如果函数内是异步执行的,但是闭包要在异步执行完了以后再执行,我们可以让这个闭包逃逸出函数再执行。
func testFunAndBlock(){
fun4 {
print("hello")
}
}
//执行异步操作,延时十秒执行block
func fun4(completeBlock:()->Void) {
let deadLine = DispatchTime.now()+10
DispatchQueue.main.asyncAfter(deadline: deadLine) {
completeBlock()
}
}//编译出错Escaping closure captures non-escaping parameter ‘completeBlock'
//正确写法加上@escaping
func fun4(completeBlock:@escaping()->Void) {
let deadLine = DispatchTime.now()+10
//gcd 延时十秒操作
DispatchQueue.main.asyncAfter(deadline: deadLine) {
completeBlock()
}
}
五、高级运算符
溢出运算符
var value:UInt8 = 255;
value = value+ 1;//编译报错 Arithmetic operation '255 + 1' (on type 'UInt8') results in an overflow
var value:UInt8 = 255;
value = value&+ 1;//编译通过 value = 0
value = value&- 1;//编译通过 value = 255
value = value&* 2;//编译通过 value = 254
重载运算符
prefix、infix、postfix自定义前缀运算符、中缀运算符、后缀运算符
prefix operator ++
prefix func ++ (value:NSInteger) -> NSInteger {
return value + 1
}
let value1 = ++3
print(value1)
//打印 4
infix operator ++
func ++ (value1:NSInteger, value2:NSInteger) -> NSInteger {
return value1*value1 + value2*value2
}
let value1 = 1++3
print(value1)
//打印10
postfix operator ++
postfix func ++ (value:NSInteger) -> NSInteger {
return value + value
}
let value1 = 3++
print(value1)
//打印6
枚举类型
enum Surname {
case 张
case 王
case 李
case 赵
}
var sur:Surname = Surname.张
sur =Surname.李
switch sur {
case .张:
print(sur)
case .王:
print(sur)
case .李:
print(sur)
case .赵:
print(sur)
default:
print(sur)
}//打印:张