[swift 进阶]读书笔记-第四章:可选值 1_3 序列_魔法数问题_可选值概览

可选值

4.1哨岗值 Sentinel Value


定义:有的函数可能返回nil 当函数返回nil 我们将返回值 用作-1 等类似的值代替,这样的值就被称为"哨岗值" 也叫魔法数。

4.2通过枚举来解决魔法数的问题


swift中Optional的本质是一个枚举(目前可以先不具体思考,等以后的项目中慢慢就领悟到了)。

enum Optional<Wrapped> {
    case none //为空
    case some(wrapped) // 有值
}
复制代码

知识点:当返回值为可选值 ? 时,实质上返回的是一个Optional的枚举

    ///返回值为Optional的实质
    let strArray = ["one","two","three"]
    switch strArray.index(of: "four") {
    case .none:
        print("返回值为空") //返回值为空
    case .some(let idx):
        print(idx)
        print("有值")
    }
复制代码

4.3可选值概览:Optional

if let

相信大家在swift 入门的时候就已经能很好的使用这个语法了。 不赘述。

技巧1: 可与Bool 联合判断
if let obj = Optional , Bool {
    /// 前面有值且后面Bool为true执行
}
复制代码
技巧2: 多let 并列。 这个在开发中也经常用到
if let obj1 = Optional1, let obj2 = Optional2, ....{
///当上面的let判断都有值的时候执行
}
复制代码

while let

类似于if let 遇到nil时终止循环 也有和上面?技巧1(一旦为false 就循环停止,并不是filter那样去工作)相同的使用方法。不赘述。

    while let line = printLine() {
    ///当printLine为空时候终止循环
        print(line)
    }
复制代码

双重可选值:


一个可选值可能会被另外一个可选值包装起来,形成了可选值的嵌套。你也可以理解成obj?? 。平时在复杂逻辑操作时注意一哈就可以了。

if var 和 while var

和 let 的使用类似。 但你var 出来的obj实际上是一个本地副本 你对obj做任何更改都不会影响原来的值。

 ///if var 的改变不影响原值
        let dict:[String:String]?
        dict = ["key1":"value1"]
        if var varDict = dict {
            varDict["key1"] = "value2"
        }
        print(dict) //Optional(["key1": "value1"])
复制代码

解包后可选值的作用域

if let obj = Optional {
///obj的作用域
}
复制代码
注:有时候我们需要提前退出来避免烦人的if嵌套,这个时候 guard let 的优点马上就体现出来了.
///为空提前退出
guard let obj = Optional else{return}
复制代码

可选链

OC中对nil发送消息,nothing had happened。 在swift中可以通过可选链(Optional chaining)达到一样的效果

那么问题来了,什么是可选链? 可选链 中的可选是?,可以把链理解成swift中的点语法,可以在《swift链式编程》一书中对这个有更深的理解

delegate?.callback() //就是一个可选链。

//当上面的delegate为可选时编译器会发出警告。 确实有值方法会被调用。没有值时候方法不会被调用。
复制代码

ps. ?写了这么多年swift的delegate和闭包 直到自己写笔记的时候才发现自己写复杂了(一点swift的优点都没体现出来。。。)

var voidCallback:(()-> Void)?
    
    ///对于闭包调用的两种写法
    //❌不推荐
    if voidCallback != nil {
        voidCallback!()
    }
    
    //✅ 推荐
    voidCallvack?()
复制代码
多个可选链的调用。下面这个例子简单易懂,不赘述。
extension Int {
    
    var half: Int? {
        guard self > 1 else {return nil}
        return self/2
    }
    
}
///多可选链调用
20.half?.half?.half //Optional(2)
复制代码

nil合并运算符 ?? (项目中经常会用到!!可以让你的代码写的很优美~)


一开始看到标题nil合并运算符的时候我是?的。这都是啥啥啥啊。 当我看到 ?? 这个符号时候豁然开朗。两个是一个意思 项目里面已经用过很多次了,百试不爽。

使用场景: 当去解包!一个可选值,且当这个可选值为nil时,我们要去赋一个默认值防止崩溃 这个时候就用到了 ??

    var number: Int?
    number = nil
    String(number ?? 5) //5 
复制代码

感觉上是和OC 中的 三目运算符 Bool ? A: B 类似 需要强调的是可选值不是指针

可选值map

可选值flatMap(Demo值得借鉴)

上面两小节讲的都是map 和flatMap对于可选值的一个用法。 有一个小demo对于日常开发可以说是有着四两拨千斤的感觉,如下:

    ///将一个网络图片加载出来的奇淫方法!!!
    let urlString = "http://www.objc.io/logo.png"
    let view = URL(string: urlString)
        .flatMap { (url) -> Data? in
        try? Data(contentsOf: url)
        }
        .flatMap { (data) -> UIImage? in
            UIImage(data: data)
        }
        .map { (image) -> UIImageView in
            UIImageView(image: image)
    }
    
    if let view = view {
        UIView().addSubview(view)
    }
复制代码

利用swift中的$0语法简化上面的代码

    let view2 = URL(string: urlString)
        .flatMap {
            try? Data(contentsOf: $0)
        }
        .flatMap {
            UIImage(data: $0)
        }
        .map {
            UIImageView(image: $0)
    }
    
    if let view2 = view2 {
        UIView().addSubview(view2)
    }
复制代码

这里的flatMapif let 非常相似。 这样我们就将一个网络图片加载出来了。 想想自己之前写了那么多的垃圾代码,不禁长叹一句:swift??!!!

使用flatMap过滤nil

同样这里我们用一个demo去说明用法

目的:我们想要求一个字符串数组中的数字和。

    let numbers = ["1","2","3","4","liaoworking"]
    ///普通青年
    var sum = 0
    for case let i? in numbers.map({
        Int($0)
    }) {
        sum += i// Int($0)为nil就不走这里了
    }
    //        sum的值为10
    
    ///优质青年
    ///当我们用?? 把nil替换成0
    numbers.map { Int($0) }.reduce(0) { $0 + ($1 ?? 0)} //10
    
    ///文艺青年
    ///在标准库中flatMap的作用可能正是你想要
    numbers.flatMap { Int($0) }.reduce(0, +) // 10
复制代码

注:这里的flatMap的作用:把一个映射为可选值的序列进行展平。

这个例子的确好吊,不自己写一遍不知道还有很多高级用法自己不会。 学习了学习了~

如果让我去面试swift开发。一定会选这个demo做一个面试题!

可选值判等

判等时我们并不关心可选值是不是nil,只用去管它是否包含我们想要的非nil的特定值即可。

switch case 匹配可选值

/// switch case
    let numbers = ["1","2","3","4","liaoworking"]
    for i in numbers {
        switch Int(i) {
        case 0:
            print("0")
        case nil:
            print("can't convert to int")
        default:
            print("not in case")
        }
    }
复制代码

swift中case匹配是通过~=运算符进行的。 我们可以实现这个方法增加对范围(0..<num)匹配的方法

###可选值的比较 和 == 类似 可选值曾经也有 <=, >=, <, >操作符 后来在SE-0121因为安全原因被移除了。

///在一个温度数组中
let temp = ["-23", "0", "66.66", "warm"]

Double("warm") 的值为nil

Double("warm") < 0 是成立的

但你不能说warm是小于0度的是很冷的。
复制代码

这显然不合情理。

文章源文件地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
接着分析 (result (type_ident (component id='Bool' bind=Swift.(file).Bool))) (brace_stmt range=[re.swift:1:59 - line:14:1] (pattern_binding_decl range=[re.swift:2:5 - line:2:33] (pattern_named type='[UInt8]' 'b') Original init: (call_expr type='[UInt8]' location=re.swift:2:19 range=[re.swift:2:13 - line:2:33] nothrow (constructor_ref_call_expr type='(String.UTF8View) -> [UInt8]' location=re.swift:2:19 range=[re.swift:2:13 - line:2:19] nothrow (declref_expr implicit type='(Array<UInt8>.Type) -> (String.UTF8View) -> Array<UInt8>' location=re.swift:2:19 range=[re.swift:2:19 - line:2:19] decl=Swift.(file).Array extension.init(_:) [with (substitution_map generic_signature=<Element, S where Element == S.Element, S : Sequence> (substitution Element -> UInt8) (substitution S -> String.UTF8View))] function_ref=single) (argument_list implicit (argument (type_expr type='[UInt8].Type' location=re.swift:2:13 range=[re.swift:2:13 - line:2:19] typerepr='[UInt8]')) )) (argument_list (argument (member_ref_expr type='String.UTF8View' location=re.swift:2:29 range=[re.swift:2:21 - line:2:29] decl=Swift.(file).String extension.utf8 (declref_expr type='String' location=re.swift:2:21 range=[re.swift:2:21 - line:2:21] decl=re.(file).check(_:_:).encoded@re.swift:1:14 function_ref=unapplied))) )) Processed init: (call_expr type='[UInt8]' location=re.swift:2:19 range=[re.swift:2:13 - line:2:33] nothrow (constructor_ref_call_expr type='(String.UTF8View) -> [UInt8]' location=re.swift:2:19 range=[re.swift:2:13 - line:2:19] nothrow (declref_expr implicit type='(Array<UInt8>.Type) -> (String.UTF8View) -> Array<UInt8>' location=re.swift:2:19 range=[re.swift:2:19 - line:2:19] decl=Swift.(file).Array extension.init(_:) [with (substitution_map generic_signature=<Element, S where Element == S.Element, S : Sequence> (substitution Element -> UInt8) (substitution S -> String.UTF8View))] function_ref=single) (argument_list implicit (argument (type_expr type='[UInt8].Type' location=re.swift:2:13 range=[re.swift:2:13 - line:2:19] typerepr='[UInt8]')) )) (argument_list (argument (member_ref_expr type='String.UTF8View' location=re.swift:2:29 range=[re.swift:2:21 - line:2:29] decl=Swift.(file).String extension.utf8 (declref_expr type='String' location=re.swift:2:21 range=[re.swift:2:21 - line:2:21] decl=re.(file).check(_:_:).encoded@re.swift:1:14 function_ref=unapplied))) ))) (var_decl range=[re.swift:2:9 - line:2:9] "b" type='[UInt8]' interface type='[UInt8]' access=private readImpl=stored writeImpl=stored readWriteImpl=stored)
06-10

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值