iOS приложение падает на ios 13 и с Xcode 11, когда это не было раньше - PullRequest
0 голосов
/ 07 февраля 2020

Наше приложение падает с фатальным исключением:

Fatal Exception: NSInternalInconsistencyException
Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.

Наше приложение раньше не генерировало это исключение в предыдущих версиях iOS (12 и ниже) и при компиляции на Xcode 10. Теперь, на iOS 13.3.1 и компиляции на Xcode 11.3.1 наше приложение вылетает через 10 секунд после каждого открытия. Я рассмотрел это исключение, и кажется, что Apple решила запретить приложениям получать доступ к UIKit в фоновых потоках (пожалуйста, исправьте меня, если я здесь не прав). После включения Main Thread Checker я смог точно определить линию, которая вызвала этот крей sh. Возврат кода привел меня к этой строке:

dispatch_async(dispatch_get_main_queue(), ^{
    //Code that uses the StoreKit to retrieve purchase data, 
    //then updates the UI using UIKit based on this information
});

Этот код в порядке, хотя верно? Потому что, хотя он отправляет этот блок кода, он делает это с основным потоком, так что это не должно быть проблемой, правильно? Что ж, даже после перемещения этого блока кода из функции dispatch_async() он все равно падает. Затем я посмотрел на синглтон для этого класса, который выглядит следующим образом:

+(instancetype)sharedPurchaseManager
{
    static PurchaseManager * sharedPurchaseManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedPurchaseManager = [[PurchaseManager alloc] _init];
    });
    return sharedPurchaseManager;
}

И я подумал, что _init() для PurchaseManager происходит в фоновом потоке. Поэтому я попытался переместить выделение для PurchaseManager из блока dispatch_once(), и приложение перестало падать. Кроме того, Main Thread Checker прекратил прерывать программу в строке, которая использует UIKit. Я почти уверен, что перемещение моего _init() кода из блока dispatch_once() не является правильным способом решения проблемы go, так как это стандартный шаблон проектирования Singleton (пожалуйста, исправьте меня, если я ошибаюсь ). Итак, как мне go решить проблему? Кроме того, в нашем коде более 10 сбоев, вызванных тем же исключением, и с шаблоном проектирования, аналогичным нашему PurchaseManager, поэтому мы хотели бы найти решение для всех этих исключений.

1 Ответ

0 голосов
/ 07 февраля 2020

Звучит так, как будто [[PurchaseManager alloc] _init] содержит некоторый код пользовательского интерфейса, но к +(instancetype)sharedPurchaseManager обращаются в первый раз из фонового потока.

Таким образом, вы можете попробовать два подхода:

1) найти способ явной инициализации sharedPurchaseManger в основном потоке (например, в applicationDidFinishLaunching)

2) попытаться использовать следующие

dispatch_once(&onceToken, ^{
    dispatch_sync(dispatch_get_main_queue(), ^{ // << intentionally synchronous!
       sharedPurchaseManager = [[PurchaseManager alloc] _init];
    }
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...