You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 {
            // 后台数据同步
        }
    }
}

核心逻辑说明

  1. initVars的同步保证:因为initVars是同步执行的方法,在viewDidLoad中先调用它,后续代码只有在initVars完全执行完毕后才会运行,天然满足initVars完成的条件。
  2. 无耦合设计:两种方案都通过"事件通知"的方式传递请求完成信号,MainViewController仅需监听信号,PremiumManager无需知道MainViewController的存在,避免了双向引用。
  3. 边界场景处理:通过isRequestFinished属性处理"页面加载时请求已完成"的情况,确保不会遗漏执行checkData。

内容的提问来源于stack exchange,提问作者Martin Perry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 15:22:37