第三章:Swift语言基础(二)——运算符、字符串与可选类型

好,咱们接着往下走。上一章我们把变量、常量、基本数据类型都捋了一遍,算是打好了地基。这一章要聊的,是Swift里几个特别“日常”但又特别容易踩坑的东西——运算符、字符串处理,还有那个让新手又爱又恨的可选类型

我个人觉得,可选类型是Swift区别于其他语言最显著的一个特征。你想想看,一个变量可能“有值”,也可能“没值”,这在OC或者Java里通常用nil或者null来表示,但一不小心就会崩溃。Swift用了一套更安全的机制来处理它。嗯,咱们慢慢聊。

3.1 基本运算符:不只是加减乘除

运算符这块,大部分跟其他语言差不多。但有几个细节,我建议你留意一下。

3.1.1 赋值运算符与算术运算符

let a = 10
var b = 5
b = a   // b 现在是 10

let sum = a + b      // 20
let diff = a - b     // 0
let product = a * b  // 100
let quotient = a / b // 1(整数除法,结果还是整数)

这里有个坑:整数除法会直接截断小数部分。我在项目中遇到过好几次,新手算平均值时直接用Int相除,结果丢了精度。如果你想要小数结果,记得把操作数转成Double。

let exact = Double(a) / Double(b)  // 1.0,但这里a和b相等所以不明显
let c = 7, d = 3
let result = Double(c) / Double(d) // 2.3333...

3.1.2 比较运算符与三元运算符

比较运算符返回Bool值,这个没什么好说的。但三元运算符 ? : 我建议你谨慎使用——不是说不能用,而是嵌套多了代码可读性会变得很差。

let age = 20
let canDrive = age >= 18 ? "可以开车" : "再等等"

这种简单的场景用三元运算符很清爽。但如果你写成 a ? b ? c : d : e 这种,相信我,过两周你自己都看不懂。

3.2 字符串插值与拼接

字符串操作在iOS开发里太频繁了。显示用户名字、拼接网络请求参数、格式化日志……几乎每个页面都会用到。

3.2.1 字符串拼接

最直接的方式就是用 + 号:

let firstName = "张"
let lastName = "三"
let fullName = firstName + lastName  // "张三"

也可以用 += 来追加:

var greeting = "你好"
greeting += ",世界"  // "你好,世界"

3.2.2 字符串插值

这个才是Swift的亮点。用 \() 把变量或表达式直接嵌入字符串中:

let name = "小明"
let age = 25
let message = "我叫\(name),今年\(age)岁"
// 输出:"我叫小明,今年25岁"

插值里甚至可以放表达式:

let price = 9.9
let count = 3
let total = "总价:\(price * Double(count)) 元"
我的小习惯:能用插值就别用拼接。插值更直观,也不容易漏掉空格。我在写网络请求的URL时,经常用插值来组装参数,比OC里的stringWithFormat清爽多了。

3.3 可选类型(Optional)详解

好,重头戏来了。可选类型是Swift用来表示“可能有值,也可能没值”的一种包装。说白了,就是一个盒子——盒子里可能有东西,也可能是空的。

3.3.1 为什么需要可选类型?

你想想看,一个变量如果可能为nil,那你在使用它之前必须检查。OC里你忘了检查,直接给nil发消息,虽然大部分情况下不会崩溃,但一旦遇到特殊情况……嗯,我曾经在定位一个闪退问题时,查了半天发现是一个NSString没初始化就拿来拼接了。

Swift从语言层面解决了这个问题:普通变量不允许为nil,只有可选类型才能为nil

var normalString: String = "Hello"
// normalString = nil  // 编译错误!

var optionalString: String? = "Hello"
optionalString = nil  // 没问题

看到那个问号 ? 了吗?它就是可选类型的标志。

3.3.2 可选类型的本质

可选类型其实是一个枚举:

enum Optional<Wrapped> {
    case none  // 没有值
    case some(Wrapped)  // 有值,包装了实际的值
}

所以 String? 本质上就是 Optional<String>。你不需要手动写这个枚举,但理解它的结构有助于你理解后面的解包操作。

3.4 可选绑定与强制解包

既然值被“包装”起来了,那怎么取出来用呢?有两种方式:强制解包和可选绑定。

3.4.1 强制解包(不建议滥用)

! 来强制取出值:

var maybeNumber: Int? = 42
let definitelyNumber = maybeNumber!  // 42

但如果 maybeNumber 是nil,这行代码会直接崩溃。我在刚学Swift时就犯过这个错——从服务器取了个字段,以为一定有值,结果后台返回了null,app直接闪退。

警告:强制解包只在你100%确定有值的时候使用。比如从Storyboard拖出来的IBOutlet,系统保证它会在viewDidLoad之前初始化好,这时候用 ! 是安全的。其他情况,尽量别用。

3.4.2 可选绑定(推荐方式)

if letguard let 来安全地解包:

var userName: String? = "张三"

// if let 方式
if let name = userName {
    print("用户名为:\(name)")
} else {
    print("用户名为空")
}

// guard let 方式(常用于函数开头)
func greet(user: String?) {
    guard let name = user else {
        print("没有用户,无法打招呼")
        return
    }
    print("你好,\(name)")
}

if letguard let 的区别在于:if let 只在花括号内能使用解包后的值;guard let 则让解包后的值在后续代码中都可以使用,但必须搭配 returnbreak 等语句提前退出。

我个人更倾向于用 guard let,因为它能减少嵌套层级,让代码更扁平。你想想看,如果连续解包三个可选值,用 if let 会变成三层嵌套,而 guard let 只需要三个连续的守卫语句。

3.4.3 多个可选绑定

Swift允许在一个 if let 中同时解包多个值:

let age: Int? = 25
let city: String? = "北京"

if let a = age, let c = city {
    print("年龄:\(a),城市:\(c)")
}

还可以加上条件判断:

if let a = age, a > 18 {
    print("已成年")
}

3.5 nil合并运算符

这个运算符 ?? 是我在项目中用得最多的一个。它的作用很简单:如果可选值有值,就取出来;如果为nil,就使用一个默认值。

let nickname: String? = nil
let displayName = nickname ?? "游客"
// displayName 是 "游客",因为 nickname 为 nil

let realName: String? = "李四"
let showName = realName ?? "游客"
// showName 是 "李四"

说白了,?? 就是 if let 的简化版。上面这个例子如果用 if let 写:

let displayName2: String
if let name = nickname {
    displayName2 = name
} else {
    displayName2 = "游客"
}

你看,三行代码变成了一行。而且 ?? 可以链式使用:

let a: String? = nil
let b: String? = nil
let c: String? = "最终值"
let result = a ?? b ?? c ?? "兜底值"
// result 是 "最终值"
避坑指南:我曾经在写UI展示逻辑时,用 ?? 给UILabel赋值,但忘了考虑空字符串的情况。比如用户昵称可能是空字符串 "",它不是nil,所以 ?? 不会触发默认值,结果界面上就显示了一个空标签。所以,?? 只处理nil,不处理空字符串。如果你需要同时处理nil和空字符串,建议用 if let 加额外判断。

3.6 综合示例:用户信息展示

咱们把这一章的知识点串起来,写一个简单的用户信息展示逻辑:

// 模拟从服务器获取的数据(可能为nil)
let serverNickname: String? = "小明"
let serverAge: Int? = nil
let serverCity: String? = "上海"

// 使用 nil 合并运算符提供默认值
let displayNickname = serverNickname ?? "匿名用户"
let displayAge = serverAge ?? 0
let displayCity = serverCity ?? "未知城市"

// 使用字符串插值拼接展示信息
let profile = """
用户信息:
昵称:\(displayNickname)
年龄:\(displayAge)
城市:\(displayCity)
"""

print(profile)
// 输出:
// 用户信息:
// 昵称:小明
// 年龄:0
// 城市:上海

这个例子虽然简单,但涵盖了可选类型、nil合并运算符、字符串插值这几个核心知识点。你在实际开发中,几乎每天都会写类似的代码。

3.7 本章小结

这一章的内容,说白了就是三件事:

  • 运算符:注意整数除法的截断,三元运算符别嵌套太深
  • 字符串:多用插值少拼接,代码更清晰
  • 可选类型:这是Swift的“护身符”,用 if letguard let 安全解包,用 ?? 提供默认值,尽量别用强制解包

下一章我们会聊集合类型——数组、字典、集合。这些东西在管理数据时特别有用。到时候我会分享一些我在处理列表数据时的经验,咱们下次见。