Делегат для объекта singleton
У меня есть NSObject, который является singleton. Есть ли проблема наличия делегата для этого одноэлементного класса? Я обеспокоен тем, что он будет терпеть неудачу для одноэлементного типа.
Вот мой сценарий. У меня есть функция (внутри этого одиночного класса), которая выполняет запрос async, чтобы вытащить NSDictionary из API. В основном, когда этот запрос завершен, я хочу уведомить класс о завершении запроса.
Ответы
Ответ 1
Нет, делегат не потерпит неудачу, но вместо этого используйте NSNotificationCenter:
static NSString *const kMyClassNotificationName = @"myClassNotificationName";
// where you would call a delegate method (e.g. [self.delegate doSomething])
[[NSNotificationCenter defaultCenter] postNotificationName:kMyClassNotificationName object:self userInfo: /* dictionary containing variables to pass to the delegate */];
// where you would set up a delegate (e.g. [Singleton instance].delegate = self)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doSomething) name:kMyClassNotificationName object:[Singleton instance]];
Ответ 2
У вас есть в основном три варианта:
-
Используйте делегат. Singelton является объектом, поэтому couse может иметь делегат. Если несколько объектов хотят использовать его и должны быть настроены как делегаты, вы можете reset их каждый раз, но это может быть волосатым.
-
Используйте уведомления, как показано Ричардом Дж. Росс III., но серьезно: мне кажется странным, если у вас есть синглтон, который должен сообщить одному делегату, но вы будете использовать трансляцию технология.
-
используют блоки завершения, где вызывающие объекты передают блок в одноэлементный, который запускается, как только синглтон выполнил задачу. См. [NSURLConnection sendAsynchronousRequest:queue:completionHandler:]
(это не одноэлемент, а метод класса. Принцип тот же), который использует один блок завершения, или большой AFNetworking, который использует успех и блок отказов.
Из него примерные коды:
[[AFGowallaAPIClient sharedClient] getPath:urlString
parameters:mutableParameters
success:^(__unused AFHTTPRequestOperation
*operation,
id JSON)
{
NSMutableArray *mutableRecords = [NSMutableArray array];
for (NSDictionary *attributes in [JSON valueForKeyPath:@"spots"]) {
Spot *spot = [[[Spot alloc] initWithAttributes:attributes] autorelease];
[mutableRecords addObject:spot];
}
if (block) {
block([NSArray arrayWithArray:mutableRecords]);
}
} failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) {
if (block) {
block([NSArray array]);
}
}];
Ответ 3
Нет ничего плохого в том, что делегат для синглтона, но он создает много крайних случаев, которые вам нужно обрабатывать. Например:
-
Если объект A вызывает setDelegate:, немедленно следует объект B, вызывающий setDelegate: тогда объект A никогда не получит вызовы делегатов.
-
Вам нужно проверить, являетесь ли вы делегатом, прежде чем отключать делегат singleton. Обычно в dealloc
вы вызываете singleton.delegate = nil;
. Если другой объект стал делегировать после того, как вы это сделали, вы просто заставили их неожиданно перестать быть делегатом.
Синглтоны с делегатами - это не устоявшийся образец. Ваши решения должны различаться в зависимости от того, насколько надежным является ваш вариант использования. Вот несколько решений (в порядке проще → наиболее надежных).
Держите его простым
Создайте приложение, чтобы никогда не иметь одновременно нескольких делегатов singleton (это может быть невозможно).
NSNotification
Используйте NSNotificationCenter для передачи сигналов вместо делегирования. См. Некоторые другие ответы, размещенные в этой теме.
Несколько делегатов
Расширьте свой синглтон, чтобы поддерживать несколько делегатов. Замените setDelegate:
на: addDelegate:
и removeDelegate:
@property (atomic) NSMutableArray *delegates;
- (void)addDelegate:(NSObject * <YourProtocol>)foo {
[self.delegates addObject:foo];
}
- (void)removeDelegate:(NSObject * <YourProtocol>)foo {
[self.delegates removeObject:foo];
}
- (void)signalDelegateEvent {
[self.delegates enumerateObjectsUsingBlock:^(id<YourProtocol> obj,
NSUInteger idx,
BOOL *stop) {
// call delegate method `foo` on each delegate
if ( [obj respondsToSelector:@selector(foo)]) {
[obj foo];
}
}];
}
Я успешно использовал шаблон с несколькими делегатами во многих приложениях. Будьте осторожны, подумайте о том, как многопоточность влияет на вещи, если вы выберете этот подход.