从零开始 iOS 开发:Swift 基础知识与实战踩坑记
引言:为什么我会选择学习 Swift 做 iOS 开发?

2021 年初,我作为一位后端出身的全栈开发工程师,被公司安排参与一个新项目——为某款内部使用的工具类 App 添加 iOS 端支持。那时我对 iOS 开发一无所知,只知道 Apple 的官方语言是 Swift,而且它在社区中有着不错的口碑。
说实话,刚开始接触 Swift 的时候,我有点迷茫。虽然有多年的编程经验,但面对全新的语法体系和 UIKit 框架时,还是感到些许陌生。更别说还要理解苹果生态、Xcode 的各种设置以及真机调试流程了。
于是,那段时间我一边啃官方文档,一边跟着一些视频教程动手实践,还在 GitHub 上扒开源项目,逐渐积累起了一些实际经验。这篇文章,我想以第一人称的方式,分享我入门 Swift 和 iOS 开发过程中遇到的真实挑战、关键知识点以及一些血泪教训,希望能帮到正在入门的同学。
我的第一个项目:做一个简单的待办事项(To-Do)App

公司需要的是一个轻量级的任务管理应用,用于帮助团队成员记录每日的工作计划,并能同步到后端服务做统计分析。因为 Android 端已有雏形,我们决定同时推进 iOS 版本的开发。
技术选型:
- 语言:Swift 5.4
- 界面框架:UIKit(后来也尝试了一下 SwiftUI)
- 网络请求:使用 Alamofire
- 本地存储:UserDefaults + Realm
- 架构模式:MVC(初期快速上线)
目标很清晰:3 周内完成基础功能并上线 TestFlight 测试版本。
最初的问题:
第一个问题:如何创建第一个 ViewController?
在 Xcode 创建工程时,默认给了一个 ViewController.swift 文件。当时我并不清楚这个文件是如何和 Storyboard 关联起来的,也不知道怎么自定义 UI 控件。
比如我想加一个按钮,然后点击弹出提示框,结果写了半天却不知道怎么把 button 的事件绑定到代码里。
@IBAction func addButtonTapped(_ sender: UIButton) {
let alert = UIAlertController(title: "提示", message: "你点我干嘛!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "好的", style: .default))
self.present(alert, animated: true)
}
这其实是一个最基础的交互场景,但那时候对 IBOutlet 和 IBAction 的作用不熟悉,导致花了很久才跑通。
第二个问题:Swift 的语法太“特别”了?
作为一个从 JavaScript、Python 转过来的开发者,Swift 的类型系统和语法结构起初让我非常不适。
举个例子:
- 可选类型(Optional)一开始让人头疼,比如不小心解包了一个 nil,就会 crash;
- 方法参数写法跟很多语言都不太一样;
- 枚举还支持关联值……这些都得慢慢适应。
比如这个函数签名:
func fetchData(completion: @escaping (Result<DataModel, Error>) -> Void)
我当时看到就懵了,啥是 escaping?Result 类型又是啥?闭包还能当参数传进来?
解决方案:边学边练,用真实项目来驱动学习
我意识到不能只看文档或照搬示例,必须通过项目驱动学习,所以我决定先从小功能做起:
学会布局:从 UILabel 到 Auto Layout
我的第一个完整页面是任务列表页,上面有一个 UITextField 输入框,一个 UITableView 显示数据,还有一个 UIBarButtonItem 触发添加动作。
这时候我才真正开始理解:
- 如何通过 Interface Builder 拖拽控件,或者纯代码添加;
- 什么是 Auto Layout 以及约束冲突;
- UIScrollView 的内容偏移、键盘遮挡等问题。
举个简单的代码示例:
let tableView = UITableView()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.delegate = self
tableView.dataSource = self
// addSubview
view.addSubview(tableView)
// 设置 AutoLayout
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
如果你不加 .translatesAutoresizingMaskIntoConstraints = false,你会发现视图根本不会动!这是新手常见的坑之一。
Swift 的基础知识速成
以下是一些我觉得作为入门者必须要掌握的核心知识点:
1. Optional 的正确使用
Optional 是 Swift 的灵魂之一。处理不好,你的 app 就随时可能闪退。
var name: String? = fetchName() // 返回可能是 nil
if let safeName = name {
print("名字是:$safeName)")
} else {
print("没有取到名字")
}
或者使用 guard:
guard let safeName = name else {
print("取不到名字,返回吧")
return
}
print("名字是:$safeName)")
2. 结构体 vs 类
Swift 中 struct 和 class 的区别值得单独拎出来讲。简单来说:
- Struct 是值类型(copy)
- Class 是引用类型(指针)
比如:
struct Point {
var x = 0, y = 0
}
var a = Point(x: 1, y: 2)
var b = a
b.x = 100
print(a.x) // 还是 1,因为是拷贝
而用 class 就不一样了。
3. 协议与委托
Delegate 模式是 iOS 开发中最常用的通信方式之一。
比如 UITableViewDelegate,UITableViewDataSource 就是两个常用的协议。
你可以自定义一个:
protocol TaskManagerDelegate: AnyObject {
func didAddNewTask(_ task: Task)
}
然后在某个 VC 中实现它。
4. 闭包(Closure)
闭包就像 Swift 中的函数式编程单元,到处都能见到:
let numbers = [1, 2, 3, 4]
let squares = numbers.map { $0 * $0 }
或者带有参数的:
func perform(operation: (Int, Int) -> Int) {
let result = operation(3, 4)
print(result)
}
perform { a, b in a + b } // 输出 7
闭包捕获上下文变量时一定要注意内存泄漏问题,别忘了 [weak self]!
代码实践:一个简单的任务添加模块
为了帮助你更好地理解和上手,下面我将展示一个完整的“添加任务”的模块代码,涵盖从界面到逻辑的全流程。
步骤 1:UI 层面 —— 添加输入框和按钮
在 Main.storyboard 中拖入一个 UITextField 和 UIButton。然后为其连接 IBOutlet 和 IBAction。
@IBOutlet weak var taskTextField: UITextField!
@IBAction func addTaskButtonTapped(_ sender: UIButton) {
guard let taskText = taskTextField.text, !taskText.isEmpty else {
showAlert(message: "请输入任务名称")
return
}
TaskStore.shared.add(task: Task(name: taskText))
taskTextField.text = ""
refreshList()
}
步骤 2:数据模型层 —— 创建 Task Model
struct Task {
let id = UUID().uuidString
let name: String
var isCompleted = false
}
步骤 3:数据存储层 —— 使用 UserDefaults 临时保存
class TaskStore {
static let shared = TaskStore()
private let defaults = UserDefaults.standard
private let taskKey = "tasks"
func add(task: Task) {
var tasks = fetchAll()
tasks.append(task)
save(tasks: tasks)
}
func fetchAll() -> [Task] {
guard let data = defaults.data(forKey: taskKey),
let tasks = try? JSONDecoder().decode([Task].self, from: data) else {
return []
}
return tasks
}
private func save(tasks: [Task]) {
if let encoded = try? JSONEncoder().encode(tasks) {
defaults.set(encoded, forKey: taskKey)
}
}
}
这就是一个简易的本地持久化模块。
踩坑经验分享
1. Autolayout 约束没设置好,iOS 屏幕适配出了问题
我们在测试 iPhone SE 的时候,发现列表文字显示不全。后来发现是 Auto Layout 没设置对齐规则。这个问题让我深刻认识到:
- 不要依赖 Frame 手动计算;
- 多设备模拟器测试很有必要;
- 使用 Safe Area 约束避免被 Home Indicator 或状态栏挡住内容。
2. 闭包循环引用导致内存泄露
我们曾经写过一个 ViewModel,里面有个定时器,调用了自身方法更新 UI:
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
self.updateUI()
}
由于 Timer 对 self 强引用,造成对象无法释放。后来改成:
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
self?.updateUI()
}
这才解决问题。所以记住:涉及到闭包持有外部对象时,记得用 [weak self]。
3. 发布到 App Store 遇到审核拒绝
第一次发布 App 时,审核居然因为“无网络状况下的错误提示缺失”被拒了。
所以我们赶紧加上判断是否联网的部分:
import ReachabilitySwift
func checkInternet() {
let reachability = try! Reachability()
if !reachability.isReachable {
showAlert(message: "请检查网络连接")
}
}
这也提醒我们:用户体验真的很重要,尤其在国内用户习惯下,任何“断网崩溃”都不可接受。
效果总结:技术收获 & 实际价值

最终我们按期完成了 iOS 端开发,并顺利通过了 App Store 审核,在内测阶段获得了良好的反馈。
从技术层面来看:
- 掌握了 Swift 的基础语法;
- 理解了 UIKit 的生命周期和组件结构;
- 熟悉了数据绑定、界面交互的设计模式;
- 了解了移动开发中常见性能优化技巧,比如图片懒加载、减少主线程阻塞等;
- 实践了基本的 App 发布流程(证书配置、TestFlight、提交审核等)
更重要的是,我体会到了“项目驱动学习”的强大之处。与其一味地看书或看教程,不如直接动手写小功能,逐步迭代。
给读者的一些建议
如果你也刚接触 Swift 和 iOS 开发,下面是我的一些切身体会:
- 多动手、少理论:Swift 很适合边学边写,不要停留在阅读层面。
- 善用 Playground:Swift Playgrounds 是快速验证想法的好地方。
- 别怕查文档:Apple 的 Swift 官方文档质量很高,英文也能读懂个七八成。
- 保持耐心:iOS 生态复杂度高,前期踩坑是常态。
- 持续关注 Apple 更新:SwiftUI、Combine 等新技术不断推出,可以适当尝试。
- 重视用户体验细节:哪怕是小动画、转场,也要尽量做到流畅自然。
最后想说一句:iOS 开发其实比想象中有趣得多,特别是当你亲手做出一个小功能并让它在手机上运行起来的时候,那种成就感无可替代。
希望这篇文章能为你打开 Swift 的大门。如果有问题欢迎留言,我们一起交流成长 🧠📱💻

评论 0