проблема с асинхронным программированием при вызове 2 методов в Objective-C - PullRequest
0 голосов
/ 11 августа 2010

Внутри класса A:

-(void)authenticateUser
{
   authenticate_Obj = [classB_Obj authenticateMobileUser];
}

Inside ClassB:

-(AuthenticateObj*)authenticateMobileUser
{
   [mobile_Obj AuthenticateMobileServer:self action:@selector(Handler:)];
   return authenticate_G_Obj;
}

-(void)Handler:(id)value
{
   authenticate_G_Obj = (AuthenticateObj*)value;
}

Теперь, когда метод authenticateMobileUser класса B возвращает элемент управления обратно в ClassA, мы инициируем объект authenticate_Obj.

Моя проблема в том, что когда я запускаю проект, authenticate_Obj имеет значение NULL ... фактически, когда он входит в метод обработчика, объект инициализируется. но контролируемый возвращается обратно в ClassA, не входя в метод Handler. Я думаю, что это проблема асинхронного выполнения. Как заставить его войти в метод обработчика, а затем только вернуть элемент управления в ClassA ??

Пожалуйста, помогите мне ..

Спасибо.

Ответы [ 3 ]

2 голосов
/ 11 августа 2010

Похоже, что вы думаете , что вы хотите сделать, это заблокировать выполнение до завершения аутентификации. Это может быть возможным, если AuthenticateMobileServer порождает фоновый поток для работы - вы бы использовали объект синхронизации, такой как NSLock - но это действительно плохая идея. Зачем вообще иметь фоновый поток, если вы все равно собираетесь блокировать? А синхронизация потоков общеизвестно сложна и подвержена ошибкам, если вы не знаете, что делаете, а вы (давайте посмотрим правде в глаза) этого не сделаете.

Вместо этого вы, вероятно, должны принять, что во время аутентификации будет период неопределенности, в течение которого ваше приложение должно продолжать обработку в некотором промежуточном состоянии, а затем использовать callback , чтобы уведомлять вас, когда аутентификация завершена, и вы можете продолжить с тем, что вам нужно сделать с аутентифицированным пользователем.

Существует множество способов сделать это, и в вопросе недостаточно подробностей, чтобы точно сказать, какой из них лучше. Но вы, похоже, уже используете что-то очень похожее в ClassB, поэтому я бы сказал, сделайте то же самое с ClassA:

Inside ClassA:

-(void)authenticateUser
{
   authenticate_Obj = nil;
   [classB_Obj authenticateMobileUserAndNotify:self action:@selector(authenticatedObject:)];
   // returns more or less immediately, not yet authenticated
}

-(void)authenticatedObject:(YourAuthObjectClass*) authObj
{
    authenticate_Obj = authObj;
    // do post-authentication stuff here
}

Inside ClassB:

-(void)authenticateMobileUserAndNotify:(id)target action:(SEL)sel
{
   // I'm making these ivars for simplicity, there might be other considerations though
   callbackTarget = target;
   callbackSelector = sel;

   [mobile_Obj AuthenticateMobileServer:self action:@selector(Handler:)];
}

-(void)Handler:(id)value
{
   authenticate_G_Obj = (AuthenticateObj*)value;
   [callbackTarget performSelectorOnMainThread:callbackSelector withObject:authenticate_G_Obj waitUntilDone:NO];
}

Очевидно, что это всего лишь эскиз, и он не предназначен для использования как есть. И вам нужно будет рассмотреть, что происходит в вашем приложении в состоянии ожидания, когда идет аутентификация, но authenticate_Obj все еще nil. Но, надеюсь, вы поняли идею.

0 голосов
/ 11 августа 2010

Оба ответа JeremyP и walkytalky верны и лежат в основе создания отзывчивого пользовательского интерфейса.Основное правило:

Если вы выполняете потенциально блокирующие операции, такие как работа в сети в главном потоке, вы попадете в беду.

Существует как минимум две причины:

  1. Вы блокируете цикл выполнения, чтобы он больше не мог обрабатывать пользовательские события.Это приведет к вращающемуся пляжному мячу на Mac и не отвечающему пользовательскому интерфейсу на Mac и iOS.
  2. Если вы работаете на iOS, есть сторожевой таймер, который проверяет, реагирует ли ваш пользовательский интерфейс на пользовательские события.Если вы блокируете пользовательский интерфейс дольше 20 секунд, вы получите код ошибки 0x8badf00d.

Таким образом, чтобы сделать это, что может занять некоторое время, вам придется сделать это в фоновом потоке.,Поскольку два ответа JeremyP и walkytalky указывают часто, вы получаете обратный вызов.Это нормально, но в целом существует три способа обмена сообщениями:

  • Делегирование
  • Уведомления
  • Наблюдение Kev-значения

Все три могут быть и используются.Между ними есть тонкие различия.Одним из наиболее важных является то, что делегирование - это обмен сообщениями 1: 1, тогда как другое - это обмен сообщениями 1: n.

Теперь не говорите, что вам нужно использовать NSThread.Взгляните на NSOperation и NSOperationQueue вместо этого.Они позволяют инкапсулировать части работы в операции и позволяют им работать в очереди в фоновом режиме.Также, если вы используете эти обратные вызовы с синтаксисом @selector (methodname :), есть что-то новое: блоки.Часто существуют эквивалентные методы, которые используют блок вместо селектора для выполнения в качестве обратного вызова.

Чтобы закончить, вот золотое правило:

Вы можете обновить свою модель нафоновый поток, но НИКОГДА не обновляйте свой интерфейс в фоновом потоке.

Посмотрите видео WWDC10 по этим темам.В двух частях рассказывается о сетевом взаимодействии, в котором подробно объясняются концепции.

0 голосов
/ 11 августа 2010

Я думаю, вы говорите, что AuthenticateMobileServer:action: является асинхронным, и вы хотите заблокировать, пока он не закончится, чтобы вы могли получить возвращаемое значение. К сожалению, мы не можем сказать вам, не зная, как это работает. Основной вопрос заключается в том, запускает ли действие обработчик основной поток или дополнительный поток.

Если он запускает действие в главном потоке, наилучшей стратегией является немедленный возврат из authenticateMobileUser без ожидания объекта аутентификации и отключение элементов пользовательского интерфейса, которые зависят от аутентификации. Затем, когда вы получите объект аутентификации, вам нужно снова включить элементы пользовательского интерфейса.

Если он запускает действие в фоновом потоке, проще всего установить другой метод, похожий на Handler (кстати, соглашение об именах для методов и переменных должно начинаться со строчной буквы), которое затем вызывается Обработчик с performSelectorOnMainThread:waitUntilDone:. Затем вы можете использовать ту же стратегию, что описана выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...