如何在Objective-C中执行回调


Answers:


94

通常,目标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 responsesToSelector:@selector(myClassWillDoSomething :)){”之类的东西来保护它们。在我的示例中,委托必须实现两种方法。

除了非正式协议,您还可以使用通过@protocol定义的正式协议。如果这样做,则可以将委托设置器的类型和实例变量更改为“ id <MyClassDelegate>”,而不仅仅是“id ”。

此外,您会注意到未保留该委托。通常这样做是因为“拥有”“ MyClass”实例的对象通常也是委托。如果MyClass保留了其委托,则将存在一个保留周期。在具有MyClass实例并且是其委托以清除该委托引用的类的dealloc方法中,这是一个好主意,因为它是一个弱返回指针。否则,如果使MyClass实例保持活动状态,则将有一个悬空指针。


+1好彻底的答案。锦上添花将成为指向代表的更深入的Apple文档的链接。:-)
奎因·泰勒

乔恩,非常感谢您的帮助。非常感谢您的帮助。对此我感到很抱歉,但是我对答案不太清楚。Message .m是在doSomething函数调用期间将自己设置为委托的类。是用户调用的回调函数吗?因为我印象中用户调用了doSomething,而您的回调函数是myClassWillDoSomethingg和myClassDidDoSomething。同样,您能告诉我如何创建一个更高的类来调用回调函数。我是C程序员,所以到目前为止我还不太熟悉Obj-C环境。
ReachConnection

“ Message .m”仅表示您是.m文件。您将有一个单独的类,我们将其称为“ Foo”。Foo将具有“ MyClass * myClass”变量,并且在某个时候Foo会说“ [myClass setDelegate:self]”。在此之后的任何时刻,如果包括foo在内的任何人都在该MyClass实例上调用了doSomethingMethod,则foo将具有其myClassWillDoSomething和myClassDidDoSomething方法。实际上,我还将发布另一个不使用委托的示例。
乔恩·赫斯

我不认为.m代表“消息”。
查克(Chuck)


140

为了完整起见,由于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);
}];

2
@Ahruman:“ void(^ _completionHandler)(int someParameter);”中的“ ^”字符是什么?意思?您能解释一下这行吗?
KonradHöffner'12

2
您能否解释为什么需要复制回调处理程序?
Elliot Chance

52

这是一个示例,它使委托的概念保持不变,并且仅进行原始回叫。

@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
感谢您发布此Jon,它非常有帮助。我不得不更改[object performSelectorwithObject:self]; 到[object performSelector:selector withObject:self];为了使其正常工作。
Banjer 2010年

16

为了使这个问题保持最新,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);
}];

如果您忘记了Objective-C的Block语法,总会有F **** ng Block语法。


在@interface中应该+ (void)sayHi:(void(^)(NSString *reply))callback;不是+ (void)sayHi:(void(^)(NSString *))callback;
Tim007'3

不按上述˚F****纳克块语法- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;(注意parameterTypes不是parameters
赖恩·布罗迪

4

回调:目标C中有4种回调类型

  1. 选择器类型:您可以看到NSTimer,UIPangesture是选择器回调的示例。用于非常有限的代码执行。

  2. 代表类型:常见,在Apple框架中最常用。UITableViewDelegate,NSNURLConnectionDelegate。它们通常用于显示从服务器异步下载许多图像等。

  3. NSNotifications:NotificationCenter是Objective C的功能之一,用于在事件发生时通知许多接收者。
  4. :块在Objective C编程中更常用。这是一个很棒的功能,用于执行代码块。您也可以参考教程以了解:块教程

如果有其他答案,请让我。我会很感激的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.