如何在Objective-C中创建委托?
认可的答案很好, 但如果你想要一个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的实例并将其指定为WebView的代理:
MyClass *instanceOfMyClass = [[MyClass alloc] init]; myWebView.delegate = instanceOfMyClass;
在UIWebView
方面,它可能有类似于以下内容的代码,以查看代理是否使用respondsToSelector:
响应webViewDidStartLoad:
消息并在适当的时候发送它。
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { [self.delegate webViewDidStartLoad:self]; }
代理属性本身通常被声明为weak
(在ARC中)或assign
(在ARC之前)以避免保留循环,因为一个对象的代理通常对该对象持有强引用。(例如,视图控制器通常是其包含的视图的代理)。
为您的类创建代理
要定义您自己的代理,您必须要在某个地方声明它们的方法,就像在Apple关于协议的文档中讨论的那样。通常情况下,您会声明正式的协议。从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 code>结构来检查我们的委托是否处理消息,而不是一遍又一遍地发送
-respondsToSelector: code>。
非正式委托
在协议存在之前,通常会使用 NSObject
上的< a href =“ http://www.google.ca/search?q=site:developer.apple.com+category+objective+c&btnI” rel =“noreferrer”>类别来声明委托可以实现的方法。例如, CALayer
仍然在这样做:
@interface NSObject(CALayerDelegate) - (void)displayLayer:(CALayer *)layer; // ... other methods here @end
这告诉编译器,任何对象都可能实现 displayLayer:
方法。
然后,您可以使用与上面描述的相同的 -respondsToSelector:
方法来调用此方法。 代理实现此方法并分配 delegate
属性,就这样(没有声明您符合协议)。 这种方法在苹果的库中很常见,但新代码应使用上面更现代的协议方法,因为这种方法污染了 NSObject
(使自动完成变得不太有用)并使它难以警告您有关拼写错误等错误。