我知道委托是如何工作的,也知道如何使用它们。

但是我如何创建它们呢?


当前回答

假设你有一个你开发的类,想要声明一个委托属性,以便在发生一些事件时能够通知它:

@class myClass;

@protocol myClassDelegate <NSObject>

-(void)myClass:(MyClass*)myObject requiredEventHandlerWithParameter:(ParamType*)param;

@optional
-(void)myClass:(MyClass*)myObject optionalEventHandlerWithParameter:(ParamType*)param;

@end


@interface MyClass : NSObject

@property(nonatomic,weak)id< MyClassDelegate> delegate;

@end

so you declare a protocol in MyClass header file (or a separate header file) , and declare the required/optional event handlers that your delegate must/should implement , then declare a property in MyClass of type (id< MyClassDelegate>) which means any objective c class that conforms to the protocol MyClassDelegate , you'll notice that the delegate property is declared as weak , this is very important to prevent retain cycle (most often the delegate retains the MyClass instance so if you declared the delegate as retain, both of them will retain each other and neither of them will ever be released).

你还会注意到协议方法将MyClass实例作为参数传递给委托,这是最好的实践,以防委托想要调用MyClass实例上的一些方法,也有助于当委托将自己声明为MyClassDelegate到多个MyClass实例时,就像当你的ViewController中有多个UITableView的实例并将自己声明为UITableViewDelegate到所有这些实例时。

在你的MyClass中,你用声明的事件通知委托,如下所示:

if([_delegate respondsToSelector:@selector(myClass: requiredEventHandlerWithParameter:)])
{
     [_delegate myClass:self requiredEventHandlerWithParameter:(ParamType*)param];
}

你首先检查你的委托是否响应你即将调用的协议方法,以防委托没有实现它,应用程序将崩溃(即使协议方法是必需的)。

其他回答

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(这使得自动补全不那么有用),并且很难让编译器警告你打字错误和类似的错误。

免责声明:这是如何创建委托的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开发者,委托是一个非常重要的设计模式,要记住它们在对象之间是一对一的关系。

你可以在这里看到原始教程

让我们从一个例子开始,如果我们在网上购买一件产品,它会经过不同团队处理的运输/交付等流程。因此,如果运输完成,运输团队应该通知交付团队,并且应该是一对一的沟通,因为广播这个信息对其他人来说是一种开销/供应商可能只希望将这个信息传递给需要的人。

所以如果我们从应用的角度考虑,一个事件可以是一个在线订单,不同的团队可以是多个视图。

下面是代码,将ShippingView作为发货团队,将DeliveryView作为交付团队:

//Declare the protocol with functions having info which needs to be communicated
protocol ShippingDelegate : class {
    func productShipped(productID : String)
}
//shippingView which shows shipping status of products
class ShippingView : UIView
{

    weak var delegate:ShippingDelegate?
    var productID : String

    @IBAction func checkShippingStatus(sender: UIButton)
    {
        // if product is shipped
        delegate?.productShipped(productID: productID)
    }
}
//Delivery view which shows delivery status & tracking info
class DeliveryView: UIView,ShippingDelegate
{
    func productShipped(productID : String)
    {
        // update status on view & perform delivery
    }
}

//Main page on app which has both views & shows updated info on product whole status
class ProductViewController : UIViewController
{
    var shippingView : ShippingView
    var deliveryView : DeliveryView

    override func viewDidLoad() {
        super.viewDidLoad()
        // as we want to update shipping info on delivery view, so assign delegate to delivery object
        // whenever shipping status gets updated it will call productShipped method in DeliveryView & update UI.
        shippingView.delegate = deliveryView
        //
    }
}

斯威夫特版本

委托只是一个为另一个类做一些工作的类。阅读下面的代码,这是一个有点傻(但希望有启发性)的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!"
}

在实际实践中,委托通常用于以下情况

当一个类需要向另一个类传递一些信息时 当一个类希望允许另一个类自定义它时

类之间不需要事先知道彼此的任何信息,除非委托类符合所需的协议。

我强烈推荐阅读以下两篇文章。它们甚至比文档更能帮助我理解委托。

什么是委托?-快速开发人员指南 委托如何工作-一个快速的开发者指南

这并不是问题的答案,但如果你在查找如何创建自己的委托也许一些更简单的东西会是更好的答案。

我很少实现委托,因为我很少需要。对于一个委托对象,我只能有一个委托。所以如果你想让你的委托进行单向通信/传递数据,你最好使用通知。

NSNotification可以将对象传递给多个接收者,它非常容易使用。 它是这样工作的:

MyClass。M文件应该是这样的

#import "MyClass.h"
@implementation MyClass 

- (void) myMethodToDoStuff {
//this will post a notification with myClassData (NSArray in this case)  in its userInfo dict and self as an object
[[NSNotificationCenter defaultCenter] postNotificationName:@"myClassUpdatedData"
                                                    object:self
                                                  userInfo:[NSDictionary dictionaryWithObject:selectedLocation[@"myClassData"] forKey:@"myClassData"]];
}
@end

在其他类中使用通知: 添加类作为观察者:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];

实现选择器:

- (void) otherClassUpdatedItsData:(NSNotification *)note {
    NSLog(@"*** Other class updated its data ***");
    MyClass *otherClass = [note object];  //the object itself, you can call back any selector if you want
    NSArray *otherClassData = [note userInfo][@"myClassData"]; //get myClass data object and do whatever you want with it
}

如果,不要忘记删除你的类作为观察者

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}