ios 出现“挂起恢复部分功能”时崩溃

siotufzp  于 2023-07-01  发布在  iOS
关注(0)|答案(1)|浏览(121)

我们已经收到了崩溃报告,其中包括短语“suspend resume partial function”,这似乎来自我们使用Swift并发。堆栈跟踪的一个示例是

Crashed: com.apple.root.user-initiated-qos.cooperative
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000cf855c2e0
0  libobjc.A.dylib                0x4174 objc_release + 16
1  libobjc.A.dylib                0x4174 objc_release_x0 + 16
2  libswiftCore.dylib             0x3ad2c8 swift_arrayDestroy + 124
3  libswiftCore.dylib             0x98f38 _ContiguousArrayStorage.__deallocating_deinit + 96
4  libswiftCore.dylib             0x3bd628 _swift_release_dealloc + 56
5  libswiftCore.dylib             0x3be44c bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1> >::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 132
6  MyAppName                      0x32e08c (3) suspend resume partial function for ProductUpdater.updateProducts() + 138 (Products.swift:138)
7  libswift_Concurrency.dylib     0x41948 swift::runJobInEstablishedExecutorContext(swift::Job*) + 416
8  libswift_Concurrency.dylib     0x42868 swift_job_runImpl(swift::Job*, swift::ExecutorRef) + 72
9  libdispatch.dylib              0x15944 _dispatch_root_queue_drain + 396
10 libdispatch.dylib              0x16158 _dispatch_worker_thread2 + 164
11 libsystem_pthread.dylib        0xda0 _pthread_wqthread + 228
12 libsystem_pthread.dylib        0xb7c start_wqthread + 8

Crashlytics似乎认为call 6是这里的问题,它将崩溃标记为“(3)suspend resume partial function for ProductUpdater.updateProducts()”。
短语“suspend resume partial function”是什么意思,它表示程序正在做什么导致了问题?下面是代码相关部分的一个非常粗略的草图:

struct Product: Codable, Equatable {
    var id: String
    var name: String
    var price: Int
}

struct ProductWrapper: Codable, Equatable {
    var metadata: String
    var products: [Product]

    static let none = ProductWrapper(metadata: "none", products: [])
}

class ProductUpdater {
    var userIsSignedIn: Bool = false
    var products: ProductWrapper = .none

    init() {
        Task {
            await updateProducts()
        }
    }

    private func updateProducts() async {
        // If there's no user signed in, we can't fetch the products.
        guard userIsSignedIn else {
            products = .none
            return
        }

        do {
            // This next line was line 138 in the original code:
            products = try await NetworkService.fetchProducts()
        } catch {
            print("Could not fetch products: \(error)")
        }
    }
}

根据这篇关于GitHub问题的评论中的模糊建议,我试图将products的赋值 Package 在一个看似毫无意义的延续中,

private func updateProducts() async {
        // If there's no user signed in, we can't fetch the products.
        guard userIsSignedIn else {
            products = .none
            return
        }

        do {
            let updatedProducts = try await NetworkService.fetchProducts()
            await withCheckedContinuation { continuation in
                products = updatedProducts
                continuation.resume()
            }
        } catch {
            print("Could not fetch products: \(error)")
        }
    }

但这似乎并没有解决问题。
我们使用Xcode Cloud来生成我们的构建,它使用的是Xcode 14.3.1(Swift 5.8.1)。

9bfwbjaz

9bfwbjaz1#

“suspend resume partial function”表示任务在await之后恢复。部分函数是两个await调用之间的函数部分(或函数的开始或结束)。
此崩溃是由于阵列上的过度释放。我希望您是在多个线程上访问products(或改变ProductWrapper的任何部分)。
我不明白这段代码是如何编译的。init返回时未初始化products,这是无效的。也许你正在跳过一个编译器错误。您需要确保在返回之前初始化所有属性。
您还通过随机线程上的任务修改产品,这是无效的。
我相信您希望进行以下更改:

//  products needs an initial value
var products: ProductWrapper = ProductWrapper(metadata: "", products: [])

你可能想让ProductUpdater成为MainActor:

@MainActor
class ProductUpdater {

这将确保您对products的访问和修改是以原子方式完成的(它也会将init中的Task推送到主actor上)。如果你想让它在另一个线程上,你几乎肯定应该让它成为一个普通的actor而不是一个类。
如果这仍然是一个问题,您需要为products添加某种锁定,以便它不会在多个线程上被访问。我鼓励在你的方案中打开线程消毒器(在诊断下),看看是否还有其他地方有这个问题。

相关问题