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

但是我如何创建它们呢?


当前回答

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

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

其他回答

认可的答案很好,但如果你想要一个1分钟的答案,试试这个:

MyClass.h文件应该如下所示(添加带有注释的委托行!)

#import <BlaClass/BlaClass.h>

@class MyClass;             //define class, so protocol can see MyClass
@protocol MyClassDelegate <NSObject>   //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 <MyClassDelegate> 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

要在另一个类中使用你的委托(UIViewController在这种情况下称为MyVC) MyVC.h:

#import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //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!");
}

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

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

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];
}

作为苹果公司推荐的一个好的实践,委托(根据定义,它是一个协议)符合NSObject协议是有好处的。

@protocol MyDelegate <NSObject>
    ...
@end

要在你的委托中创建可选方法(即不一定要实现的方法),你可以像这样使用@optional注释:

@protocol MyDelegate <NSObject>
    ...
    ...
      // Declaration for Methods that 'must' be implemented'
    ...
    ...
    @optional
    ...
      // Declaration for Methods that 'need not necessarily' be implemented by the class conforming to your delegate
    ...
@end

因此,当使用你指定为可选的方法时,你需要(在你的类中)检查respondsToSelector是否视图(符合你的委托)已经实际实现了你的可选方法。

在我看来,为委托方法创建单独的类,你可以在任何地方使用。

在我的自定义下拉列表类。h

typedef enum
{
 DDSTATE,
 DDCITY
}DropDownType;

@protocol DropDownListDelegate <NSObject>
@required
- (void)dropDownDidSelectItemWithString:(NSString*)itemString     DropDownType:(DropDownType)dropDownType;
@end
@interface DropDownViewController : UIViewController
{
 BOOL isFiltered;
}
@property (nonatomic, assign) DropDownType dropDownType;
@property (weak) id <DropDownListDelegate> delegate;
@property (strong, nonatomic) NSMutableArray *array1DropDown;
@property (strong, nonatomic) NSMutableArray *array2DropDown;

然后进去。M文件创建对象数组

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat rowHeight = 44.0f;
return rowHeight;
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return isFiltered?[self.array1DropDown count]:[self.array2DropDown count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = @"TableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}

if (self.delegate) {
    if (self.dropDownType == DDCITY) {
        cell.textLabel.text = [self.array1DropDown objectAtIndex:indexPath.row];
    }
    else if (self.dropDownType == DDSTATE) {
        cell.textLabel.text = [self.array2DropDown objectAtIndex:indexPath.row];
    }
}
return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
 [self dismissViewControllerAnimated:YES completion:^{
    if(self.delegate){
        if(self.dropDownType == DDCITY){
            [self.delegate dropDownDidSelectItemWithString:[self.array1DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
        else if (self.dropDownType == DDSTATE) {
            [self.delegate dropDownDidSelectItemWithString:[self.array2DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
    }
}];
}

这里所有的都设置为自定义委托类。之后你可以在任何你想要的地方使用这个委派方法。例如……

在之后的另一个视图控制器导入中

像这样创建调用委托方法的动作

- (IBAction)dropDownBtn1Action:(id)sender {
DropDownViewController *vehicleModelDropView = [[DropDownViewController alloc]init];
vehicleModelDropView.dropDownType = DDCITY;
vehicleModelDropView.delegate = self;
[self presentViewController:vehicleModelDropView animated:YES completion:nil];
}

之后像这样调用委托方法

- (void)dropDownDidSelectItemWithString:(NSString *)itemString DropDownType:(DropDownType)dropDownType {
switch (dropDownType) {
    case DDCITY:{
        if(itemString.length > 0){
            //Here i am printing the selected row
            [self.dropDownBtn1 setTitle:itemString forState:UIControlStateNormal];
        }
    }
        break;
    case DDSTATE: {
        //Here i am printing the selected row
        [self.dropDownBtn2 setTitle:itemString forState:UIControlStateNormal];
    }

    default:
        break;
}
}

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

@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];
}

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