Swift中如何同步PremiumManager请求与ViewController任务执行?
解决方案
要实现checkData在initVars完成且PremiumManager内的商品请求结束后执行,同时避免类间互相持有引用,推荐以下两种无耦合的实现方式:
方案一:使用Combine框架(iOS 13+ 推荐)
利用Combine的发布者-订阅者模式,让PremiumManager对外暴露请求完成的信号,MainViewController仅需订阅该信号即可,无需持有PremiumManager的强引用(用weak self避免循环引用)。
改造PremiumManager
import Combine import StoreKit class PremiumManager: SKProductsRequestDelegate { static let shared = PremiumManager() // 私有发布者,仅内部可发送事件 private let _requestCompleted = PassthroughSubject<Void, Never>() // 对外暴露的只读发布者,外部仅能订阅 var requestCompleted: AnyPublisher<Void, Never> { _requestCompleted.eraseToAnyPublisher() } // 标记请求是否已完成,处理页面加载时请求已结束的场景 private(set) var isRequestFinished = false private var activeRequest: SKProductsRequest? // 私有化构造,确保单例唯一性 private init() {} static func loadProducts() { shared.load() } func load() { // 避免重复发起请求 guard activeRequest == nil else { return } let request = SKProductsRequest(productIdentifiers: ["some_id"]) request.delegate = self request.start() activeRequest = request } func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { // 执行你的业务操作 isRequestFinished = true _requestCompleted.send() _requestCompleted.send(completion: .finished) activeRequest = nil } func request(_ request: SKRequest, didFailWithError error: Error) { // 处理请求错误 isRequestFinished = true _requestCompleted.send() _requestCompleted.send(completion: .finished) activeRequest = nil } }
改造MainViewController
import Combine import UIKit class MainViewController: UIViewController { // 存储订阅对象,防止被提前释放 private var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() // 先同步执行变量初始化,确保initVars完成后再处理后续逻辑 initVars() // 处理请求已完成的场景:如果商品请求在页面加载前就结束了,直接调用checkData if PremiumManager.shared.isRequestFinished { checkData() return } // 订阅PremiumManager的请求完成信号 PremiumManager.shared.requestCompleted .receive(on: DispatchQueue.main) // 确保回调在主线程执行 .sink(receiveValue: { [weak self] in self?.checkData() }) .store(in: &cancellables) } func initVars() { // 同步初始化变量逻辑 } func checkData() { DispatchQueue.global(qos: .userInitiated).async { // 后台执行数据同步任务 } } }
方案二:使用NotificationCenter(兼容iOS低版本)
通过系统通知中心传递请求完成事件,同样无需类间互相持有引用,适合iOS 13以下的项目。
改造PremiumManager
import StoreKit class PremiumManager: SKProductsRequestDelegate { static let shared = PremiumManager() // 定义通知名称,建议用逆域名格式避免冲突 static let productRequestCompletedNotification = NSNotification.Name("com.yourapp.PremiumManager.RequestCompleted") private(set) var isRequestFinished = false private var activeRequest: SKProductsRequest? private init() {} static func loadProducts() { shared.load() } func load() { guard activeRequest == nil else { return } let request = SKProductsRequest(productIdentifiers: ["some_id"]) request.delegate = self request.start() activeRequest = request } func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { // 业务逻辑处理 isRequestFinished = true NotificationCenter.default.post(name: Self.productRequestCompletedNotification, object: nil) activeRequest = nil } func request(_ request: SKRequest, didFailWithError error: Error) { // 错误处理 isRequestFinished = true NotificationCenter.default.post(name: Self.productRequestCompletedNotification, object: nil) activeRequest = nil } }
改造MainViewController
import UIKit class MainViewController: UIViewController { private var notificationObserver: NSObjectProtocol? override func viewDidLoad() { super.viewDidLoad() initVars() // 处理请求已完成的场景 if PremiumManager.shared.isRequestFinished { checkData() return } // 注册通知监听 notificationObserver = NotificationCenter.default.addObserver( forName: PremiumManager.productRequestCompletedNotification, object: nil, queue: .main ) { [weak self] _ in self?.checkData() // 仅需执行一次,收到通知后移除监听 if let observer = self?.notificationObserver { NotificationCenter.default.removeObserver(observer) self?.notificationObserver = nil } } } deinit { // 页面销毁时移除监听,防止野指针 if let observer = notificationObserver { NotificationCenter.default.removeObserver(observer) } } func initVars() { // 同步初始化变量 } func checkData() { DispatchQueue.global(qos: .userInitiated).async { // 后台数据同步 } } }
核心逻辑说明
- initVars的同步保证:因为
initVars是同步执行的方法,在viewDidLoad中先调用它,后续代码只有在initVars完全执行完毕后才会运行,天然满足initVars完成的条件。 - 无耦合设计:两种方案都通过"事件通知"的方式传递请求完成信号,
MainViewController仅需监听信号,PremiumManager无需知道MainViewController的存在,避免了双向引用。 - 边界场景处理:通过
isRequestFinished属性处理"页面加载时请求已完成"的情况,确保不会遗漏执行checkData。
内容的提问来源于stack exchange,提问作者Martin Perry
相关产品推荐
相关产品推荐

