我知道委托是如何工作的,也知道如何使用它们。
但是我如何创建它们呢?
我知道委托是如何工作的,也知道如何使用它们。
但是我如何创建它们呢?
当前回答
免责声明:这是如何创建委托的Swift版本。
So, what are delegates? …in software development, there are general reusable solution architectures that help to solve commonly occurring problems within a given context, these “templates”, so to speak, are best known as design patterns. Delegates are a design pattern that allows one object to send messages to another object when a specific event happens. Imagine an object A calls an object B to perform an action. Once the action is complete, object A should know that B has completed the task and take necessary action, this can be achieved with the help of delegates!
为了更好的解释,我将向你展示如何在一个简单的应用程序中创建一个自定义委托,在类之间传递数据,首先下载或克隆这个starter项目并运行它!
你可以看到一个应用有两个类,ViewController A和ViewController B, B有两个视图在点击时改变ViewController的背景颜色,没什么太复杂的对吧?好,现在让我们用一种简单的方法来思考,当类B的视图被点击时,也改变类A的背景颜色。
问题是,这个视图是类B的一部分,对类A一无所知,所以我们需要找到一种方法在这两个类之间进行通信,这就是委派的作用所在。 我将实现分为6个步骤,以便在需要时将其作为备忘单使用。
步骤1:在ClassBVC文件中寻找步骤1的pragma标记并添加这个
//MARK: step 1 Add Protocol here.
protocol ClassBVCDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}
第一步是创建一个协议,在这种情况下,我们将在类B中创建协议,在协议中,您可以根据实现的需求创建尽可能多的函数。在本例中,我们只有一个简单的函数,它接受一个可选的UIColor作为参数。 在类名的末尾添加单词delegate来命名协议是一个很好的做法,在本例中是ClassBVCDelegate。
步骤2:在ClassVBC中寻找步骤2的pragma标记并添加这个
//MARK: step 2 Create a delegate property here.
weak var delegate: ClassBVCDelegate?
这里我们只是为类创建了一个委托属性,这个属性必须采用协议类型,并且它应该是可选的。此外,您应该在属性之前添加weak关键字,以避免保留周期和潜在的内存泄漏,如果您不知道这意味着什么,现在不用担心,只需要记住添加这个关键字。
步骤3:在ClassBVC的handleTap方法中寻找步骤3中的pragma标记并添加它
//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(tapGesture.view?.backgroundColor)
你需要知道的是,运行应用,点击任意视图,你不会看到任何新行为,这是对的但我想指出的是,应用在调用委托时不会崩溃,这是因为我们将它创建为可选值这就是它不会崩溃的原因即使委托还不存在。现在我们转到ClassAVC文件,把它变成委托的。
第四步:在ClassAVC的handleTap方法中寻找第四步的pragma标记,并像这样将其添加到您的类类型旁边。
//MARK: step 4 conform the protocol here.
class ClassAVC: UIViewController, ClassBVCDelegate {
}
现在ClassAVC采用了ClassBVCDelegate协议,你可以看到你的编译器给你一个错误,说“类型' ClassAVC不符合协议' ClassBVCDelegate ',这只意味着你还没有使用协议的方法,想象一下,当类A采用协议时,就像与类B签署了一个合同,这个合同说“任何采用我的类必须使用我的函数!”
注意:如果你有Objective-C背景,你可能会想,你也可以关闭这个错误,使这个方法是可选的,但令我惊讶的是,Swift语言不支持可选协议,如果你想这样做,你可以为你的协议创建一个扩展,或者在你的协议实现中使用@objc关键字。
就我个人而言,如果我必须创建一个具有不同可选方法的协议,我更喜欢将其分解为不同的协议,这样我将遵循给我的对象一个单一责任的概念,但它可以根据具体的实现而变化。
这是一篇关于可选方法的好文章。
步骤5:在prepareforsegue方法中寻找步骤5的pragma标记,并添加这个
//MARK: step 5 create a reference of Class B and bind them through the `prepareforsegue` method.
if let nav = segue.destination as? UINavigationController, let classBVC = nav.topViewController as? ClassBVC {
classBVC.delegate = self
}
这里我们只是创建了ClassBVC的一个实例并将它的委托分配给self, self是什么呢?self是被委派的类avc !
第六步:最后,在ClassAVC中寻找pragma第六步,让我们使用协议的函数,开始输入func changeBackgroundColor,你会看到它为你自动完成它。你可以在里面添加任何实现,在这个例子中,我们只需要改变背景颜色,添加这个。
//MARK: step 6 finally use the method of the contract
func changeBackgroundColor(_ color: UIColor?) {
view.backgroundColor = color
}
现在运行应用程序!
委托无处不在,你可能会无意识地使用它们,如果你过去创建一个表视图,你使用委托,UIKIT的很多类都围绕着它们工作,很多其他框架也一样,它们解决了这些主要问题。
避免对象的紧密耦合。 修改行为和外观而不需要子类化对象。 允许任务被处理到任何任意对象。
恭喜你,你实现了一个自定义委托,我知道你可能在想,这么麻烦?如果你想成为一名iOS开发者,委托是一个非常重要的设计模式,要记住它们在对象之间是一对一的关系。
你可以在这里看到原始教程
其他回答
Objective-C委托是一个对象,它被分配给另一个对象的委托属性。要创建一个委托协议,您需要定义一个实现感兴趣的委托方法的类,并将该类标记为实现委托协议。
例如,假设你有一个UIWebView。如果你想实现它的委托的webViewDidStartLoad:方法,你可以创建一个这样的类:
@interface MyClass<UIWebViewDelegate>
// ...
@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中关于协议的讨论一样。您通常声明一个正式的协议。这个声明,转述自UIWebView.h,看起来像这样:
@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end
这类似于接口或抽象基类,因为它为你的委托创建了一个特殊类型,在本例中是UIWebViewDelegate。委托实现者必须采用这个协议:
@interface MyClass <UIWebViewDelegate>
// ...
@end
然后实现协议中的方法。对于在协议中声明为@optional的方法(像大多数委托方法一样),在对其调用特定方法之前,需要使用-respondsToSelector:检查。
命名
委托方法通常以委托类名开始命名,并将委托对象作为第一个参数。他们也经常使用will-, should-或did-形式。因此,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> delegate;
@end
@implementation Something {
struct {
unsigned int didFinishLoadingItem:1;
unsigned int didFailWithError:1;
} delegateRespondsTo;
}
@synthesize delegate;
- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
if (delegate != aDelegate) {
delegate = aDelegate;
delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
}
}
@end
然后,在主体中,我们可以通过访问delegateRespondsTo结构体来检查委托是否处理消息,而不是通过反复发送-respondsToSelector:。
非正式代表
在协议出现之前,通常在NSObject上使用category来声明委托可以实现的方法。例如,CALayer仍然这样做:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
这告诉编译器任何对象都可以实现displayLayer:。
然后使用上面描述的相同的-respondsToSelector:方法来调用该方法。委托实现此方法并分配委托属性,仅此而已(没有声明您符合协议)。这种方法在苹果的库中很常见,但是新的代码应该使用上面的更现代的协议方法,因为这种方法会污染NSObject(这使得自动补全不那么有用),并且很难让编译器警告你打字错误和类似的错误。
下面是一个创建委托的简单方法
在.h文件中创建协议。确保它是在协议之前定义的,使用@class后面跟着UIViewController的名字,因为我要使用的协议是UIViewController类>。
步骤:1:创建一个名为“YourViewController”的新类Protocol,它将是UIViewController类的子类,并将这个类分配给第二个ViewController。
步骤2:转到“YourViewController”文件,并如下所示修改它:
#import <UIKit/UIkit.h>
@class YourViewController;
@protocol YourViewController Delegate <NSObject>
@optional
-(void)defineDelegateMethodName: (YourViewController *) controller;
@required
-(BOOL)delegateMethodReturningBool: (YourViewController *) controller;
@end
@interface YourViewController : UIViewController
//Since the property for the protocol could be of any class, then it will be marked as a type of id.
@property (nonatomic, weak) id< YourViewController Delegate> delegate;
@end
协议行为中定义的方法可以用@optional和@required作为协议定义的一部分进行控制。
步骤3: 委托的实现
#import "delegate.h"
@interface YourDelegateUser ()
<YourViewControllerDelegate>
@end
@implementation YourDelegateUser
- (void) variousFoo {
YourViewController *controller = [[YourViewController alloc] init];
controller.delegate = self;
}
-(void)defineDelegateMethodName: (YourViewController *) controller {
// handle the delegate being called here
}
-(BOOL)delegateMethodReturningBool: (YourViewController *) controller {
// handle the delegate being called here
return YES;
}
@end
//在调用该方法之前测试该方法是否已经定义
- (void) someMethodToCallDelegate {
if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
[self.delegate delegateMethodName:self];
}
}
斯威夫特版本
委托只是一个为另一个类做一些工作的类。阅读下面的代码,这是一个有点傻(但希望有启发性)的Playground示例,它展示了如何在Swift中实现这一点。
// A protocol is just a list of methods (and/or properties) that must
// be used by any class that adopts the protocol.
protocol OlderSiblingDelegate: class {
// This protocol only defines one required method
func getYourNiceOlderSiblingAGlassOfWater() -> String
}
class BossyBigBrother {
// The delegate is the BossyBigBrother's slave. This position can
// be assigned later to whoever is available (and conforms to the
// protocol).
weak var delegate: OlderSiblingDelegate?
func tellSomebodyToGetMeSomeWater() -> String? {
// The delegate is optional because there might not be anyone
// nearby to boss around.
return delegate?.getYourNiceOlderSiblingAGlassOfWater()
}
}
// PoorLittleSister conforms to the OlderSiblingDelegate protocol
class PoorLittleSister: OlderSiblingDelegate {
// This method is repquired by the protocol, but the protocol said
// nothing about how it needs to be implemented.
func getYourNiceOlderSiblingAGlassOfWater() -> String {
return "Go get it yourself!"
}
}
// initialize the classes
let bigBro = BossyBigBrother()
let lilSis = PoorLittleSister()
// Set the delegate
// bigBro could boss around anyone who conforms to the
// OlderSiblingDelegate protocol, but since lilSis is here,
// she is the unlucky choice.
bigBro.delegate = lilSis
// Because the delegate is set, there is a class to do bigBro's work for him.
// bigBro tells lilSis to get him some water.
if let replyFromLilSis = bigBro.tellSomebodyToGetMeSomeWater() {
print(replyFromLilSis) // "Go get it yourself!"
}
在实际实践中,委托通常用于以下情况
当一个类需要向另一个类传递一些信息时 当一个类希望允许另一个类自定义它时
类之间不需要事先知道彼此的任何信息,除非委托类符合所需的协议。
我强烈推荐阅读以下两篇文章。它们甚至比文档更能帮助我理解委托。
什么是委托?-快速开发人员指南 委托如何工作-一个快速的开发者指南
答案实际上已经回答了,但我想给你一个创建委托的“小抄”:
DELEGATE SCRIPT
CLASS A - Where delegate is calling function
@protocol <#Protocol Name#> <NSObject>
-(void)delegateMethod;
@end
@interface <#Some ViewController#> : <#UIViewController#>
@property (nonatomic, assign) id <<#Protocol Name#>> delegate;
@end
@implementation <#Some ViewController#>
-(void)someMethod {
[self.delegate methodName];
}
@end
CLASS B - Where delegate is called
@interface <#Other ViewController#> (<#Delegate Name#>) {}
@end
@implementation <#Other ViewController#>
-(void)otherMethod {
CLASSA *classA = [[CLASSA alloc] init];
[classA setDelegate:self];
}
-delegateMethod() {
}
@end
也许这更符合你所错过的:
如果你从c++的角度来看,委托需要一点时间来适应——但基本上“它们就是工作”。
它的工作方式是,你设置一些对象,你写的委托到NSWindow,但你的对象只有实现(方法)为一个或几个可能的委托方法。发生了一些事情,NSWindow想要调用你的对象它使用Objective-c的respondsToSelector方法来确定你的对象是否想要调用那个方法,然后调用它。这就是objective-c的工作方式——根据需要查找方法。
对你自己的对象做这个完全是微不足道的,没有什么特别的,例如,你可以有一个包含27个对象的NSArray,所有不同类型的对象,只有18个其中一些有方法-(void)setToBue;其他9个没有。在所有18个需要执行的地方调用setToBlue,就像这样:
for (id anObject in myArray)
{
if ([anObject respondsToSelector:@selector(@"setToBlue")])
[anObject setToBlue];
}
关于委托的另一件事是它们不会被保留,所以你总是需要在MyClass dealloc方法中将委托设为nil。