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

但是我如何创建它们呢?


当前回答

免责声明:这是如何创建委托的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

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

也许这更符合你所错过的:

如果你从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。

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

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

当使用形式化协议方法来创建委托支持时,我发现你可以通过添加以下内容来确保正确的类型检查(尽管是运行时,而不是编译时):

if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
    [NSException raise:@"MyDelegate Exception"
                format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}

在你的委托访问器(setDelegate)代码中。这有助于减少错误。