Архитектура приложения iOS с NSOperations

Два месяца назад я начал писать новое приложение для iPhone, и по этой причине я создал общий веб-сервис RESTFul, который позволяет мне иметь много таких необходимых функций, как аутентификация пользователя, профили пользователей, система дружбы, обработка мультимедиа, систему обмена сообщениями и так далее. На мой взгляд, есть несколько вариантов использования для повторного использования этого веб-сервиса для будущих приложений iPhone.

С учетом этого состояния я решил написать статическую библиотеку для этого приложения (и всех будущих приложений), которая обрабатывает все настройки и обработку тяжелых изображений (изображений, видео, звука), связь с веб-службой, анализ и отображение результатов, обработка CoreData и т.д.

Для моего приложения существуют сценарии, когда выполняется множество параллельных задач (в худшем случае), например. пользователь в настоящее время меняет изображение своего профиля, в то время как приложение отправляет местоположение пользователя на сервер (в фоновом режиме) и получает новое push-уведомление.

Поэтому было принято решение инкапсулировать каждую логическую операцию (например, SendUserLocation или GetCurrentFriendList) в NSOperation и добавить их в serviceQueue (NSOperationQueue).

Каждая операция может порождать подзадачи, когда операция успешно получена из веб-службы и должна обрабатывать ее сейчас.

alt text

Типичный метод ServiceManager выглядит как

- (void)activateFriendsSync:(id)observer onSuccess:(SEL)selector {
    ELOSyncFriends *opSyncFriends  = [[ELOSyncFriends alloc] initWithSM:self];
    [self ELServiceLogger:opSyncFriends];
    [serviceQueue addOperation:opSyncFriends];
    if(observer) {
        [self registerObserver:observer selector:selector name:opSyncFriends.notificationName]; 
    }
}

Каждая операция, запрос (к серверу) и subTask использует GUID как имя уведомления для уведомления родительского объекта при его обработке. Если все в операции выполнено, оно отправляет уведомление обратно в пользовательский интерфейс.

Тем не менее, код для добавления и удаления подзадач выглядит следующим образом:

- (void)removeSubTask:(NSNotification*)notification {
    ELRequest *request = (ELRequest*)[notification object];
    [subTasks removeObjectIdenticalTo:request.notificationName];
    if([subTasks count] == 0) {
         // all SubTaks done, send notification to parent
        [serviceManager.notificationCenter postNotificationName:self.notificationName object:request];
    }
}

- (NSString*)addSubTask {
    NSString* newName = [self GetUUID];
    [subTasks addObject:[newName retain]];
    [serviceManager.notificationCenter addObserver:self selector:@selector(removeSubTask:) name:newName object:nil];
    return newName;
} 

- (NSString *)GetUUID {
    CFUUIDRef theUUID = CFUUIDCreate(NULL);
    CFStringRef string = CFUUIDCreateString(NULL, theUUID);
    CFRelease(theUUID);
    return [(NSString *)string autorelease];
}

Теперь все, что мне нужно сделать, - вызвать serviceManager в моем gui, чтобы запустить определенную операцию, например

[self.core.serviceManager activateFriendsSync:nil onSuccess:nil];

Если я хочу зарегистрировать наблюдателя, я просто передаю объект-наблюдатель и селектор, подобный этому

[self.core.serviceManager activateFriendsSync:self onSuccess:@selector(myMethod:)];

И последнее, но не менее важно мой вопрос (ы): "Архитектура" работает очень хорошо и стабильно, но стоит ли это делать? Это создает слишком много накладных расходов? Это даже имеет смысл? Как вы лично выполняете параллельные операции?

Лучший Хенрик

P.S. Не стесняйтесь редактировать мой вопрос, задавать вопросы (как комментарий), называть меня именами для этого мышления.

Мне действительно было трудно объяснить это, в основном потому, что я не являюсь носителем английского языка. И не пойми меня неправильно. Я не писал эту публикацию, чтобы хвастаться. Все, что я хочу сделать, это узнать (и, возможно, написать более продвинутый вопрос iphone/цель c)

Ответ 1

да, если он обрабатывается запросами на обслуживание и у вас много вызовов, то такая библиотека не будет (imo) overkill, и я написал что-то подобное. эта структура очень упростила мне управление сложной системой с очень сложными и разнообразными задачами.

основная разница в дизайне, которую я сделал, заключалась в том, чтобы не использовать NSNotification с менеджером сервиса. вместо этого я предпочитаю использовать протоколы/типы для обратных вызовов (к которым операция содержит ссылку). NSNotification довольно тяжелая. в этом случае операция не сохраняет слушателя (-ы)/уведомленные объекты, но слушатели сохраняют операцию. если отношение равно 1-1, то разрешите аннулирование.

Еще одно важное соображение заключается в том, чтобы определить потоки на раннем этапе. разрешить клиентам определять, на какой поток они хотят получить ответ. причина этого заключается в том, что часто существует ограничение или логическая запись для обратного вызова, если уведомленный/прослушиватель должен обновить интерфейс (например, вы используете UIKit или AppKit). поэтому создатель может сказать операции "вы должны сообщить мне из основного потока" или "я могу обработать ответ из любого потока". это уменьшит ваш код контроллера/слушателя/наблюдателя и вероятность ошибок.

Ответ 2

Для "вспомогательных операций": как разместить их в очереди, при этом родительская операция будет зависимой (cf. -[ NSOperation addDependency: ]) каждой из своих дочерних операций? NSOperationQueue может упорядочить всю вашу кучу операций для вас. Я думаю, что это просто и естественно работать.

Ответ 3

Вы только что описали очень похожую архитектуру, которую я использую в нескольких своих приложениях.

У меня есть мой уровень менеджера сервиса, который возвращает набор объектов мгновенно, а затем возвращает обновленный набор через некоторое время.

NSArray *stuff = [serviceManager getFriendsForUser:@"Bob"];

а затем, после ответа сервера, получен NSNotification, который содержит обновленный список (в этом случае друзья для Боба).

Помимо этого крошечного изменения, ваша архитектура одинакова!

Это очень много работы, чтобы все было настроено, но я думаю, что это стоит того, что исправление ошибок/расширение кода намного проще.