?
批准的答案很棒,但如果你正在寻找一个1分钟的答案,请尝试以下方法:
MyClass.h文件应该像这样(添加带有注释的委托行!)
#import@class MyClass; //define class, so protocol can see MyClass @protocol MyClassDelegate //define delegate protocol - (void) myClassDelegateMethod: (MyClass *) sender; //define delegate method to be implemented within another class @end //end protocol @interface MyClass : NSObject { } @property (nonatomic, weak) id delegate; //define MyClassDelegate as delegate @end
MyClass.m文件应该如下
#import "MyClass.h" @implementation MyClass @synthesize delegate; //synthesise MyClassDelegate delegate - (void) myMethodToDoStuff { [self.delegate myClassDelegateMethod:self]; //this will call the method implemented in your other class } @end
在另一个类(在这种情况下,称为MyVC的UIViewController)中使用您的代理MyVC.h:
#import "MyClass.h" @interface MyVC:UIViewController{ //make it a delegate for MyClassDelegate }
MyVC.m:
myClass.delegate = self; //set its delegate to self somewhere
实现委托方法
- (void) myClassDelegateMethod: (MyClass *) sender { NSLog(@"Delegates are great!"); }
Objective-C的代理是被分配到另一个对象的delegate
属性的对象。要创建一个代理,您可以定义一个类来实现您感兴趣的代理方法,并将该类标记为实现代理协议。
例如,假设你有一个UIWebView
。 如果您想要实现其代理的webViewDidStartLoad:
方法,您可以创建如下类:
@interface MyClass// ... @end @implementation MyClass - (void)webViewDidStartLoad:(UIWebView *)webView { // ... } @end
然后,您可以创建MyClass的实例并将其分配为web视图的代理:
MyClass *instanceOfMyClass = [[MyClass alloc] init]; myWebView.delegate = instanceOfMyClass;
在UIWebView
方面,它可能有类似于以下代码的代码,以查看代理是否对于webViewDidStartLoad:
消息做出反应,使用respondsToSelector:
以及在适当时发送它。
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { [self.delegate webViewDidStartLoad:self]; }
代理属性本身通常被声明为weak
(在ARC中)或assign
(在ARC之前)以避免保留循环,因为对象的代理通常对该对象持有强引用。 (例如,视图控制器通常是其包含的视图的代理。)
为您的类制作代理
为了定义自己的代理,你需要在某个地方声明它们的方法,就像在Apple docs on protocols中所讨论的那样。您通常声明一个正式协议。该声明(从UIWebView.h派生)如下所示:
@protocol UIWebViewDelegate@optional - (void)webViewDidStartLoad:(UIWebView *)webView; // ... other methods here @end
这类似于接口或抽象基类,因为它为您的代理创建了一个特殊类型,例如,在本例中的UIWebViewDelegate
。代理实现者必须采用此协议:
@interface MyClass// ... @end
然后实现协议中的方法。对于在协议中声明为@optional
的方法(如大多数代理方法),您需要在调用特定方法之前使用-respondsToSelector:
进行检查。
命名
代理方法通常以委托类名开头,以委托对象作为第一个参数。它们还经常使用will-,should-或did-形式。例如,webViewDidStartLoad:
(第一个参数是web视图)而不是loadStarted
(不带参数)。
速度优化
可以在设置代理时缓存委托是否响应选择器的信息,而不是每次我们想要发送消息时都进行检查。一种非常简洁的方法是使用位域,如下所示:
@protocol SomethingDelegate@optional - (void)something:(id)something didFinishLoadingItem:(id)item; - (void)something:(id)something didFailWithError:(NSError *)error; @end @interface Something : NSObject @property (nonatomic, weak) id delegate; @end @implementation Something { struct { unsigned int didFinishLoadingItem:1; unsigned int didFailWithError:1; } delegateRespondsTo; } @synthesize delegate; - (void)setDelegate:(id )aDelegate { if (delegate != aDelegate) { delegate = aDelegate; delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)]; delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)]; } } @end
然后,在代码主体中,我们可以通过访问delegateRespondsTo
结构体来检查我们的委托处理消息,而不是一遍又一遍地发送-respondsToSelector:
。
非正式代理
在协议出现之前,常常使用NSObject
的一个类别来声明代理可以实现的方法。例如,CALayer
仍然使用这种方法:
@interface NSObject(CALayerDelegate) - (void)displayLayer:(CALayer *)layer; // ... other methods here @end
这告诉编译器,任何对象都可以实现displayLayer:
方法。
然后,您会使用上面描述的相同的-respondsToSelector:
方法来调用此方法。委托实现此方法并分配delegate
属性,就完成了这一操作(不需要声明您符合协议)。这种方法在Apple的库中很常见,但新的代码应该使用上面更现代的协议方法,因为这种方法会污染NSObject
(使自动完成不太有用)并使编译器很难警告您有关拼写错误和类似错误的内容。