struct vs class:值类型与引用类型的"江湖地位"
swift
// 值类型:struct
struct Point {
var x: Int
var y: Int
}
var p1 = Point(x: 0, y: 0)
var p2 = p1 // 复制一份全新内存
p2.x = 10
print(p1.x) // 0 ✅ 原值不动
// 引用类型:class
class Button {
var title: String = "OK"
}
let btn1 = Button()
let btn2 = btn1 // 指向同一块堆内存
btn2.title = "Cancel"
print(btn1.title) // Cancel ❗️共享修改
结论
- 需要"拷贝"语义 → struct(线程安全、写时复制)
- 需要"共享"或继承 → class
iOS 应用生命周期
iOS 13 之后分两块:
- AppDelegate:进程级事件(启动、终止、后台下载)
- SceneDelegate:窗口级事件(多窗口支持)
swift
// AppDelegate.swift
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 初始化 SDK、注册推送
return true
}
// SceneDelegate.swift
func sceneDidBecomeActive(_ scene: UIScene) {
// 刷新 UI、重启定时器
}
记忆口诀:Launch → Foreground → Active → Background → Suspended → Terminate
strong / weak / unowned
修饰符 | 是否增加引用计数 | 是否可选 | 场景 |
---|---|---|---|
strong | ✅ | 否 | 默认所有权 |
weak | ❌ | 是 | delegate、父→子 |
unowned | ❌ | 否 | 生命周期同步(如闭包捕获 self) |
循环引用实战
swift
class Person {
var pet: Pet?
deinit { print("Person 释放") }
}
class Pet {
// 错误:strong 互相持有
// var owner: Person?
// 正确:
weak var owner: Person?
deinit { print("Pet 释放") }
}
闭包场景:
swift
class Net {
var completion: (() -> Void)?
func load() {
// 捕获列表破局
completion = { [weak self] in
self?.updateUI()
}
}
}
口诀:双向强引用 = 死锁;weak / unowned 拆链
MVC → MVVM
- MVC:C 既管 UI 又管业务 → MassiveViewController
- MVVM:VM 负责"数据→视图"的格式化,易测试、可绑定
swift
final class ArticleVM: ObservableObject {
@Published var title = ""
func fetch() {
// 纯逻辑,无 UIKit
}
}
SwiftUI + Combine 让 MVVM 成为"官方亲儿子"
Combine 入门
- Publisher:发信号
- Subscriber:收信号
- Operator:加工信号
swift
let vm = TimerVM()
class TimerVM: ObservableObject {
@Published var seconds = 0
private var bag = Set<AnyCancellable>()
init() {
Timer.publish(every: 1, on: .main, in: .common)
.autoconnect()
.assign(to: \.seconds, on: self)
.store(in: &bag)
}
}
优势:类型安全、内存管理自动、与 SwiftUI 无缝
GCD:async vs sync
swift
// 异步:不阻塞当前线程
DispatchQueue.global(qos: .userInitiated).async {
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
self.imageView.image = UIImage(data: data!)
}
}
// 同步:阻塞等待(死锁高危)
// DispatchQueue.main.sync { } // ❌主线程等待主线程→死锁
最佳实践:UI 更新 main.async;耗时任务 global().async
网络层选型
方案 | 优点 | 缺点 |
---|---|---|
URLSession | 官方、0 依赖 | 样板多 |
Alamofire | 链式、自动验证、上传友好 | 增加 500 KB |
swift
// URLSession 原生 async/await
let (data, _) = try await URLSession.shared.data(from: url)
let model = try JSONDecoder().decode(User.self, from: data)
数据持久化矩阵
场景 | 技术 | 示例 |
---|---|---|
小配置 | UserDefaults | flag、score |
大结构化 | CoreData / Realm | 离线文章 |
文件 | FileManager | 缓存图片 |
敏感 | Keychain | token、password |
SwiftUI 属性包装器
- @State:View 内部私有状态
- @Binding:父子双向引用
- @ObservedObject:外部可观察对象
- @EnvironmentObject:全局注入
swift
struct PlayerView: View {
@State private var isPlaying = false // ① 局部
@Binding var progress: Double // ② 父级传入
@ObservedObject var vm: PlayerVM // ③ 引用类型
@EnvironmentObject var appSet: AppSettings // ④ 全局
}
UIView 几何三兄弟
- frame:父坐标系 → 布局
- bounds:自身坐标系 → 绘图、子视图布局
- center:父坐标系中心点 → 动画平移
swift
print(view.frame) // (x=20,y=100,width=200,height=50)
print(view.bounds) // (x=0,y=0,width=200,height=50)
print(view.center) // (x=120,y=125)
DiffableDataSource
告别 performBatchUpdates
崩溃:
swift
enum Section { case main }
typealias Snapshot = NSDiffableDataSourceSnapshot<Section, Article>
var dataSource: UITableViewDiffableDataSource<Section, Article>!
func apply(_ items: [Article]) {
var snap = Snapshot()
snap.appendSections([.main])
snap.appendItems(items)
dataSource.apply(snap, animatingDifferences: true)
}
优势:一致性、动画自动、线程安全
静态库 vs 动态库
维度 | 静态 Framework | 动态 Framework |
---|---|---|
链接时机 | 编译期 | 运行时 |
包大小 | 增大 IPA | 多 App 共享可降体积 |
启动速度 | 略快 | 略慢 |
热更新 | ❌ | ✅(App Store 政策外) |
官方建议:系统级用动态;小 SDK / 启动敏感用静态
MVVM 优劣大辩论
✅ 测试友好、UI 逻辑分离、SwiftUI 原生绑定
❌ 文件数翻倍、初学者绑定语法易踩坑
适用:中大型项目、单元测试覆盖率要求高
AutoLayout:从 Frame 到约束
swift
// 代码约束
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20)
])
口诀:先关 mask,再激活;Anchor 链式可读性高
Combine vs RxSwift
维度 | Combine | RxSwift |
---|---|---|
官方 | ✅ | ❌ |
iOS 版本 | ≥13 | ≥9 |
运算符 | 50+ | 200+ |
跨平台 | 仅 Apple | Android / Web |
结论:新纯 iOS 项目 Combine;多平台或复杂流 RxSwift
URLSession 实战封装
swift
struct APIClient {
static func request<T: Decodable>(_ endpoint: String) async throws -> T {
guard let url = URL(string: endpoint) else { throw URLError(.badURL) }
let (data, response) = try await URLSession.shared.data(from: url)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw URLError(.badServerResponse) }
return try JSONDecoder().decode(T.self, from: data)
}
}
// 使用
let user: User = try await APIClient.request("https://api.xxx.com/user")
Alamofire 优势
- 链式参数封装
- 自动 Validation(statusCode 200..<300)
- Multipart 上传一行代码
- RequestInterceptor 刷新 Token 无感重试
API 认证三板斧
- OAuth2:AccessToken + RefreshToken,标准但流程重
- APIKey:Header 内传 key,简单却易泄漏
- JWT:自包含签名+过期,无需服务端状态,注意 Payload 别放敏感
存储:全进 Keychain;HTTPS 必须;吊销策略后端控制
安全 checklist
✅ HTTPS + SSL Pinning
✅ 不落日志、不埋 Git
✅ 输入校验 + 输出转义
✅ 限流 / 防重放
✅ 定期轮换密钥