Як виконувати функції зворотного дзвінка в Objective-C?
Я просто хотів би побачити кілька завершених прикладів, і я повинен це зрозуміти.
Як виконувати функції зворотного дзвінка в Objective-C?
Я просто хотів би побачити кілька завершених прикладів, і я повинен це зрозуміти.
Відповіді:
Зазвичай зворотні виклики в об'єктивному C здійснюються з делегатами. Ось приклад реалізації користувацького делегата;
Файл заголовка:
@interface MyClass : NSObject {
id delegate;
}
- (void)setDelegate:(id)delegate;
- (void)doSomething;
@end
@interface NSObject(MyDelegateMethods)
- (void)myClassWillDoSomething:(MyClass *)myClass;
- (void)myClassDidDoSomething:(MyClass *)myClass;
@end
Файл реалізації (.m)
@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 respondsToSelector: @selector (myClassWillDoSomething :)) {". У моєму прикладі делегат зобов'язаний реалізувати обидва способи.
Замість неформального протоколу ви також можете використовувати офіційний протокол, визначений за допомогою @protocol. Якщо ви це зробите, ви змінили б тип делегатного сеттера та змінну екземпляра на " id <MyClassDelegate>
", а не просто " id
".
Також ви помітите, що делегат не зберігається. Зазвичай це робиться тому, що об'єкт, який "володіє" екземплярами "MyClass", як правило, також є делегатом. Якби MyClass зберігав свого делегата, тоді був би цикл збереження. Добре ідея методу dealloc класу, який має екземпляр MyClass і є його делегатом, щоб очистити цей посилання делегата, оскільки це слабкий зворотний покажчик. Інакше якщо щось підтримує екземпляр MyClass, ви матимете звисаючий покажчик.
Для повноти, оскільки RSS StackOverflow просто випадковим чином відновив для мене питання, іншим (новішим) варіантом є використання блоків:
@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);
}];
Ось приклад, який виключає поняття делегатів і просто виконує необмежений зворотний дзвінок.
@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:] буде асинхронним, що зробить зворотний виклик кориснішим, ніж тут.
Щоб це питання було актуальним, впровадження ARC iOS 5.0 означає, що це можна досягти за допомогою 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 **** і синтаксис блоку, якщо ви коли-небудь забудете синтаксис блоку Objective-C.
+ (void)sayHi:(void(^)(NSString *reply))callback;
НЕ+ (void)sayHi:(void(^)(NSString *))callback;
- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;
(Примітка parameterTypes
НЕ parameters
)
CallBack: В Об'єкті C є 4 типи зворотних дзвінків
Тип вибору : Ви можете побачити NSTimer, UIPangesture - приклади зворотного виклику Selector. Використовується для дуже обмеженого виконання коду.
Тип делегата : поширений і найчастіше використовується в рамках Apple. UITableViewDelegate, NSNURLConnectionDelegate. Зазвичай вони використовуються для показу асинхронного завантаження багатьох зображень із сервера тощо.
Будь ласка, дозвольте мені, якщо будь-які інші відповіді на це. Я оціню це.