2020.6.14 学习笔记
2020-06-11 本文已影响0人
Mi欧阳
16年的时候,学习了一段时间的Swift。学了一段时间就中断了,一来是因为对于新语言的抵触,二来Swift每次版本更新,API都会发生大改,个人讨厌这点。第三点,Swift的写法是真的自由,对于喜欢条条框框的我极不适应。
20年因为找工作再次从头开始学习,当前最新版本应该是5.0,但因为设备问题,所以暂时看得还是3.0的资料。
单独开一片记录Swfit中一些奇奇怪怪的写法。
字典(数组)中的万用类型
//这里的Any就相当于OC中的id类型,万用类型
//如果字典的value中不包含基本数据类型,那么可以用AnyObject替代Any
var array1 = [String:Any]()
array1["key1"] = 2
//Null对象可以存入字典中
array1["key2"] = NSNull()
//这样子编译会通过,但是这个键值对不会存入字典中
array1["key3"] = nil
print(array1)
输出结果:["key2": <null>, "key1": 2]
String组合
let apples = 3
let oranges = 5
let other = "我好开心。"
//Swift中String组合或者增加内容,可以用\()的写法
var fruitSummary = "我买了 \(apples + oranges) 元的水果。\(other)"
//也可以用append的写法
fruitSummary.append("我快乐的回家了。")
print(fruitSummary)
输出结果:我买了 8 元的水果。我好开心。我快乐的回家了。
遍历一个对象,Swift中字典和数组都能被遍历
//已知初始化对象是个字典,其中每个元素的value又是数组
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var max = 0
//直接可以用(key,value)这种写法代表字典对象
//因为key在代码段中用不到,所以系统会推荐以"_"符号替代key
for (_, values) in interestingNumbers {
for value in values {
if value > max {
max = value
}
}
}
print(max)
字典对象获取所有的key或者value
let dict = ["27":"w","15":"t","36":"b"]
//获取所有的key,一定要强转为Array类型,否则不能直接使用
let keys = Array(dict.keys)
//获取所有的value
let values = Array(dict.values)
print(keys)
print(values)
//让字典中的所有对象根据key的大小排序,小的在前
let keys1 = dict.sorted(by: {$0.0 < $1.0})
//让字典中的所有对象根据value的大小排序,小的在前
let values1 = dict.sorted(by: {$0.1 < $1.1})
print(keys1)
print(values1)
//别问这个是怎么实现的,涉及尾闭包的简写,我只能明白个大概
输出结果:["36", "27", "15"]
输出结果:["b", "w", "t"]
输出结果:[("15", "t"), ("27", "w"), ("36", "b")]
输出结果:[("36", "b"), ("15", "t"), ("27", "w")]
可选值操作符(?)、非空操作符(!)、合并空值运算符(??)
//?可选值,代表该对象可能为空。!代表一定有值
var str1 :String?
//如果这个对象真的为空的话,会面的点语法君不会调用
print("str1的长度是:\(str1?.characters.count)")
//解包方法1:??即合并空值运算符,其作用等于 if(a != nil) {a!}else{b}
print("str1的长度是:\(str1?.characters.count ?? 0 + 1)")
str1 = "test"
//即使给str赋值后,使用str1也无法直接使用,需要解包
print("str1的内容是:\(str1)")
//解包方法2:可以用!来解包,前提是我们知道此时对象一定有值。否则会Carsh
print("str1的长度是:\(str1!.characters.count + 1)")
//解包方法3:还可以用if let 语法来解包
if let tempStr1 = str1 {
//这个写法的在尾闭包函数中,千万不要去使用str1
print("tempStr2的长度是:\(tempStr1.characters.count + 1)")
}
//解包方法4:隐式解包,在变量声明时,就包含非空操作符
//这种拆包方式多用于类的属性
var str2 :String!
输出结果:str1的长度是:nil
输出结果:str1的长度是:1
输出结果:str1的内容是:Optional("test")
输出结果:str1的长度是:5
输出结果:tempStr2的长度是:5
for循环的几种书写方式
let testArr = [9,7,3,8,5,2,1,0,6]
//方法1:enumerated()函数
for num in testArr.enumerated() {
//每个元素均为Element类型的对象
print("打印信息:index:",num.offset,"value:",num.element)
}
//方法2:还是enumerated()函数
for (index, value) in testArr.enumerated() {
print("打印信息_\(index): '\(value)'")
}
//方法3:...表示闭区间,如:0...10,表示0至10并且包含10
for i in 0...(testArr.count-1) {
print(testArr[i])
}
//方法4:..<表示开区间,如:0..<10 表示0至9,不包含10
for i in 0..<testArr.count {
print(testArr[i])
}
//方法5:stride语法 from:开始位置 by:index每次增加 to:结束位置,开区间,不包含
for i in stride(from: 0, to: testArr.count, by: 1) {
print("stride:",testArr[i])
}
//方法6:stride语法 from:开始位置 by:index每次增加 through:结束位置,闭区间,包含
for i in stride(from: 0, through: testArr.count - 1, by: -1) {
print("stride:",testArr[i])
}
//方法7:stride语法反向遍历 把by设置为负的
for i in stride(from:testArr.count - 1, through:0 , by: -1) {
print("stride反向:",testArr[i])
}
//方法8:reversed()函数反向遍历
for s in testArr.reversed() {
print("reversed()函数反向遍历",s)
}
//方法9:先获取索引,再反向遍历
for (index, value) in testArr.enumerated().reversed() {
print("反向索引_\(index): '\(value)'")
}
switch的用法以及case条件的多样化
//在OC中,case的条件一般只能为int类型
//但在Swift中case可以千变万化
let testStr = "你好,世界"
switch testStr {
case "大家好":
print("Add some raisins and make ants on a log.")
//可以写多个值,但这些值需要是同种书写方法
//比如 case "你好", let z where z.hasSuffix("世界") 就不能通过
case "你好", "世界":
print("That would make a good tea sandwich.")
//如果testStr是以"世界为结尾"
case let z where z.hasSuffix("世界"),let z where z.contains("我"):
print("这个语句以'世界'为结尾")
//switch默认不会发生穿透,需要穿透时主动选择关键字fallthrough
//但fallthrough关键字下方的case不可以再使用 case let语法
fallthrough
print("由于fallthrough关键字,所以这句不会打印")
//同时由于fallthrough关键字,这个case即使不符合要求也会执行其中类容
case _ as Int://如果testStr是int类型
print("虽然不符合要求,但由于fall through关键字,依然会被打印")
default:
print("最终default")
}
输出结果:这个语句以'世界'为结尾
输出结果:虽然不符合要求,但由于fall through关键字,依然会被打印
//支持区间运算符
let i = 15
switch i {
case 0 ..< 10:
print("数字在0~9内")
case 10 ... 20:
print("数字在10~20内")
default:
print("数字不在目标范围内")
}
//支持元组
let request = (10,"success")
switch request {
case (1, "false"):
print("第一元素为1,第二元素为false")
//在用元组的同时依然可以用区间运算符
// "_"符号代表忽略该项,该项为什么都没有关系
case (1..<10, _):
print("第一元素为在0到10内,第二元素忽略")
//支持let语法,只要第二个元素有值,就满足要求
//let state 其实就等价于常见的 let state = state
case (1...10, let state):
print("第一元素为在0到10内,",state)
//支持额外的判断逻辑
case let(number,state) where state != "success" || number == 4:
print("支持额外的逻辑判断")
//如果case中包含的条件涵盖所有可能,可以不写default,否则必须写
case (let errorCode, _):
print("没找到合适的",errorCode)
}
输出结果:数字在10~20内
输出结果:第一元素为在0到10内, success
while 和 repeat while
var n = 2
while n < 2 {
n = n * 2
}
print(n)
var m = 2
//repeat while 其实就相当于与do while语句,至少会执行一次
repeat {
m = m * 2
} while m < 2
print(m)
输出结果:2
输出结果:4
模式(Pattern)和 if 语句 以及 if case 语句
/*
swift中一共有8中模式(Pattern)
通配符模式(Wildcard Pattern)
标识符模式(Identifier Pattern)
值绑定模式(Value-Binding Pattern)
元组模式(Tuple Pattern)
枚举Case模式(Enumeration Case Pattern)
可选模式(Optional Pattern)
类型转换模式(Type-Casting Pattern)
表达式模式(Expression Pattern)
他们全部可以用于switch case中,也可用于if case中
*/
var tempStr :String?
tempStr = "test"
//常规判断tempStr是否为空的写法是这样的
if tempStr != nil {
let newStr = tempStr!
let len = newStr.characters.count
print(len)
}
//但Swift中有Optional Binding语法,也就是如下的简写方式
if let newStr = tempStr {
print(newStr.characters.count)
}
var tempInt = 3
//传统用法
if tempInt>=6 && tempInt < 12 {
}
//Swift中有枚举case模式(Enumeration Case Pattern)
//if case语句等价于只有1个case的switch语句
if case 6..<12 = tempInt {
}
//对于带?的可选值对象,swift中有可选模式(Optional Pattern)
var tempInt2 :Int?
//传统用法
if let x = tempInt2, x>=6 && x < 12 {
}
//等价于上面的传统用法
if case .some(6..<12) = tempInt2 {
}
//可选值模式示例2
let ages: [Int?] = [nil, 2, 3, nil, 5]
for case let age? in ages {
//输出: 2 3 5
print(age)
}
//类型转换模式(Type-Casting Pattern)
var t : Any = 10
//swift中is关键字用来对对象的类型进行判断
if case is Int = t {
print("bingo")
}
if t is Int {
print("bingo")
}
//除了is,还有as关键字,以及延伸的 as! 和 as?
//as用于进行类型转换,用于子类向上转换为超类
//在switch case语句中,as也可用于判断对象类型
//as!表示强制进行子类转超类的类型转换,前提是你确定能成功。比如,某个超类对象中储存的实际是一个子类对象数据
//as?则用于你不确定子类转超类能否成功的时候,不成功返回nil。同样,即使成功也需要解包。
//如果可以确定100%能转换成功就使用as!, 否则使用as?
缺省参数名
//常规方法写法
func greet(person: String, day: String) -> String {
return "Hello \\(person), today is \\(day)."
}
//调用时需要加上参数名
greet(person: "Bob", day: "Tuesday")
//在定义时,参数名前加上下划线,那么调用时就需要使用缺省参数名的方式
func greet1(_ person: String,_ day: String) -> String {
return "Hello \\(person), today is \\(day)."
}
//调用时不能写参数名
greet1("Bob","Tuesday")
定义未知数量的参数
//方法在定义时可以用...替代未知数量的同类型参数
func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
print(sumOf())
print(sumOf(numbers: 42, 597, 12))
方法嵌套
//方法函数中可以嵌套方法函数,俄罗斯套娃
//这个功能主要可以做一些方法切割的事情,OC中不能这么操作
func returnFifteen() -> Int {
var y = 10
//add属于returnFifteen中的嵌套方法
func add() {
y += 5
}
//方法内部定义方法,生命周期为方法
add()
return y
}
returnFifteen()
函数方法可以作为参数也可以作为返回值
//这个方法的返回之是一个函数
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
//返回函数构造体
return addOne
}
//接收返回的函数构造体
var increment = makeIncrementer()
increment(7)
//函数除了可以做返回值,当然也可以作为参数
//这点OC中也能实现,但需要用SEL来包装
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
//把外部的一个函数方法作为参数传入到hasAnyMatches方法中
hasAnyMatches(list: numbers, condition: lessThanTen)
get 、set 、willSet
class TestClass {
var _perimeter: Double?
//在声明属性的时候直接写它的getter、setter方法
var perimeter: Double {
get {
//_perimeter需要手动声明并手动绑定
if _perimeter != nil {
return _perimeter!
}else{
_perimeter = 5.0
return _perimeter!
}
}
set {
//newValue是默认的代参名
_perimeter = newValue
}
}
var perimeter2: Double = 0.0 {
//观察属性
willSet {
//当给 perimeter2 赋值时把新值同步给 perimeter
perimeter = newValue
}
}
}
枚举类型
enum Rank: Int { // Int 设置枚举值的类型
// 定义枚举值设置值
case ace = 1
// 可以case 后面一次定义多个枚举值
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
//枚举中可以定义函数
func simpleDescription() -> String {
switch self { // self 就是这个枚举本身
case .ace:
return "我是ace"
case .jack:
return "我是jack"
case .queen:
return "我是queen"
case .king:
return "我是king"
default:
return String(self.rawValue)
}
}
}
//调用
print(Rank.ace)
print(Rank.ace.simpleDescription())
//使用枚举所代表的值,需要使用.rawValue方法
print(Rank.ace.rawValue)
输出结果:ace
输出结果:我是ace
输出结果:1
结构体
//定义结构体和定义函数方法非常相似
struct People {
var name: String
var age: Int
// 结构体内可以定义方法
func simpleDescription() -> String {
return "这个人名叫: \(name),年龄是 \(age)"
}
}
//唯一区别是结构体实例化时不需要init关键字
let onePeople = People(name: "张三", age: 18)
print(onePeople.simpleDescription())
协议
protocol ExampleProtocol {
//协议中可定义属性,但必须明确指定该属性对外是否可读写
var simpleDescription: String{get set}
//使用mutating关键字后 结构体或者枚举类型如果遵循协议,可以在有mutating修饰符修饰的方法中对于协议中的属性进行修改
mutating func adjust() -> String
}
//协议是可以继承的
protocol SonProtocol: ExampleProtocol {
var good: Bool { get }
}
//如果协议中有可选实现的方法,那么需要用到@objc和optional
//但是这么使用这个协议就属于OC对象了,将不能实现能多Swift特性,比如继承父协议
//自定义的协议要用optional属性只有这么一个办法
@objc protocol SpecialProtocol {
//可选实现方法
@objc optional func test(name:String,type:Int)
}
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "遵循协议的类."
var anotherProperty: Int = 69105
func adjust()-> String {
//string还可以用这种运算符来实现变化
simpleDescription += "多打印一段。"
return simpleDescription
}
}
struct SimpleStructure: SonProtocol {
var simpleDescription: String = "一个默认的结构体。"
var good: Bool = true
mutating func adjust() -> String {
//string还可以用这种运算符来实现变化
simpleDescription += "多打印一段。"
return simpleDescription
}
}
//调用
var classValue: ExampleProtocol = SimpleClass();
//输出:遵循协议的类.
print(classValue.simpleDescription)
//输出:遵循协议的类.多打印一段。
print(classValue.adjust())
//结构体虽然继承的是子协议,但用遵循父协议的对象来接收,也没问题
var structValue: ExampleProtocol = SimpleStructure();
//输出:一个默认的结构体。
print(structValue.simpleDescription)
//输出:一个默认的结构体。多打印一段。
print(structValue.adjust())
//输出:一个默认的结构体。多打印一段。多打印一段。
print(structValue.adjust())
//这操作相当于重置
structValue.simpleDescription = "一个遵循子协议的结构体。"
//输出:一个遵循子协议的结构体。多打印一段。
print(structValue.adjust())
扩展(实际是OC中的Category+ Extension,且全局生效)
protocol StringProtocol {
var stringValue: String { get }
mutating func adjust()
}
//Swift中的拓展实际上是OC中分类(Category)和类扩展(Extension)的结合体
//Swift中没有Category的概念,一个拓展形成后在全局都会生效
//可以为目标类添加原来没有的方法,也可以让目标类遵循某个协议
//可以为目标类添加原来没有的方法,也可以让目标类遵循某个协议
extension Int: StringProtocol {
var stringValue: String {
return "\(self)"
}
//因为有mutating修饰符,所以甚至可以改变自己的值
mutating func adjust() {
self += 42
}
mutating func reset() {
self -= 42
}
}
var intObject = 7;
intObject.adjust()
//输出49
print(intObject.stringValue)
intObject.reset()
//输出7
print(intObject.stringValue)
异常捕获
//创建一个枚举类型
enum SimpleError: Error {
case errorTooBig
case errorTooSmall
func desc() -> String {
switch self {
case .errorTooBig:
return "Too Big"
case .errorTooSmall:
return "Too Small"
}
}
}
//定义异常捕获方法
func send(age: Int) throws -> String {
if age <= 0 {
throw SimpleError.errorTooSmall
}
if age >= 100 {
throw SimpleError.errorTooBig
}
return "年龄符合预期"
}
//异常捕获调用
do {
let printerResponse = try send(age: -1)
print(printerResponse)
} catch SimpleError.errorTooBig {
print("这个人年龄太大了")
} catch let error as SimpleError {
//输出:Too Small
print(error.desc())
} catch {
print(error)
}
if let语法的内部实现推测
var baseInt:Int?
baseInt = 3
//这种写法,虽然有编译警告,但可以执行成功
//报错信息:Explicitly specified type 'Int?' adds an additional level of optional to the initializer, making the optional check always succeed
if let tempInt:Int? = baseInt {
print(tempInt)
}
//用我们以前认为的if let语句内部实现来还原,可以成功还原
if baseInt != nil {
let tempInt:Int? = baseInt!
print(tempInt)
}
var baseStr = "3.3"
if let tempInt :Int? = Int(baseStr){
//输出"nil",虽然预料到了,但依然非常奇怪
print(tempInt)
}
//用我们以前认为的if let语句内部实现来还原,会发现执行结果完全不同
if Int(baseStr) != nil {//根本就不会进入这个if语句中
//即使进来了, Int(baseStr)!强制解包会发生崩溃
let tempInt:Int? = Int(baseStr)!
print(tempInt)
}
/*
根据报错信息:Explicitly specified type 'Int?' adds an additional level of optional to the initializer, making the optional check always succeed
如果向初始值添加额外的可选类型,可选检查将恒定成功
if let是一个解包语句的写法,但在解包之前,编译器会对接收解包结果的变量类型做判断
如果是非可选值类型,执行解包;如果是可选值类型,直接赋值,并认为解包结果为true
所以if let内部实现并不似我们想象的那么简单
我们推导的if let 还原写法仅仅是与其内部真正的实现相似
*/