单例模式

定义 单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。 单例模式的核心要点: 唯一性:整个应用程序生命周期内只存在一个实例 全局访问:提供一个全局访问点获取该实例 懒加载:通常在第一次使用时才创建实例 为什么需要单例模式 单例模式要解决的核心问题是:确保某个类在整个应用中只有一个实例,并提供全局访问点。 问题场景:假设我们正在开发一个App,需要管理用户的登录状态和用户信息。 如果不使用单例,可能会遇到这样的问题: class UserManager { var currentUser: User? var isLoggedIn: Bool = false func login(user: User) { currentUser = user isLoggedIn = true } } // 在登录页面 let userManager1 = UserManager() userManager1.login(user: user) // 在个人中心页面 let userManager2 = UserManager() print(userManager2.isLoggedIn) // false!因为是不同的实例 这种方式有什么问题? 状态不一致:不同地方创建的UserManager是不同的实例,它们的状态相互独立,导致登录状态无法共享 资源浪费:如果UserManager管理的是数据库连接、网络会话等资源,创建多个实例会浪费系统资源 数据同步困难:多个实例之间的数据需要手动同步,容易出错 单例模式的解决思路: 单例模式确保整个应用只有一个UserManager实例,所有地方都访问同一个对象: class UserManager { static let shared = UserManager() private init() {} var currentUser: User? var isLoggedIn: Bool = false func login(user: User) { currentUser = user isLoggedIn = true } } // 在登录页面 UserManager.shared.login(user: user) // 在个人中心页面 print(UserManager.shared.isLoggedIn) // true!同一个实例 适合使用单例的场景: ...

May 2, 2026

OOP、POP与AOP

在iOS开发中,经常会接触到三种重要的编程范式:面向对象编程(OOP)、面向协议编程(POP)和面向切面编程(AOP)。 OOP - 面向对象编程 基本概念 面向对象编程(Object-Oriented Programming)是一种以对象为核心的编程范式。它将数据和操作数据的方法封装在一起,形成对象。 OOP的四大核心特性: 特性 说明 iOS中的体现 封装 隐藏内部实现细节,只暴露必要的接口 @interface/@implementation分离,private属性 继承 子类继承父类的属性和方法 UIViewController继承自UIResponder 多态 同一接口可以有不同的实现 子类重写父类方法 抽象 提取共同特征形成抽象类型 抽象基类、协议 Objective-C中的OOP Objective-C是一门典型的面向对象语言,它在C语言的基础上添加了面向对象的特性。 // 基类定义 @interface Animal : NSObject @property (nonatomic, copy) NSString *name; - (void)speak; - (void)eat:(NSString *)food; @end @implementation Animal - (void)speak { NSLog(@"Animal speaks"); } - (void)eat:(NSString *)food { NSLog(@"%@ is eating %@", self.name, food); } @end // 子类继承 @interface Dog : Animal @property (nonatomic, copy) NSString *breed; @end @implementation Dog // 重写父类方法 - 多态 - (void)speak { NSLog(@"%@ barks: Woof!", self.name); } // 子类特有方法 - (void)fetch { NSLog(@"%@ is fetching", self.name); } @end Swift中的OOP Swift同样支持面向对象编程,但提供了更现代的语法和更强的类型安全。 ...

May 2, 2026

KMP(Kotlin Multiplatform)详解

KMP(Kotlin Multiplatform)是 JetBrains 推出的跨平台代码共享方案。它与 Flutter、React Native 的"一套代码一套 UI"思路不同,走的是"业务逻辑共享,UI 原生“的路线:Kotlin 写的网络、数据库、ViewModel、业务规则等代码编译成 iOS/Android/HarmonyOS/Web 可直接使用的二进制产物,而 UI 仍然由各平台原生框架(SwiftUI/UIKit、Jetpack Compose、ArkUI、DOM/Compose Web)自己实现。2023 年 11 月 KMP 正式 Stable;2024 年 6 月华为开发者大会(HDC 2024)上 JetBrains 与华为合作宣布 Kotlin 原生支持 HarmonyOS NEXT,KMP 正式扩展到"Android + iOS + 鸿蒙 + Web"的四端矩阵;随后 Compose Multiplatform for iOS 经历 Alpha → Beta 演进,使得"连 UI 也可以一起共享"成为可选项。 对 iOS 开发者而言,KMP 的意义在于:Android/鸿蒙同事写的 Kotlin 代码你可以像 Swift 一样调用,而代价只是多一个 XCFramework 和一点桥接代码。本文从架构开始,一路讲到编译原理、内存模型、iOS/鸿蒙互操作细节与工程最佳实践。 一、KMP 是什么 1.1 一句话定义 KMP 是 Kotlin 官方的跨平台编译工具链,允许你用 Kotlin 写一份"业务逻辑"代码,编译到多个目标平台: ...

May 2, 2026

KISS原则

什么是KISS原则? KISS(Keep It Simple, Stupid)原则是软件工程中最重要的设计原则之一。它的核心理念是:简单的解决方案比复杂的解决方案更好。 核心思想 简单不等于简陋 KISS 原则强调的"简单"是指: 易于理解:代码的意图一目了然 易于修改:改动时不会牵一发而动全身 易于调试:出问题时容易定位原因 易于测试:测试用例简单直接 简单不意味着功能缺失或代码草率,而是用最直接的方式解决问题。 复杂性的代价 不必要的复杂性会带来: 更高的学习成本:新成员需要更长时间理解代码 更多的 bug:复杂逻辑更容易出错 更难维护:修改一处可能影响多处 更慢的开发速度:需要考虑更多的边界情况 graph LR A[复杂性增加] --> B[理解成本上升] A --> C[Bug 概率上升] A --> D[维护难度上升] B --> E[开发效率下降] C --> E D --> E iOS开发中的KISS实践 1. 选择简单的架构 // 过度设计:小项目使用复杂的 VIPER 架构 // 每个模块都有 View, Interactor, Presenter, Entity, Router // 一个简单的列表页面需要创建 5+ 个文件 // KISS:根据项目规模选择合适的架构 // 小项目:MVC 就足够了 class UserListViewController: UIViewController { private var users: [User] = [] override func viewDidLoad() { super.viewDidLoad() loadUsers() } private func loadUsers() { Task { users = try await UserService.shared.fetchUsers() tableView.reloadData() } } } // 随着项目增长,再逐步引入更复杂的架构 2. 避免过早优化 // 违反 KISS:过早引入复杂的缓存机制 class ImageLoaderOverEngineered { private let memoryCache = NSCache<NSString, UIImage>() private let diskCache: DiskCache private let networkQueue = DispatchQueue(label: "imageLoader", attributes: .concurrent) private let cacheQueue = DispatchQueue(label: "cacheQueue") private var pendingRequests: [URL: [(UIImage?) -> Void]] = [:] private let requestLock = NSLock() func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) { // 复杂的多级缓存、请求合并、线程同步逻辑... // 对于大多数 App 来说,这可能是过度设计 } } // KISS:从简单开始 class SimpleImageLoader { private let cache = NSCache<NSString, UIImage>() func loadImage(from url: URL) async -> UIImage? { let key = url.absoluteString as NSString // 先检查缓存 if let cached = cache.object(forKey: key) { return cached } // 从网络加载 guard let (data, _) = try? await URLSession.shared.data(from: url), let image = UIImage(data: data) else { return nil } // 存入缓存 cache.setObject(image, forKey: key) return image } } // 当真正遇到性能问题时,再逐步优化 3. 简化条件逻辑 // 违反 KISS:复杂的嵌套条件 func processOrder(_ order: Order) -> Result<Receipt, OrderError> { if order.items.count > 0 { if order.customer != nil { if order.customer!.isVerified { if order.paymentMethod != nil { if order.shippingAddress != nil { if order.totalAmount > 0 { // 终于可以处理订单了... return .success(Receipt()) } else { return .failure(.invalidAmount) } } else { return .failure(.noShippingAddress) } } else { return .failure(.noPaymentMethod) } } else { return .failure(.unverifiedCustomer) } } else { return .failure(.noCustomer) } } else { return .failure(.emptyOrder) } } // KISS:使用 Guard 早期返回 func processOrderSimple(_ order: Order) -> Result<Receipt, OrderError> { guard !order.items.isEmpty else { return .failure(.emptyOrder) } guard let customer = order.customer else { return .failure(.noCustomer) } guard customer.isVerified else { return .failure(.unverifiedCustomer) } guard order.paymentMethod != nil else { return .failure(.noPaymentMethod) } guard order.shippingAddress != nil else { return .failure(.noShippingAddress) } guard order.totalAmount > 0 else { return .failure(.invalidAmount) } return .success(Receipt()) } 4. 使用标准库而非自己实现 // 违反 KISS:自己实现数组去重 func removeDuplicatesManual<T: Hashable>(from array: [T]) -> [T] { var seen = Set<T>() var result = [T]() for element in array { if !seen.contains(element) { seen.insert(element) result.append(element) } } return result } // KISS:使用标准库 let uniqueArray = Array(Set(array)) // 或保持顺序 let uniqueOrderedArray = array.reduce(into: [T]()) { result, element in if !result.contains(element) { result.append(element) } } // 更简单:使用 Swift Algorithms import Algorithms let unique = array.uniqued() 5. 避免过度泛型化 // 违反 KISS:过度泛型,难以理解 protocol DataProviding { associatedtype DataType associatedtype ErrorType: Error func fetch() async throws -> DataType } protocol DataTransforming { associatedtype Input associatedtype Output func transform(_ input: Input) -> Output } class GenericDataLoader< Provider: DataProviding, Transformer: DataTransforming > where Provider.DataType == Transformer.Input { // 复杂的泛型约束... } // KISS:简单直接的实现 class UserLoader { func loadUsers() async throws -> [User] { let data = try await APIClient.shared.fetch("/users") return try JSONDecoder().decode([User].self, from: data) } } // 当真正需要复用时,再考虑抽象 6. 简化 API 设计 // 违反 KISS:过多的配置选项 func createButton( title: String, titleColor: UIColor = .white, titleFont: UIFont = .systemFont(ofSize: 16), backgroundColor: UIColor = .blue, cornerRadius: CGFloat = 8, borderWidth: CGFloat = 0, borderColor: UIColor = .clear, shadowColor: UIColor = .black, shadowOffset: CGSize = .zero, shadowRadius: CGFloat = 0, shadowOpacity: Float = 0, contentInsets: UIEdgeInsets = .init(top: 12, left: 24, bottom: 12, right: 24), isEnabled: Bool = true, // ... 更多参数 ) -> UIButton { // ... } // KISS:提供简单的默认实现和预设样式 enum ButtonStyle { case primary case secondary case destructive case text } func createButton(title: String, style: ButtonStyle = .primary) -> UIButton { let button = UIButton() button.setTitle(title, for: .normal) switch style { case .primary: button.backgroundColor = .systemBlue button.setTitleColor(.white, for: .normal) button.layer.cornerRadius = 8 case .secondary: button.backgroundColor = .systemGray5 button.setTitleColor(.systemBlue, for: .normal) button.layer.cornerRadius = 8 case .destructive: button.backgroundColor = .systemRed button.setTitleColor(.white, for: .normal) button.layer.cornerRadius = 8 case .text: button.backgroundColor = .clear button.setTitleColor(.systemBlue, for: .normal) } return button } // 使用简单 let primaryButton = createButton(title: "Submit") let cancelButton = createButton(title: "Cancel", style: .secondary) 7. 选择合适的数据结构 // 违反 KISS:用复杂的数据结构解决简单问题 class UserStatusManager { private var statusTree: RedBlackTree<String, UserStatus> private var statusIndex: [String: TreeNode<String, UserStatus>] // 复杂的树结构来管理用户状态... } // KISS:简单的字典就够了 class SimpleUserStatusManager { private var userStatus: [String: UserStatus] = [:] func setStatus(_ status: UserStatus, for userId: String) { userStatus[userId] = status } func getStatus(for userId: String) -> UserStatus? { return userStatus[userId] } } 如何平衡简单与功能 渐进式复杂度 遵循"简单开始,按需增长"的原则: ...

May 2, 2026

Block底层原理

基础概念 Block 是 Apple 在 C 语言基础上扩展的语法特性(也称为 Closure / 闭包),它允许将一段代码和其执行时需要的上下文环境封装为一个可传递的对象。Block 在 Objective-C 和 C/C++ 中均可使用,是 GCD、动画 API、回调等场景的核心机制。 // Block 的声明与调用 void (^myBlock)(NSString *) = ^(NSString *name) { NSLog(@"Hello, %@", name); }; myBlock(@"World"); 一个核心问题是:Block 是函数指针还是对象? 答案是——Block 本质上是一个 OC 对象。它的底层是一个包含 isa 指针的 C 结构体,满足 OC 对象的基本条件;同时它也包含一个函数指针(FuncPtr),指向 Block 实际执行的代码。因此可以说,Block 是一个用对象形式包装了函数指针和捕获变量的特殊 OC 对象。 底层数据结构 通过 clang -rewrite-objc 可以将 Block 语法转换为 C++ 代码,观察其底层结构。这个转换虽然不完全等同于最终编译产物,但准确反映了 Block 的数据布局和调用机制。 编译器转换示例 对以下源码: int main() { int a = 10; void (^block)(void) = ^{ NSLog(@"%d", a); }; block(); return 0; } Clang 会将其转换为如下结构: ...

May 2, 2026

AI编程工具

AI编程工具正在重塑软件开发的工作方式。从简单的代码补全到自主完成复杂任务的编程Agent,这个领域在短短两三年内经历了巨大的变化。本文梳理AI编程工具的核心概念、工作原理和实践方法。 一、AI编程工具的演进 1.1 三代AI编程工具 graph LR G1["第一代代码补全Copilot (2021)"] --> G2["第二代对话式编程ChatGPT (2022)"] G2 --> G3["第三代编程AgentCursor/Devin (2024+)"] 代际 交互方式 能力范围 代表产品 第一代 行内补全,按Tab接受 单行/单函数级别 GitHub Copilot 第二代 对话框问答 代码片段级别 ChatGPT、Claude 第三代 Agent模式,自主执行 跨文件/跨系统级别 Cursor、Windsurf、Devin 1.2 当前主流工具对比 工具 类型 核心特点 GitHub Copilot IDE插件 最早的AI编程助手,集成在VS Code/JetBrains等主流IDE中 Cursor 独立IDE 基于VS Code深度定制,Agent模式、多文件编辑 Windsurf 独立IDE Codeium推出,强调"Flow"模式的流畅体验 Claude Code CLI工具 终端中运行的编程Agent,适合命令行工作流 Devin 云端Agent 全自主编程Agent,自带开发环境 Amazon Q Developer IDE插件 AWS深度集成,擅长云原生开发 二、AI编程工具的工作原理 2.1 代码补全的原理 代码补全是最基础的能力。以Copilot为例: graph LR CTX["上下文收集当前文件内容打开的相关文件光标位置"] --> PROMPT["构造Prompt"] PROMPT --> LLM["LLM(Codex/GPT-4)"] LLM --> SUGGEST["补全建议"] SUGGEST --> USER["用户Tab接受 / Esc拒绝"] 上下文收集是关键——模型看到的上下文越好,补全质量越高: ...

May 2, 2026

代理模式

在iOS开发中,“代理"这个词通常有两层含义: 设计模式中的代理模式(Proxy Pattern):提供目标对象的替代品或占位符,控制对目标对象的访问 iOS中的代理模式(Delegate Pattern):一种基于协议的回调机制,用于对象间通信 虽然中文都叫"代理”,但它们是不同的设计模式,解决不同的问题。本文将两者都进行详细介绍。 一、Proxy Pattern(代理模式) 定义 代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。 核心思想:代理对象作为真实对象的"替身",客户端通过代理间接访问真实对象。代理可以在访问前后添加额外逻辑,而调用者对此毫不知情。 Client → Proxy → RealSubject ↑ 控制访问 添加功能 延迟创建 关键特征: 代理对象与真实对象实现相同的接口 客户端不知道自己使用的是代理还是真实对象 代理对象持有真实对象的引用(通常是强引用) 模式结构 classDiagram class Subject { <<interface>> +request() } class RealSubject { +request() } class Proxy { -realSubject: RealSubject +request() } class Client { +useSubject() } Subject <|.. RealSubject Subject <|.. Proxy Proxy --> RealSubject : delegates to Client --> Subject : uses Proxy的类型 1. 虚拟代理(Virtual Proxy) 虚拟代理用于延迟创建开销大的对象,只有在真正需要时才创建: ...

May 2, 2026

Instruments详解

Instruments 是 Xcode 内置的性能分析套件,基于 DTrace/Apple Trace 基础设施构建,可以对 iOS、iPadOS、macOS、watchOS、tvOS、visionOS 的应用与系统进行 CPU、内存、图形、能耗、网络、I/O 等各个维度的观测。 Xcode 26(WWDC25)对 Instruments 做了近年来最大规模的升级,重点包括: Power Profiler:全新的能耗分析工具,支持 Tethered 与 Passive 两种录制模式。 下一代 SwiftUI instrument:基于 Cause & Effect Graph 可视化状态变更到视图更新的因果链。 Processor Trace:基于 Apple Silicon 硬件特性的"全量指令级"采集(Xcode 16.3 引入,26 完善)。 CPU Counters 重做:引入 Bottleneck Analysis 方法论与 CPU Bottlenecks 模板。 Foundation Models instrument:为 FoundationModels 框架提供 Prompt / Asset Loading / Inference 分阶段观测。 Animation Hitches 重做:修正多显示器场景下的统计,数据量显著减少,处理更快。 UI 重设计:主菜单精简、Settings 页面重写、Track 次级菜单、Launch/Attach 环境变量配置。 一、Instruments 基础 1.1 启动 Instruments 的几种方式 方式 使用场景 Xcode Product → Profile(⌘I) 日常开发,自动以 Release 模式编译并附加符号 Xcode Debug Navigator 中的图表 粗略观测 CPU / Memory / Disk / Network / Energy 直接打开 Instruments.app 对已安装的 App、系统进程或已录制的 .trace 文件进行分析 命令行 xcrun xctrace record CI / 自动化性能回归 设备端 Developer Settings 的 Power Profiler 离线、无 Mac 场景采集能耗数据 Xcode 26 中,Product → Profile 会按默认 scheme 的 Profile 构建配置(通常是 Release + -O),这与 Debug 模式的数据差异很大,做性能评估时必须用 Profile 构建。 ...

May 2, 2026

Flutter 详解

Flutter 是 Google 推出的跨平台 UI 工具包,用 Dart 写一份代码即可编译到 iOS、Android、Web、macOS、Windows、Linux、嵌入式设备。它与 KMP/RN 的最大区别是"不映射到原生控件,也不跑在 WebView 里"——Flutter 自带一套 Impeller 渲染引擎,直接向 GPU(Metal / Vulkan)提交绘制命令,从按钮到滚动条的每一个像素都是自己画出来的。 这种"自绘"架构让 Flutter 拥有"双端像素级一致“的超能力,也带来了”iOS 原生设计语言永远慢半拍“的原罪。在 AI Coding 时代,Flutter 的统一性和 GenUI 生态让它成为 LLM 生成 UI 的理想容器,但端侧 AI、Liquid Glass 等 Apple 独占能力的缺失也让它在 iOS 26 时代面临新的挑战。 本文从架构出发,穿透到 Dart VM 编译链、Impeller 渲染管线、iOS 嵌入原理,再到 AI 时代的机遇与陷阱,是一篇写给 iOS 开发者的 Flutter 体系化导读。 一、Flutter 是什么 1.1 一句话定义 Flutter 是一套”声明式 UI + 自绘渲染 + AOT 原生代码“的跨平台框架: 声明式 UI:UI 是 build(context) => Widget 的纯函数结果,状态变 → 重新 build → diff → 更新。 自绘渲染:所有控件都是 Flutter 自己用 Impeller 绘制出来的,不使用 UIKit / SwiftUI 组件。 AOT 编译:发布时 Dart 代码被 gen_snapshot 编译为 ARM64 机器码,与 C++ 引擎一起链接到 Flutter.framework 内。 一句话记忆:Flutter 不是跑在 iOS 上,它是借 iOS 的一块 CAMetalLayer 画自己的东西。 ...

May 2, 2026

DRY原则

什么是DRY原则? DRY(Don’t Repeat Yourself)原则是由 Andy Hunt 和 Dave Thomas 在《The Pragmatic Programmer》一书中提出的软件开发原则。 Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. 系统中的每一项知识都必须有一个单一、明确、权威的表示。 DRY 原则的核心不仅仅是"不要复制粘贴代码",而是确保知识和逻辑在系统中只存在一处。 核心思想 知识的单一来源 DRY 原则强调的是"知识"的不重复,而非简单的"代码"不重复。知识包括: 业务规则:如"订单金额超过100元免运费" 数据结构定义:如用户模型的字段 算法逻辑:如价格计算公式 配置信息:如 API 地址、超时时间 // 违反 DRY:同一个业务规则在多处定义 class OrderService { func calculateShipping(orderAmount: Double) -> Double { if orderAmount > 100 { // 业务规则:100元以上免运费 return 0 } return 10 } } class CartViewController { func updateShippingLabel() { if cartTotal > 100 { // 重复的业务规则! shippingLabel.text = "免运费" } else { shippingLabel.text = "运费:¥10" } } } class CheckoutViewModel { func getShippingFee() -> Double { return totalAmount > 100 ? 0 : 10 // 又是重复! } } // 遵循 DRY:业务规则只定义一次 struct ShippingPolicy { static let freeShippingThreshold: Double = 100 static let standardShippingFee: Double = 10 static func calculateFee(for orderAmount: Double) -> Double { return orderAmount > freeShippingThreshold ? 0 : standardShippingFee } static func isFreeShipping(for orderAmount: Double) -> Bool { return orderAmount > freeShippingThreshold } } // 所有地方都使用这个单一来源 class OrderService { func calculateShipping(orderAmount: Double) -> Double { return ShippingPolicy.calculateFee(for: orderAmount) } } class CartViewController { func updateShippingLabel() { if ShippingPolicy.isFreeShipping(for: cartTotal) { shippingLabel.text = "免运费" } else { shippingLabel.text = "运费:¥\(ShippingPolicy.standardShippingFee)" } } } DRY vs WET WET 是 DRY 的反义词,常见的解释有: ...

May 2, 2026