2009-06-18 2 views
109

Как выполнять функции обратного вызова в Objective-C?Как выполнять обратные вызовы в Objective-C

Я просто хотел бы увидеть некоторые завершенные примеры, и я должен это понять.

+3

Этот вопрос изумительный, и ответы действительно полезны. –

+0

не представлял себе, что это так сложно понять ......... –

ответ

89

Обычно обратные вызовы в объекте C выполняются с помощью делегатов. Вот пример пользовательской реализации делегата;

/// Header File 
@interface MyClass : NSObject { 
    id delegate; 
} 
- (void)setDelegate:(id)delegate; 
- (void)doSomething; 
@end 

@interface NSObject(MyDelegateMethods) 
- (void)myClassWillDoSomething:(MyClass *)myClass; 
- (void)myClassDidDoSomething:(MyClass *)myClass; 
@end 


/// Message (.m) File 
@implementation MyClass 
- (void)setDelegate:(id)aDelegate { 
    delegate = aDelegate; /// Not retained 
} 

- (void)doSomething { 
    [delegate myClassWillDoSomething:self]; 
    /* DO SOMETHING */ 
    [delegate myClassDidDoSomething:self]; 
} 
@end 

Это иллюстрирует общий подход. Вы создаете категорию в NSObject, которая объявляет имена ваших методов обратного вызова. NSObject фактически не реализует эти методы. Этот тип категории называется неофициальным протоколом, вы просто говорите, что многие объекты могут реализовать эти методы. Это способ переслать объявить подпись типа селектора.

Далее у вас есть объект, являющийся делегатом «MyClass», а MyClass при необходимости вызывает методы делегата на делегате. Если ваши обратные вызовы делегатов являются необязательными, вы обычно охраняете их на сайте отправки с чем-то вроде «if ([delegate отвечаетSoSelector: @selector (myClassWillDoSomething :)) {". В моем примере делегат должен реализовать оба метода.

Вместо неофициального протокола вы также можете использовать формальный протокол, определенный с помощью @protocol. Если вы это сделаете, вы измените тип установщика делегата, а переменную экземпляра будет «id <MyClassDelegate>» вместо «id».

Кроме того, вы заметите, что делегат не сохраняется. Обычно это делается потому, что объект, который «владеет» экземплярами «MyClass», как правило, также является делегатом. Если MyClass сохранил свой делегат, тогда будет цикл сохранения. Это хорошая идея в методе dealloc класса, который имеет экземпляр MyClass, и является его делегатом, чтобы очистить эту ссылку делегата, поскольку это слабая обратная указатель. В противном случае, если что-то поддерживает экземпляр MyClass, у вас будет висячий указатель.

+0

+1 Хороший тщательный ответ. Обледенение на торте было бы ссылкой на более подробную документацию Apple на делегатов. :-) –

+0

Джон, спасибо большое за вашу помощь. Я действительно ценю твою помощь. Прошу прощения за это, но я не совсем понимаю ответ. Сообщение .m - это класс, который задает себя как делегат во время вызова функции doSomething. Функция doSomething вызывает функцию обратного вызова, которую вызывает пользователь? так как у меня создается впечатление, что пользователь вызывает doSomething, а ваши функции обратного вызова - myClassWillDoSomethingg & myClassDidDoSomething. Также вы можете показать мне, как создать более высокий класс, который вызывает функцию обратного вызова. Я программист на C, поэтому пока не знаком с областью Obj-C. – ReachConnection

+0

«Сообщение .m» просто означает, что вы .m-файл. У вас будет отдельный класс, назовем его «Foo». Foo будет иметь переменную MyClass * myClass, и в какой-то момент Foo скажет «[myClass setDelegate: self]». В любой момент после этого, если кто-то, включая foo, вызвал doSomethingMethod в этом экземпляре MyClass, foo будет иметь его myClassWillDoSomething и вызовы myClassDidDoSomething. На самом деле я просто отправлю второй пример, который не использует делегатов. –

46

Вот пример, в котором содержатся концепции делегатов и просто выполняется обратный вызов.

@interface Foo : NSObject { 
} 
- (void)doSomethingAndNotifyObject:(id)object withSelector:(SEL)selector; 
@end 

@interface Bar : NSObject { 
} 
@end 

@implementation Foo 
- (void)doSomethingAndNotifyObject:(id)object withSelector:(SEL)selector { 
    /* do lots of stuff */ 
    [object performSelector:selector withObject:self]; 
} 
@end 

@implementation Bar 
- (void)aMethod { 
    Foo *foo = [[[Foo alloc] init] autorelease]; 
    [foo doSomethingAndNotifyObject:self withSelector:@selector(fooIsDone:)]; 
} 

- (void)fooIsDone:(id)sender { 
    NSLog(@"Foo Is Done!"); 
} 
@end 

Обычно метод - [Foo doSomethingAndNotifyObject: withSelector:] будет асинхронным, который сделает обратный вызов более полезным, чем здесь.

+1

Большое спасибо. Я понимаю вашу первую реализацию обратного вызова после ваших комментариев. Кроме того, ваша вторая реализация обратного вызова более проста. Оба очень хороши. – ReachConnection

+1

Спасибо, что отправил этого Джона, это было очень полезно. Мне пришлось изменить [object performSelectorwithObject: self]; to [object performSelector: селектор withObject: self]; чтобы заставить его работать правильно. – Banjer

+0

Конечно, это классический ответ StackOverflow :) Эй, кто-то должен немного расшириться: «Обычно метод будет асинхронным ...», может быть, пример этого? Потрясающие. – Fattie

130

Для полноты, так как StackOverflow RSS просто случайно воскресила вопрос для меня, другой (новый) вариант заключается в использовании блоков:

@interface MyClass: NSObject 
{ 
    void (^_completionHandler)(int someParameter); 
} 

- (void) doSomethingWithCompletionHandler:(void(^)(int))handler; 
@end 


@implementation MyClass 

- (void) doSomethingWithCompletionHandler:(void(^)(int))handler 
{ 
    // NOTE: copying is very important if you'll call the callback asynchronously, 
    // even with garbage collection! 
    _completionHandler = [handler copy]; 

    // Do stuff, possibly asynchronously... 
    int result = 5 + 3; 

    // Call completion handler. 
    _completionHandler(result); 

    // Clean up. 
    [_completionHandler release]; 
    _completionHandler = nil; 
} 

@end 

... 

MyClass *foo = [[MyClass alloc] init]; 
int x = 2; 
[foo doSomethingWithCompletionHandler:^(int result){ 
    // Prints 10 
    NSLog(@"%i", x + result); 
}]; 
+0

люблю это решение - похоже на выполнение анонимной функции для обратных вызовов, просто, меньше кода для написания. – David

+2

@Ahruman: что означает символ «^» в «void (^ _completionHandler) (int someParameter)»; имею в виду? Не могли бы вы объяснить, что делает эта линия? –

+0

Блоки также упрощают перенос обратных вызовов C-стиля (указатели функций, которые принимают параметры 'void *') в блоки. Я написал пример, как это сделать здесь: [Обтекание обратных вызовов C-стиля с блоками] (http://cutecoder.org/programming/wrapping-style-callbacks-blocks/). – adib

5

Чтобы сохранить этот вопрос уточненный, IOS 5,0-х введение ARC означает, что это может быть достигнуто с помощью Blocks еще более лаконично:

@interface Robot: NSObject 
+ (void)sayHi:(void(^)(NSString *))callback; 
@end 

@implementation Robot 
+ (void)sayHi:(void(^)(NSString *))callback { 
    // Return a message to the callback 
    callback(@"Hello to you too!"); 
} 
@end 

[Robot sayHi:^(NSString *reply){ 
    NSLog(@"%@", reply); 
}]; 

Там всегда F****ng Block Syntax, если вы забыли синтаксис блока Objective-C в.

+0

В @interface должно быть '+ (void) sayHi: (void (^) (NSString * reply)) callback;' not '+ (void) sayHi: (void (^) (NSString *)) callback;' –

+0

Not в соответствии с вышеупомянутым [F **** ng Block Syntax] (http://goshdarnblocksyntax.com/): '- (void) someMethodThatTakesABlock: (returnType (^ nullability) (parameterTypes)) blockName;' (Примечание 'parameterTypes' not 'parameters') –

3

CallBack: Есть 4 типа обратного вызова в Objective C

  1. Выбор типа: Вы можете видеть NSTimer, UIPangesture являются примерами Selector обратного вызова. Используется для очень ограниченного выполнения кода.

  2. Делегат Тип: Обычный и наиболее используемый в рамках Apple. UITableViewDelegate, NSNURLConnectionDelegate. Они обычно используются для демонстрации загрузки многих изображений с сервера асинхронно и т. Д.

  3. NSNotifications: NotificationCenter - одна из особенностей Objective C, которая используется для уведомления многих получателей в момент возникновения события.
  4. Блоки: Блоки чаще используются в программировании Objective C. Это отличная функция и используется для выполнения куска кода. Вы также можете обратиться к учебнику, чтобы понять: Blocks tutorial

Пожалуйста, позвольте мне ответить на любой другой вопрос. Я буду признателен.