美文网首页
2020.6.14 学习笔记

2020.6.14 学习笔记

作者: Mi欧阳 | 来源:发表于2020-06-11 09:25 被阅读0次

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

    相关文章

      网友评论

          本文标题:2020.6.14 学习笔记

          本文链接:https://www.haomeiwen.com/subject/zroutktx.html