Swift语法精讲:从基础到进阶——一个老码农的碎碎念
上个月地铁10号线上,我靠着扶手眯着眼刷Swift文档,旁边一个实习生模样的小伙子探头看了眼我的屏幕,小声问:“哥,您这年纪还写iOS啊?”
我当时差点把手机扔他脸上——35岁怎么了?老子还在用Swift给公司搞双11大促首页呢!
不过说真的,自从去年被领导“建议”接手公司那个快十年的老iOS项目后,我才真正重新拾起Swift。以前总觉得Obj-C才是正统,结果现在看那些.m文件就像看甲骨文。
今天这篇不是什么官方教程,纯粹是一个被deadline追着跑、靠ChatGPT续命、每天通勤两小时的老程序员在实战中踩坑又爬出来的经验总结。如果你也正在被产品经理催着加“再改一版UI就上线”的需求,或者刚被App Store审核拒了三次,希望下面这些内容能帮你省点头发。
为啥突然要深挖Swift?
这事得从去年双11说起。我们那个老项目用的是Swift 4.x,UI还是纯Storyboard拖出来的,测试同学每次提Bug都说“这页面转场卡得像PPT”。领导拍板:重构!用SwiftUI重做核心流程!
我内心OS:你咋不上天?但嘴上只能回“好的收到”。于是开始了白天改需求、晚上啃Swift文档的日子。最开始连@State和@Binding都分不清,写个列表滚动直接内存爆炸,Xcode报错EXC_BAD_ACCESS时真想砸电脑。
但慢慢摸下来发现,Swift这语言,越用越香。尤其是配合Rust最近学的ownership思想(别笑,我确实在同时啃两个新东西),对值类型、不可变性这些概念理解更深了。
基础不牢,地动山摇:几个你以为会其实不会的点
1. Optional 到底是个啥?
新手总以为Optional就是“可能为nil”,但没理解它本质是个枚举:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
这意味着你根本不能直接拿String?去拼字符串,否则编译器直接给你红脸。以前我总写:
let name: String? = "老张"
print("Hello, \(name)") // 编译报错!
后来才学会用??、if let、guard let这些安全解包姿势。现在我的代码里基本看不到强制解包!了——除非是IBOutlet,毕竟Storyboard绑定的东西不可能为nil(吧?)。
2. 值类型 vs 引用类型:别再乱传class了!
Swift里Struct是值类型,Class是引用类型。这点在多线程或状态管理时特别重要。比如你有个用户模型:
struct User {
var name: String
var avatarURL: URL
}
// 在ViewModel里修改
var currentUser = User(name: "老王", avatarURL: someURL)
currentUser.name = "老李" // 不会影响其他持有该User的地方
但如果你用class:
class User {
var name: String
init(name: String) { self.name = name }
}
let userA = User(name: "老王")
let userB = userA
userB.name = "老李"
print(userA.name) // 输出"老李"!因为指向同一个实例
在SwiftUI里,强烈建议Model用Struct。不然状态更新会乱套,View刷新莫名其妙失效。我上周就因为一个class model导致列表选中状态全错,debug到凌晨两点。
进阶玩法:让代码更Swift-y
1. Result<T, Error> + async/await:告别回调地狱
以前网络请求嵌套三层回调,代码长得像意大利面条。现在用async/await,清爽多了:
func fetchUserProfile() async throws -> User {
let (data, _) = try await URLSession.shared.data(from: profileURL)
return try JSONDecoder().decode(User.self, from: data)
}
// 调用
Task {
do {
let user = try await fetchUserProfile()
DispatchQueue.main.async {
self.currentUser = user
}
} catch {
print("加载失败: \(error)")
}
}
注意:别忘了切回主线程更新UI!虽然SwiftUI自动处理部分刷新,但涉及复杂状态还是手动dispatch保险。
2. Property Wrapper:自己造轮子也挺爽
除了系统提供的@State、@Published,你也可以自定义。比如我写了个@UserDefaultsBacked,自动同步到本地存储:
@propertyWrapper
struct UserDefaultsBacked<Value> {
let key: String
let defaultValue: Value
var wrappedValue: Value {
get {
return UserDefaults.standard.object(forKey: key) as? Value ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
// 使用
@UserDefaultsBacked(key: "isDarkMode", defaultValue: false)
var isDarkMode: Bool
这样在Settings页面切换主题,其他View自动响应,不用再写一堆通知或者单例。
实战避坑指南:血泪换来的经验
审核被拒?可能是这些Swift细节没注意
- 隐私描述缺失:如果你用了
CLLocationManager,即使只是测试,也要在Info.plist里加上NSLocationWhenInUseUsageDescription,否则App Store直接拒。 - 后台模式滥用:别随便开
background fetch,除非真有推送同步需求。我们有一次因为加了个无意义的后台任务,被审核团队问了三天。 - Swift包兼容性:用Swift Package Manager引入第三方库时,注意最低iOS版本。有些库只支持iOS 13+,但你项目支持iOS 11,Xcode不报错,但上传时Archive会失败。
性能陷阱:别让Swift“优雅”拖慢App
避免在View body里做计算:SwiftUI的body会被频繁调用,如果在里面写
map.filter.sort,列表滑动直接掉帧。// 错误示范 var body: some View { List(items.filter { $0.isFavorite }) { item in ... } } // 正确做法:提前计算好 private var favoriteItems: [Item] { items.filter { $0.isFavorite } }慎用
@StateObjectvs@ObservedObject:前者只在View首次创建时初始化,后者每次父View刷新都会重建。用错会导致ViewModel重复请求数据。
资源推荐:别再瞎搜了
| 类型 | 推荐 | 说明 |
|---|---|---|
| 官方文档 | Swift.org | 语法权威,但有点干 |
| 中文社区 | SwiftGG翻译组 | 很多高质量译文 |
| 视频教程 | Sean Allen的YouTube频道 | 实战向,幽默风趣 |
| 开源项目 | Apollo iOS | 看大厂怎么组织Swift项目 |
| 调试工具 | InjectionIII | 热重载Swift代码,省去反复编译 |
我自己最常用的是Swift Playground + ChatGPT组合。遇到奇怪语法,直接扔给Claude:“用Swift写个带泛型的缓存类,支持LRU淘汰”,5秒出原型,再自己调整。效率比翻Stack Overflow高多了(别打我,我知道应该自己思考……)
最后几句心里话
写Swift这几年,最大的感悟是:语言只是工具,解决问题才是目的。
我见过用Swift写出屎山代码的,也见过用Obj-C写出优雅架构的。关键不是语法多炫酷,而是能不能让App稳定、让用户流畅、让自己下班。
上周五晚上,我终于把重构后的首页提交TestFlight,产品经理发来消息:“这次滑动真的丝滑!” 那一刻,觉得通勤一小时、加班到九点都值了。
所以,别管35岁还是25岁,只要键盘还在敲,代码还能跑,咱们就还是程序员。
Swift也好,Rust也罢,学就完了。反正,头发已经不多了,不如多写点好代码。
PS:如果你也在北京挤地铁写代码,评论区留个言,说不定哪天咱俩在国贸站撞见,还能一起吐槽今天的CI又挂了 😅

评论 0