你可能不知道的 Swift 开发小技巧——Pt.1

2,586 阅读4分钟

这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战


引言

在我一开始写 Swift 代码时,总是显的不简洁,没有充分发挥 Swift 的特性。随着写的时间长了,也总结了一些 Swift 开发的小技巧,让代码看起来更加简洁和高大上。

内容一共分为两篇文章,我们先来看第一篇~


使用内置函数

现在有个整型数组,我们想计算数组内所有数字之和。第一反应可能就是 for 循环,于是就写出了下面的代码:

let numbers = [2021, 11, 12]
var sum = 0
for number in numbers {
    sum += number
}
print(sum)    //2044

当然,这么写也没啥毛病,但我们可以使用 reduce(_:_:)把 for 循环简化成一行:

let numbers = [2021, 11, 12]
let sum = numbers.reduce(0, +)
print(sum)   //2044

上面这段代码就等价于 0 + 2021 + 11 + 12
reduce 的第一个参数 0 是初始值,相当于上面 for 循环代码中的 var sum = 0
第二个参数接受一个闭包,这就给了我们在闭包里为所欲为的机会了。

除了reduce(_:_:),Swift 还提供了其他的内置函数,比如filter(_:)map(_:)flatMap(_:)firstIndex(where:)等等。更多的大家可以 command+shift+0 打开开发文档查看。

image.png


省略 return 关键字

如果一个函数或闭包只有一行代码,那就可以把 return 关键字省略掉。而且,如果你喜欢的话,可以把所有代码放在一行:

func makeGreeting(withName name: String) -> String {
    "求点赞, \(name)"
}

func makeGreeting(withName name: String) -> String { "求点赞, \(name)" }

简化闭包

struct Person {
    var name: String
    var age: Int
    ...
}

假如有个 Persons 数组,里面是 Person 对象,现在要筛选出大于 18 岁的 Person,用前面提到的filter(_:),可以这么写:

let person1 = Person(name: "JieJie", age: 20)
let person2 = Person(name: "Faker", age: 25)
let person3 = Person(name: "JakeyLove", age: 30)
let person4 = Person(name: "JinX", age: 10)
let persons = [person1, person2, person3, person4]

let result = persons.filter { person in
    person.age > 18
}
print(result.map{ $0.name }) //["JieJie", "Faker", "JakeyLove"]

Swift 自动为内联函数提供了参数名称缩写功能,所以上面的 filter 语句可以简化成:

let result = persons.filter{ $0.age > 18 }

检查数组是否越界

还是上面的 persons 数组,数量不固定,假如我们要对上面的 persons 数组抽奖,第 6 个人中奖,如果人数没有达到 6,那就延长抽奖日期,通常可能会这么做:

let index = 6
if index >= 0 && index < persons.count {
    print("恭喜 \(persons[index].name) 中奖")
} else {
    print("延长抽奖日期")
}

上面这个写法就显的很普通了,我们用数组的indices属性把它变得高大上:

if persons.indices.contains(index) {
    print("恭喜 \(persons[index].name) 中奖")
} else {
    print("延长抽奖日期")
}

默认值

大家都知道可以用??为可选变量设置默认值,其实在 Swift 中,还有更多的地方可以设置默认值,比如:

  • 为函数参数设置默认值
  • 为 dictionary 的某个 key 设置默认值

先来看看为函数参数设置默认值,设置后,该参数在调用时就可以省略:

func greet(name: String = "World") -> String { 
    "Hello,\(name)" 
}

greet("World!!!")  //Hello,World!!!

greet()   //Hello,World

再来看看为 dictionary 的某个 key 设置默认值。

假如有个 names 数组,里面存放的是人名,现在要写个函数返回出现次数最多的人名。一种实现方式如下:

let names = ["小张", "小李", "小张", "小李", "小张", "小王"]

func findMostCountName(inNames names: [String]) -> String {
    var occurrenceFor: [String : Int] = [:]
    for name in names {
        if let count = occurrenceFor[name] {
            occurrenceFor[name] = count + 1
        } else {
            occurrenceFor[name] = 1
        }
    }

    var maxCount = 0
    var result = ""
    for (name, count) in occurrenceFor {
        if count > maxCount {
            maxCount = count
            result = name
        }
    }
    return result
}

let name = findMostCountName(inNames: names)
print(name) //小张

再来看看给 dictionary:occurrenceFor 的 key 设置默认值的效果:

func findMostCountName(inNames names: [String]) -> String {
    var occurrenceFor: [String : Int] = [:]
    for name in names {
        occurrenceFor[name, default: 0] += 1  //如果 key 存在,返回对应的 value;否则返回 0
    }
    
    var maxCount = 0
    var result = ""
    for (name, count) in occurrenceFor {
        if count > maxCount {
            maxCount = count
            result = name
        }
    }
    return result
}

可见,给 dictionary 键设置默认值可以使代码看起来更加整洁。只需一行代码,就可以替换掉 if-else 语句。


检查变量是否在两个数之间

假如有个字段:年龄,现在要判断它是不是在 18 岁到 32 岁之间。通常我们会这么做:

let age = 23

if age >= 18 && age <= 32 {
    print("符合要求")
}

可能觉得像上面这么写没啥问题,但再来看看下面这个:

let age = 23

if age <= 18 && age >= 32 {
    print("符合要求")
}

是不是很像,但这个时候 print 语句永远也不会执行。大家在读这两个 if 语句的时候有什么感受?是不是需要在大脑中有那么一下停顿,来思考符不符合条件。这就造成不易读而且容易出错。

在 Swift 中提供了更易读、简便的方法来实现:

let age = 23

if (18...32).contains(age) {
    print("符合要求")
}

if (18...32) ~= age {
    print("符合要求")
}

结语

  • 尽量使用内置函数替代 for 循环
  • 只有一行代码的函数省略 return 关键字
  • 闭包的简化
  • 检查数组越界的方法
  • 为函数参数和字典的 key 提供默认值
  • 检查变量是否在两个数之间

今天的小技巧就到这啦~ 大家觉得有用,不妨在项目中实践一下哦~