美文网首页程序员
Swift使用guard和throws更优雅的处理逻辑和错误

Swift使用guard和throws更优雅的处理逻辑和错误

作者: YJ_Wong | 来源:发表于2018-09-08 11:33 被阅读144次

    代码写多了就想优化,这是一个天然的过程。近期在代码优化方面积累了一些心得,会慢慢整理出来。

    本文主要适用于想要缩减代码行数及规范化逻辑和错误的场景。

    首先回忆下在OC中是如何处理错误和逻辑的,下面罗列两种常见的处理方式。
    1、方法定义返回值,根据返回值判断成功还是失败。复杂情况下定义枚举可以覆盖更多业务场景。

    + (BOOL) isFileExist:(NSString *)filePath
    {
        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL result = [fileManager fileExistsAtPath:filePath];
        return result;
    }
    

    2、通过NSError的指针写入,判断NSError不为空获取错误信息。

    NSError *error;
    BOOL success = [data writeToFile: path options: options error: &error];
    if(error) {
        // 发生了错误
    }
    

    但实际情况是,很多时候不会出什么问题,所以不少开发会图省事直接给error赋值nil。

    下面介绍Swift中高逼格的用法。也就是关键字 throwsguard的应用。

    简单描述下例子场景:编写一个方法,通过Index获取数组中的对象。
    let item = array[index]
    在一般情况下为了代码可靠性和健壮性,会做一些非空判断和逻辑判断。根据返回值来绝对本次操作是否成功。

    func getObjectByIndex(index:Int) -> Int {
            let array = [1,2,3,4,5]
            if index > 0{
                if array.count > 0{
                    if index < array.count {
                        return array[index]
                    }
                }
            }
            return -1
        }
    

    这里有三层判断,但在实际项目开发中我见过10层以上的嵌套,以至于后面版本迭代逻辑时很容易挑错在哪个代码块里。

    而且即使最后返回-1,谁也不敢保证数据源里真的有一个合法的-1被正确的返回出来了。也许有人会想到定义枚举来更细致的区分错误,但这个例子中又和返回数据冲突了。。。。

    Swift中使用guard配合throws可以很便捷的解决这个问题

    guard的知识比较基础,已经了解的同学可以跳过直接往下看。

    先说guard的用法,字面意思是守护 警卫。

    很形象的比喻:当你走进一个大门,门口一个警卫站着,看你有问题就拦下你,没问题就放你过去。

    当guard关键字后的表达式为false时,就会执行else后的代码块,否则就继续往下执行

    //条件为true,else后的代码块不会执行 
    guard 1 == 1 else { return }
    //条件为false,else后的代码块会执行
    guard 1 < 1 else { return }
    

    很好理解不是么,熟练应用后可以有效减少代码的嵌套。

    继续改造上面的例子,首先定义一些错误类型的枚举。

        enum arrayError: Error {
            case indexCrossBoard, indexLessZero, arrayIsEmpty
        }
    

    注:在Swift4.0中已经取消了ErrorType关键字。目前统一继承Error

    接下来在入参后面中加入throws关键字标记该方法,在方法体内部使用throw抛出具体的错误类型。

        func getObjByIndex(index:Int) throws -> Int {
            let array = [1,2,3,4,5]
            //when false,execute code in black after else
            guard index < array.count else { throw arrayError.indexCrossBoard }
            guard index > 0 else { throw arrayError.indexLessZero }
            guard array.count > 0 else { throw arrayError.arrayIsEmpty }
            
            return array[index]
        }
    

    可以看到值返回和错误返回已经被区分开了,当一个方法体被throws关键字标记后的方法代表它可能会向外抛出错误,这个错误可以是自定义的,可以取自上面自定义的枚举。

    有抛出就一定有接收,所以方法在被调用时会被强制加上do catch try关键字,不然编译器会报错。

            do {
                let item:Int = try getObjByIndex(index: 20)
                
            } catch arrayError.indexCrossBoard {
                print("Error of Corss board")
            } catch arrayError.indexLessZero {
                print("Error of Index less Zero")
            } catch arrayError.arrayIsEmpty {
                print("Array is Empty")
            } catch {
                
            }
    

    相比较传统的NSError处理错误,这样的规范使得开发无法漠视操作中可能会带来的错误。比如磁盘满了,但任然尝试写入文件,排查了半天又不知道哪里出问题了,身边又围了很多QA和产品,你懂的。

    而这种做法表面上看try catch一定程度上冗长了代码,但回想下我们之前处理不同的错误类型不也是要嵌套很多if判断来执行不同操作么。所以代码优化只是一定程度上的规整和增加可读性,该做的事还是少不了的。

    以上是入门级用法,我们还可以尝试进行闭包抛出错误。

        //Use throws mark Closure
        typealias ArrayErrorCallback = () throws -> Bool
        
        func checkObjectById(index:Int, errorBlock:@escaping (_ inner:ArrayErrorCallback) -> Void) {
            let array = [1,2,3,4,5]
            if index < array.count {
                // throw error
                errorBlock({ throw arrayError.indexCrossBoard })
            }
            // return value
            errorBlock({return true})
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            checkObjectById(index:20) { (inner: ArrayErrorCallback) -> Void in
                do {
                    let success = try inner()
                    print(success)
                } catch {
                    print(error)
                }
            }
        }
    

    用法差不多,只是把throws标记在闭包上。更适合异步操作的场景。

    基本就是这么多,大家可以回去翻阅下自己项目中的业务常见,看哪些地方适合这样的改动,本质上还是以适合为主,不建议强行装X。

    建了一个Swift的QQ交流群 859978655,欢迎大家加入。

    相关文章

      网友评论

        本文标题:Swift使用guard和throws更优雅的处理逻辑和错误

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