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 还原写法仅仅是与其内部真正的实现相似
 */
上一篇下一篇

猜你喜欢

热点阅读