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

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

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

首先回忆下在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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容