问题描述
我知道代表如何工作,我知道如何使用它们。
但是如何创建呢?
例如,假设你有一个 UIWebView
。如果您要实现其代理的方法,您可以创建如下类:
@interface MyClass< UIWebViewDelegate>
// ...
@end
@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView {
// .. 。
}
@end
然后你可以创建一个MyClass实例,将其分配为Web视图的委托:
MyClass * instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;
在 UIWebView
代码类似于此查看代理是否响应 webViewDidStartLoad:
消息使用并在适当时发送。
if([self.delegate respondingToSelector:@selector(webViewDidStartLoad :)]){
[self.delegate webViewDidStartLoad:self];
}
委托属性本身通常被声明为 weak
(在ARC中)或
assign
(pre-ARC)以避免保留循环,因为对象的委托通常持有对该对象的强引用。 (例如,视图控制器通常是其包含的视图的委托。)
为您的类创建委派
要定义自己的代理,您必须在某处声明他们的方法,如。您通常声明一个正式协议。从UIWebView.h释义的声明如下:
@protocol UIWebViewDelegate< NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ...这里的其他方法
@end
到接口或抽象基类,因为它为您的委托创建了一个特殊类型,UIWebViewDelegate在这种情况下。代理实现者必须采用此协议:
@interface MyClass< UIWebViewDelegate>
// ...
@end
协议。对于在协议中声明为 @optional
(像大多数委托方法)的方法,您需要检查 -respondsToSelector:
之前调用一个特定的方法就可以了。
命名
委托方法通常从委托类名称开始命名,并将委托对象作为第一参数。他们也经常使用意志,应该或确实。因此, webViewDidStartLoad:
(第一个参数是Web视图),而不是 loadStarted
(不带参数)。
速度优化
不是在每次我们要发送消息时检查代理是否响应选择器,您可以在设置代理时缓存该信息。一个非常干净的方法是使用位域,如下所示:
@protocol SomethingDelegate< NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end
@interface Something:NSObject
@property(nonatomic,weak)id< SomethingDelegate>代表;
@end
@implementation某事{
struct {
unsigned int didFinishLoadingItem:1;
unsigned int didFailWithError:1;
} delegateRespondsTo;
}
@synthesize delegate;
- (void)setDelegate:(id< JSSomethingDelegate>)aDelegate {
if(delegate!= aDelegate){
delegate = aDelegate;
delegateRespondsTo.didFinishLoadingItem = [delegate responsesToSelector:@selector(something:didFinishLoadingItem :)];
delegateRespondsTo.didFailWithError = [delegate responsesToSelector:@selector(something:didFailWithError :)];
}
}
@end
,我们可以通过访问我们的 delegateRespondsTo
结构来检查我们的代理处理消息,而不是发送 -respondsToSelector:
非正式代表
在协议存在之前,通常使用 NSObject
来声明一个委托可以实现的方法。例如, CALayer
仍然这样做:
@interface NSObject(CALayerDelegate )
- (void)displayLayer:(CALayer *)layer;
// ...这里的其他方法
@end
任何对象可能实现 displayLayer:
的编译器。
-respondsToSelector:方法调用此方法。代理只需实现此方法并分配 delegate
属性,就是它(没有声明你符合协议)。这种方法在苹果的库中很常见,但是新代码应该使用上面更现代的协议方法,因为这种方法污染了 NSObject
(这使得自动完成不太有用)编译器警告您有关错误和类似错误。
I know how delegates work, and I know how I can use them.
But how do I create them?
An Objective-C delegate is an object that has been assigned to the delegate
property another object. To create one, you simply define a class that implements the delegate methods you're interested in, and mark that class as implementing the delegate protocol.
For example, suppose you have an UIWebView
. If you'd like to implement its delegate's webViewDidStartLoad: method, you could create a class like this:
@interface MyClass<UIWebViewDelegate>
// ...
@end
@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView {
// ...
}
@end
Then you could create an instance of MyClass and assign it as the web view's delegate:
MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;
On the UIWebView
side, it probably has code similar to this to see if the delegate responds to the webViewDidStartLoad:
message using respondsToSelector: and send it if appropriate.
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
The delegate property itself is typically declared weak
(in ARC) or assign
(pre-ARC) to avoid retain loops, since the delegate of an object often holds a strong reference to that object. (For example, a view controller is often the delegate of a view it contains.)
Making Delegates for Your Classes
To define your own delegates, you'll have to declare their methods somewhere, as discussed in the Apple Docs on protocols. You usually declare a formal protocol. The declaration, paraphrased from UIWebView.h, would look like this:
@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end
This is analogous to an interface or abstract base class, as it creates a special type for your delegate, UIWebViewDelegate in this case. Delegate implementors would have to adopt this protocol:
@interface MyClass <UIWebViewDelegate>
// ...
@end
And then implement the methods in the protocol. For methods declared in the protocol as @optional
(like most delegate methods), you need to check with -respondsToSelector:
before calling a particular method on it.
Naming
Delegate methods are typically named starting with the delegating class name, and take the delegating object as the first parameter. They also often use a will-, should-, or did- form. So, webViewDidStartLoad:
(first parameter is the web view) rather than loadStarted
(taking no parameters) for example.
Speed Optimizations
Instead of checking whether a delegate responds to a selector every time we want to message it, you can cache that information when delegates are set. One very clean way to do this is to use a bitfield, as follows:
@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end
@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end
@implementation Something {
struct {
unsigned int didFinishLoadingItem:1;
unsigned int didFailWithError:1;
} delegateRespondsTo;
}
@synthesize delegate;
- (void)setDelegate:(id <JSSomethingDelegate>)aDelegate {
if (delegate != aDelegate) {
delegate = aDelegate;
delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
}
}
@end
Then, in the body, we can check that our delegate handles messages by accessing our delegateRespondsTo
struct, rather than by sending -respondsToSelector:
over and over again.
Informal Delegates
Before protocols existed, it was common to use a category on NSObject
to declare the methods a delegate could implement. For example, CALayer
still does this:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
This essentially tells the compiler that any object might implement displayLayer:
.
You would then use the same -respondsToSelector:
approach as described above to call this method. Delegates simply implement this method and assign the delegate
property, and that's it (there's no declaring you conform to a protocol). This method is common in Apple's libraries, but new code should use the more modern protocol approach above, since this approach pollutes NSObject
(which makes autocomplete less useful) and makes it hard for the compiler to warn you about typos and similar errors.
这篇关于如何在Objective-C中创建委托?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!