Загрузка ресурсов по запросу без ошибок на iOS, но недоступна

Наше приложение работает каждый раз без сбоев на всех устройствах iOS, с которыми мы тестировали (свежая установка или обновление из xcode/adhoc production/debug, мы пробовали их все). Но при проверке приложения его отклоняют, потому что кажется, что ресурс по требованию никогда не становится доступным, даже если загрузка ресурса завершается без ошибок.

Мы обращаемся к ресурсу в области блокировки обратного вызова камеры. Если ресурс доступен, мы идем дальше и используем его, в противном случае мы делаем beginAccessingResourcesWithCompletionHandler() и блокируем бесплатный обратный вызов только после завершения загрузки. Проблема в том, что обозреватель приложения говорит, что оно загружается (для этого есть индикатор выполнения), а затем продолжает просить повторную загрузку снова и снова. Почему он был бы недоступен, если он только что успешно завершил загрузку (обратите внимание, что ошибки нет)?

[request conditionallyBeginAccessingResourcesWithCompletionHandler:^(BOOL resourcesAvailable) {
if (resourcesAvailable)
{
    /* use the resource. */
    /* unblock the callback. done. */
}
else
{
    /* ask to download resource */
    [request beginAccessingResourcesWithCompletionHandler:^(NSError * _Nullable error) {
        if (error)
        {
            NSLog(@"%@", error);
            /* don't unblocked. return. will hang. */ 
        }
        /* unblock the callback. done. resource should be available next camera frame. */
    }];
} }];

Также это не проблема нехватки памяти. У нас есть это покрыто и протестировано. Более того, beginAccessingResourcesWithCompletionHandler() возвращает без ошибок.


person Hashman    schedule 12.10.2019    source источник
comment
Это урезанная версия реального кода. Это после всех образцов в Интернете. Вы делаете условное начало, и если это не удается, вы делаете начало доступа для загрузки. И вы говорите, что beginAccessingResourcesWithCompletionHandler() нужно вызывать только из основного потока? Почему? Я нигде не видел, чтобы это упоминалось. Также обработчик завершения возвращает без ошибок. Разве он не должен возвращать ошибку, если что-то пошло не так с загрузкой из-за отсутствия в основном потоке?   -  person Hashman    schedule 13.10.2019
comment
Я вижу в списке 4-3, что они делают beginAccess в основном потоке. developer.apple.com/library/archive/documentation/   -  person Hashman    schedule 13.10.2019
comment
Я отправляю в магазин версию, которая помещает beginAccessing в блок основного потока. Невозможность воспроизвести его на тестовом устройстве усложняет задачу. Обновлю, если это исправит.   -  person Hashman    schedule 13.10.2019
comment
О, это просто очень большая кодовая база. Я не могу скинуть все это, это было бы просто нечитаемо. Ресурс OnDemand на самом деле представляет собой веса для нейронной сети, встроенной в металлические шейдеры, которые компилируются при запуске обратного вызова буфера камеры. Пока доступ к ресурсам OnDemand и компиляция сети не будут завершены, обратные вызовы буфера камеры необходимо пропустить. Это делается простым выходом из обратного вызова камеры, если компиляция сети еще не завершена (первым шагом является загрузка указанных ресурсов OnDemand). Это все, что я имею в виду под блокировкой обратного вызова.   -  person Hashman    schedule 13.10.2019
comment
Помещение beginAccess внутри вызова основного потока не решило проблему. Все еще отклонено в проверке, потому что на их устройстве почему-то все еще не видны ресурсы Доступные, даже после того, как первый beginAccess завершается без ошибок (он успешно загрузил ресурсы).   -  person Hashman    schedule 14.10.2019


Ответы (2)


Я предполагаю, что проблема заключается в несоблюдении предупреждения большими буквами в документации. :

Объект NSBundleResourceRequest может использоваться только для одного успешного запроса ресурсов.

Вы не можете повторно использовать объект запроса ресурса пакета. У него всего две задачи: загружать ресурсы (один раз) и удерживать их на месте (путем сохранения). После того, как вам удалось загрузить ресурсы один раз, вы можете сделать только две вещи с этим объектом запроса ресурсов пакета (и вы можете сделать обе из них):

  • Звоните endAccessingResources.

  • Выбросьте объект (или дайте ему выйти из области видимости или умрите с прекращением работы приложения).

Если вы хотите снова узнать, в том же запуске приложения, есть ли у вас еще эти ресурсы, вам нужно начать все сначала с нового запроса ресурсов пакета.

Однако я бы пошел дальше. Как я уже сказал в комментариях, вы, кажется, неправильно используете conditionallyBegin. Нет необходимости спрашивать дважды во время одного и того же запуска приложения, есть ли у вас уже эти ресурсы, и нет необходимости вызывать conditionallyBegin, чтобы узнать это, потому что, когда вы говорите beginAccessing, правильно будет произойдет: либо вы начнете использовать ресурсы (если они есть), либо вы их скачаете и начнете использовать (если их не было). Единственная причина для использования conditionallyBegin заключается в том, что ваша логика будет отличаться от той, если окажется, что некоторые ресурсы отсутствуют.

person matt    schedule 14.10.2019
comment
Я переопределяю NSBundleResourceRequest каждую итерацию. Однако я повторно использовал его между вызовами условноBegin и beginAccess (я понимаю, что вы говорите о том, что beginAccess кажется только необходимым, но это то, что конкретно советует Apple). Я изменил его, чтобы определить новый запрос ресурса для вызова beginAccess. Отправлено в магазин и только что получило отказ, по-прежнему не работает на тестовом устройстве, успешно завершает загрузку, но при следующем доступе по-прежнему видит его недоступным. - person Hashman; 15.10.2019
comment
У меня нет идей, какой привередливый API. Версия для Android была сделана несколько месяцев назад. придерживаемся TestFlight только сейчас, поскольку мы не видели этого ни на одном устройстве, кроме того, что использует рецензент. - person Hashman; 15.10.2019

После 9 представлений эта проблема была решена для нас. Но, к сожалению, неясно, какое изменение было необходимо для его решения. Более того, это было рядом с обновлением 13.2.3. В целом я бы удостоверился в этом (обратите внимание, что есть много примеров с голосованием за SO, которые не следуют этим): 1) Все вызовы beginAccess выполняются в основном потоке. 2) Не используйте повторно resourceRequest между условноBeginAccess и beginAccess. (как указал Мэтт). Каждый раз переопределяйте их из имени тега. 3) Убедитесь, что resourceRequest определены таким образом, что они не выходят за рамки до тех пор, пока не будет выполнен весь процесс. Возможно, на устройствах проверяющих есть фоновый демон, который мгновенно удаляет освобождаемые ресурсы (из-за выхода за пределы области действия). 4) если доступ к odr осуществляется внутри блока dispatch_sync(), убедитесь, что нет пути для параллельного запуска beginAccess.

person Hashman    schedule 19.10.2019