# Podfile 示例 target 'MainApp'do # 业务组件(同层,互不依赖) pod 'UserModule' pod 'OrderModule' pod 'PaymentModule' # 中间层 pod 'Mediator' pod 'Mediator/Order'# Category,扩展 Order 的便捷方法 pod 'Mediator/User' end
# 各业务组件的 Podspec 只依赖基础库 # OrderModule.podspec Pod::Spec.new do |s| s.name = 'OrderModule' s.dependency 'Mediator' s.dependency 'BaseNetwork' s.dependency 'BaseUI' end
关键点:业务组件之间不直接依赖,都通过 Mediator 间接通信。
四、插件化原理与实现
4.1 插件化的应用场景
热更新:修复线上 Bug 无需发版
按需加载:减少包体积,冷启动只加载核心
动态能力:运营活动插件、A/B 测试模块
多端复用:同一套插件可被主 App、Widget、Watch 加载
4.2 iOS 插件化技术选型
技术
说明
限制
Dynamic Framework
动态库,可延迟加载
App Store 限制主二进制外的动态库
Bundle + 反射
资源/代码打包成 .bundle,运行时加载
无法绕过沙盒,需提前打包进 App
JavaScript 引擎
React Native / Flutter / JSI
非原生,性能与体验有差异
JSPatch / 热修复
通过 Runtime 动态替换方法
已不可上架 App Store
注意:苹果审核禁止下载执行任意代码,真正「从网络下载插件并执行」的纯插件化在 App Store 场景不可行。实际做法多为:预置多个 Framework/Bundle,运行时按需加载,或通过 JS 引擎 + 离线包 实现业务热更新。
// 反射调用 Class class = NSClassFromString("UIViewController"); id vc = [[class alloc] init]; SEL sel = NSSelectorFromString("viewDidLoad"); [vc performSelector:sel];
funcloadDashboardData(completion: @escaping (DashboardData?) -> Void) { let group =DispatchGroup() var user: User? var orders: [Order]? var error: Error? group.enter() fetchUser { result in user =try? result.get() group.leave() } group.enter() fetchOrders { result in orders = (try? result.get()) ?? [] group.leave() } group.notify(queue: .main) { iflet u = user, let o = orders { completion(DashboardData(user: u, orders: o)) } else { completion(nil) } } }
let uploadQueue =OperationQueue() uploadQueue.maxConcurrentOperationCount =3 for chunk in chunks { let op =BlockOperation { uploadChunk(chunk) } uploadQueue.addOperation(op) } uploadQueue.waitUntilAllOperationsAreFinished() mergeChunks()
10.3 搜索防抖
用户输入时,取消上一次未完成的搜索任务,延迟 300ms 再发起新请求:
1 2 3 4 5 6 7 8 9
var searchWorkItem: DispatchWorkItem? funcsearchTextDidChange(_text: String) { searchWorkItem?.cancel() let workItem =DispatchWorkItem { [weakself] in self?.performSearch(text) } searchWorkItem = workItem DispatchQueue.main.asyncAfter(deadline: .now() +0.3, execute: workItem) }
assert(Thread.isMainThread, "UI must be updated on main thread")
11.2 避免循环引用
在闭包中使用 self 时注意 [weak self]:
1 2 3 4 5 6 7
DispatchQueue.global().async { [weakself] in guardletself=selfelse { return } let result =self.doWork() DispatchQueue.main.async { [weakself] in self?.updateUI(result) } }
// 获取成员变量 Ivar *ivars = class_copyIvarList(cls, &count); for (unsignedint i = 0; i < count; i++) { Ivar ivar = ivars[i]; constchar *name = ivar_getName(ivar); constchar *type = ivar_getTypeEncoding(ivar); }
5.4 动态创建类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 创建新类 Class newClass = objc_allocateClassPair([NSObjectclass], "MyDynamicClass", 0);
let searchAction =Action<String, [User], NSError> { keyword in return searchAPI(keyword) // 返回 SignalProducer }
// 绑定启用条件(例如:输入非空) let enabledSearch =Action(state: viewModel.keyword, enabledIf: { $0.count >0 }) { _, keyword in return searchAPI(keyword) }
// 执行 searchAction.apply("Swift").startWithResult { result in switch result { case .success(let users): self.users = users case .failure(let error): self.showError(error) } }
五、常用操作符
5.1 转换类
map:转换每个值
1 2 3
signal .map { $0.uppercased() } .observeValues { print($0) }
filter:过滤值
1 2 3
signal .filter { $0.count >=3 } .observeValues { print($0) }
reduce:聚合为单个值(在 completed 时发出)
1 2 3
signal .reduce(0) { $0+$1 } .observeValues { print("总和: \($0)") }
5.2 组合类
combineLatest:合并多个流的最新值
1 2 3 4
let combined =Signal.combineLatest(usernameSignal, passwordSignal) combined.observeValues { (user, pwd) in loginButton.isEnabled = user.count >0&& pwd.count >=6 }
zip:按顺序一一配对
1 2
let zipped =Signal.zip(numbersSignal, lettersSignal) // (1,A), (2,B), (3,C)...
5.3 扁平化 (flatten)
merge:内层多个流的值按到达顺序全部输出
concat:按顺序消费内层流,前一个完成后才订阅下一个
latest:只保留「最新」的内层流,常用于搜索联想(新关键词来时取消旧请求)
1 2 3 4 5 6 7
searchKeywordSignal .flatMap(.latest) { keyword in searchAPI(keyword) // 新关键词会取消上一次请求 } .observeValues { users in self.updateUI(users) }