【问题标题】:How to perform Callbacks in Objective-C如何在 Objective-C 中执行回调
【发布时间】:2010-11-04 04:13:29
【问题描述】:

如何在 Objective-C 中执行回调函数?

我只是想看看一些完整的例子,我应该理解它。

【问题讨论】:

    标签: objective-c cocoa callback


    【解决方案1】:

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

    【讨论】:

    • @Ahruman:“void (^_completionHandler)(int someParameter);”中的“^”字符是什么意思?意思是?你能解释一下那条线的作用吗?
    • 您能否解释一下为什么需要复制回调处理程序?
    【解决方案2】:

    通常,目标 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 保留了它的委托,那么就会有一个保留周期。在具有 MyClass 实例并且是清除该委托引用的委托的类的 dealloc 方法中这是一个好主意,因为它是一个弱反向指针。否则,如果某些东西使 MyClass 实例保持活动状态,您将有一个悬空指针。

    【讨论】:

    • +1 好彻底的答案。锦上添花将是指向更深入的 Apple 代表文档的链接。 :-)
    • 乔恩,非常感谢您的帮助。我真的很感谢你的帮助。对此我很抱歉,但我对答案不太清楚。 Message .m 是一个在 doSomething 函数调用期间将自己设置为委托的类。 doSomething 是用户调用的回调函数吗?因为我的印象是用户调用 doSomething,而您的回调函数是 myClassWillDoSomethingg 和 myClassDidDoSomething。另外,你能告诉我如何创建一个调用回调函数的更高类吗?我是一名 C 程序员,所以我对 Obj-C 环境还不太熟悉。
    • "Message .m" 只是意味着,在你的 .m 文件中。你会有一个单独的类,我们称之为“Foo”。 Foo 会有一个“MyClass *myClass”变量,在某些时候 Foo 会说“[myClass setDelegate:self]”。在此之后的任何时候,如果包括 foo 在内的任何人在 MyClass 的该实例上调用 doSomethingMethod,则 foo 将调用其 myClassWillDoSomething 和 myClassDidDoSomething 方法。实际上,我还将发布第二个不使用委托的不同示例。
    • 我不认为 .m 代表“消息”。
    【解决方案3】:

    这里有一个例子,它把委托的概念排除在外,只做一个原始的回调。

    @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:] 将是异步的,这将使回调比这里更有用。

    【讨论】:

    • 非常感谢约翰。我了解您在 cmets 之后的第一个回调实现。同样,您的第二个回调实现更简单。两者都很好。
    • 感谢您发布此 Jon,它非常有帮助。我不得不改变 [object performSelectorwithObject:self];到 [object performSelector:selector withObject:self];为了让它正常工作。
    【解决方案4】:

    为了让这个问题保持最新,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 Syntax

    【讨论】:

    • 在@interface 中应该是+ (void)sayHi:(void(^)(NSString *reply))callback; 而不是+ (void)sayHi:(void(^)(NSString *))callback;
    • 不符合上述F****ng Block Syntax- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;(注意parameterTypes不是parameters
    【解决方案5】:

    CallBack:Objective C 中有 4 种类型的回调

    1. Selector type:可以看到NSTimer、UIPangesture是Selector回调的例子。用于非常有限的代码执行。

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

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

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

    【讨论】:

      猜你喜欢
      • 2015-02-13
      • 2021-10-02
      • 1970-01-01
      • 2016-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-16
      • 2023-03-15
      相关资源
      最近更新 更多