Swift 学习2
一:playground
作用:学习代码、实验代码、测试代码
官方的学习资源是以playgroud形式提供的,建立自己的playgroud文件,能够每次版本升级时,第一时间发现语法的变化
Swift程序中与Oc的不同
1、没有.h与.m文件,都是以.swift结尾的文件
2、没有main.m文件 —— appDelegate中的@UIApplicationMain 就是程序的入口
3、Swift的类都是用class标识
4、OC中的initwithXX方法在Swift中是“类型(xxx)”
5、swift中的属性和方法都是用 “.” 的形式
6、结尾可以不加分号(加了也可以)
7、在当前类中使用当前属性,不需要加“self."(除了闭包)
8、在swift中把枚举分成了两部分"枚举名.枚举值",枚举名可以省略
9、在swift中使用print打印,也可以使用nslog,但效率没有print高
二、Swift的基本语法
1、变量与常量
swift中变量与常量的类型可以不设置,在赋值时由系统自动推断,如果希望手动指定那么在变量或常量后加":类型"
在swift中,不同类型之间不能进行运算,oc可以,因为oc有隐式转换
运算符对称,运算符左右两边的空格要对称,如果一边有空格,另一边没有就会报错,一边多了个空格不会报错,但影响美观
var来声明变量。在swift中,如果定义了一个变量,没有初始值的时候,系统会报错。如果开发者就是想声明一个变量为空,那么需要在类型的后面加个"?"
let用来声明常量。在swift中,如果定义了一个常量,没有初始值的时候,系统会报错。对于常量,不能通过后面加"?"来处理,只能赋值为nil或者0来处理。
2、if判断与guard判断
在swift中没有 非零即真 的概念,条件语句一定是bool类型(true/false )
swift中条件语句的小括号可以省略,但如果执行代码只有一句,代码块的大括号不可以省略
guard判断如果条件不成立,那么执行else代码块,相当于一个没有第一个代码块的if
3、三目运算 与 可选类型
三目运算在swift中与oc没有区别
在可选类型中,"??" 是一个简单的三目运算,比如
age ?? 0 意为age为nil或空时使用??后面的0值
使用 ? 修饰的类型为可选类型,属性在赋值时候,会带有Optional关键字,这样不能与不带关键字的类型做操作,可以使用 "!"来强行解包,但使用需要小心小心再小心。
4、if _ let / guard _ let
类似自定义view的if(self = [super XXX])中的 “=”,判断等号前面的有值就执行第一代码块,否则else。声明的作用域仅仅在if内部。
而guard是判断不成立则执行else,声明作用域为guard的下方(不仅仅在guard内部)
5、for循环
不可以使用c形式的for循环,++形式在swift3.0后移除不能在使用,for循环的时候,小括号不要写
语法:
for i in x..<y (x ---- y-1)
for i in x...y (x-------y)
for value in arr (arr 数组)
6、switch
swift的switch可以判断任何类型,比oc要广;
默认没有break,并且不会造成穿透效果(如果想实现穿透,设置关键字 "fallthrough");
switch可以进行范围判断 语法:
case _where c(声明的变量) > 60 (范围)
7、字符串的长度、遍历、截取、拼接
在swift中字符串可以直接使用String表示,string本质是一个结构体,比oc的NSSting更加轻便,而且效率更高。
问题:
——如何获取字符串长度
——str.characters.count
——如何遍历字符串
——for char in str.characters 其中char就是一个字符
——字符串字节注意点
——一个英文/数字等于一个字节,一个中文等于三个字节
——字符串截取
——如一字符串"123456789"
~ 最后两个不要 let end2index = str.index(str.endIndex, offsetBy: - 2)
str.substring(to: end2index)
~ 前两个不要 let start2index = str.index(str.start, offsetBy: 2)
str.substring(from: end2index)
~ 如何把string转化成NSString
使用as 相当于强转
——字符串如何拼接
——需求:两个常量 "老王" 18,拼接成"我叫老王今年18"
~方式1
~ "我叫" + name + "今年" + string(age)
~方式2
~"我叫\(name)今年\(age)" **常用
8、数组定义、拼接、遍历
——数组定义
——使用中括号形式,如果数组中有不同类型,需指定一下 ":[Any]"
——如何定义可变和不可变的数组
——可变数组用var 不可变的就是let
——如何合并数组
——"+=" arr += arrb;
——如何往数组添加元素
——"append"
——如何移除元素
——"remove"系列
——如何获取元素
——和oc一样通过下标
9、函数
无参无返、有参无返,有参有返、外部参数无返回值的三种写法
方法一
func demo (){
}
方法二
func demo () -> Void{
}
方法三
func demo () -> (){
}
——函数类型作为参数
你可以用(Int, Int) -> Int这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现留给函数的调用者来提供。
下面是另一个例子,正如上面的函数一样,同样是输出某种数学运算结果:
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// 打印 "Result: 8"
——函数类型作为返回类型
你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(->)后写一个完整的函数类型。
下面的这个例子中定义了两个简单函数,这两个函数的类型都是(Int) -> Int:
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
如下名为chooseStepFunction(backward:)的函数,它的返回类型是(Int) -> Int类型的函数。
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
——函数内部定义函数(嵌套函数)
默认情况下,嵌套函数是对外界不可见的,但是可以被它们的外围函数(enclosing function)调用。一个外围函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用。
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!
10、block 闭包
——基本的闭包形式
block一般用在 异步任务完成的回调
无参无返回
方法一
let demo = {
}
方法二
let demo = { () in
}
方法三
let demo = { () ->Void in
}
方法四
let demo = { () -> () in
}
有参数无返回
let demo = { (a: Int, b:Int) in
}
有参数有返回
let demo = { (a: Int, b:Int) -> Int in
}
解析:in后面写具体希望执行的代码,in前面为 有参无返/有参有返 的定义
——尾随闭包
——若将闭包作为函数最后一个参数,可以省略参数标签,然后将闭包表达式写在函数调用括号后面
func testFunction(testBlock: ()->Void){
//这里需要传进来的闭包类型是无参数和无返回值的
testBlock()
}
//正常写法
testFunction(testBlock: {
print("正常写法")
})
单表达式闭包隐式返回
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
根据上下文推断类型
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
在这个例子中,sorted(by:)方法的参数类型明确了闭包必须返回一个Bool类型值。因为闭包函数体只包含了一个单一表达式(s1 > s2),该表达式返回Bool类型值,因此这里没有歧义,return关键字可以省略。
1、如果有多个参数,最后一个参数为闭包,(这是才称为尾随闭包)那么尾随闭包就会提前把小括号关闭
//尾随闭包写法
testFunction(){
print("尾随闭包写法")
}
2、当只有一个参数且为闭包时,那么尾随闭包就会把小括号和参数省略
//也可以把括号去掉,也是尾随闭包写法。推荐写法
testFunction {
print("去掉括号的尾随闭包写法")
}
——逃逸闭包
当一个闭包作为参数传到一个函数中,需要这个闭包在函数返回之后才被执行,我们就称该闭包从函数种逃逸。一般如果闭包在函数体内涉及到异步操作,但函数却是很快就会执行完毕并返回的,闭包必须要逃逸掉,以便异步操作的回调。
逃逸闭包一般用于异步函数的回调,比如网络请求成功的回调和失败的回调。语法:在函数的闭包行参前加关键字“@escaping”。
//例1
func doSomething(some: @escaping () -> Void){
//延时操作,注意这里的单位是秒
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
//1秒后操作
some()
}
print("函数体")
}
doSomething {
print("逃逸闭包")
}
//例2
varcomletionHandle: ()->String = {"约吗?"}
func doSomething2(some: @escaping ()->String){
comletionHandle = some
}
doSomething2 {
return"叔叔,我们不约"
}
print(comletionHandle())
//将一个闭包标记为@escaping意味着你必须在闭包中显式的引用self。
//其实@escaping和self都是在提醒你,这是一个逃逸闭包,
//别误操作导致了循环引用!而非逃逸包可以隐式引用self。
——闭包的循环引用及解决方法
——原因
——原理跟OC中的block类似, 当有个属性记录下了函数传递回来的闭包, 产生强引用, 就会发生闭包的循环引用
——解决方法(三种)
——使用weak修饰变量, 打破强引用, 因为使用weak修饰的变量有一次变成nil的机会
——使用[weak self] 修饰闭包原理跟__weak类似, 这样在闭包中使用self, 就是弱引用
——使用[unowned self ] 修饰闭包, 跟__unsafe_unretained类似, 不安全
11、面向对象
在一个模型类中,属性用var而不是let。
创建Person对象时,可以直接使用 "类名()"的形式
声明常量或者变量时候,默认应该是有值的。
解决方案
方法一
把属性变为可选类型(一般使用这种)
方法二
直接赋值
方法三
在构造函数中赋值,(需要写在super之前)
——重写与重载构造函数
——重写
父类方法,子类再写一遍
——重载
都是在一个类中,方法名相同,参数不同
好处:更灵活,方便记忆,只需要记住方法名即可
12、异常处理
所有的方法后带有throws的都应该进行错误处理
写法为 在所需执行的代码前面写 "try/try?/try!" (其中try与try?比较常用)
方式1 try
使用默认的try时,如果有错误可以通过do—catch进行捕捉,如果没有错误,那就正常执行
do{
let res = try JSONSerialization.jsonObject(with:data,options:[])
print()
}catch{
print()
}
方式2 try?
try?如果使用的是可选try,那么如果有错误会返回nil,没有错误就会正常执行
let res = try JSONSerialization.jsonObject(with:data,options:[])
print()
方式3 try!
try!是强行try,意思是可以放心的用,但是如果是错误的,那么就崩溃了