有人能解释一下Objective C代理的工作原理吗?

时间:2011-05-26 20:33:52

标签: objective-c delegates

好吧,这可能看起来像是一个愚蠢的问题,但如果你忍受我并阅读整个问题,你会明白为什么我会问它。

我在网上看到过很多很好的例子和解释,但是我也无法得到它。对我来说理解它的原因和方法非常重要,所以不要使用文档中的一些例子(顺便说一句,我真的很难理解Apple Docs,我用的是php.net文档,有大量的例子)因为那时候我只是不明白。

我想要两个类,一个是gardenClass。它有一个带花的数组,每个花都是从flowerClass实例化的。所以,flowerClass不知道什么样的花园实例化它。它有时会尖叫着水。

所以,如果有人在这里为我解释,我会非常感激!

我试过了,根据我们从你们那里获得的信息,我就是这样做的。

MyGarden.h

#import "Flower.h"

@interface MyGarden : NSObject <WateringDelegate>
{
    Flower *pinkFlower;
}
@end

MyGarden.m

#import "MyGarden.h"
#import "Flower.h"

@implementation MyGarden

- (void) giveWaterToFlower
{
    NSLog(@"Watering Flower");
}

- (void)viewDidLoad
{
    pinkFlower = [[Flower alloc] init];
    [pinkFlower setDelegate:self];
    [pinkFlower startToGrow];
}
@end

Flower.h

@protocol WateringDelegate <NSObject>
@required
- (void) giveWaterToFlower;
@end

@interface Flower : NSObject
{
    id <WateringDelegate> delegate;
}

@property (retain) id delegate;
- (void) startToGrow;
@end

Flower.m

#import "Flower.h"

@implementation Flower

@synthesize delegate;

- (void) needWater
{
    [[self delegate] giveWaterToFlower];
}

- (void) startToGrow
{
    [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(needWater) userInfo:nil repeats:YES];
}
@end

我没有导入任何UIKit或基础,因为我现在试图在这里做与代表相关的东西...... 那么,这是正确的吗?

3 个答案:

答案 0 :(得分:4)

委托是您为应用程序实现自定义行为的地方,它会影响另一个类(例如Cocoa框架中的普通旧对象)的工作方式。

在不使用委托模式的其他语言中,您可以将对象子类化以更改其行为。 Objective-C也允许你这样做,但是委托的好处是每次你想要做一个小改动你都不必子类,一个对象可以是多个其他对象的委托。

所以考虑一个控制器类,它拥有一个带有一些控件的窗口,可能是一个信息表,一个工具栏等。你可以将控制器设置为这些控件的委托,这样当控件需要更多关于什么的信息时他们应该这样做,他们可以看你的控制器。您可能希望自定义窗口的大小调整方式,用户在表视图中选择行时的情况,信息的显示方式等等。这些是控件对其委托(您的控制器)所做的所有调用,使用其委托协议中定义的方法。如果您很幸运,您可以编写所有自定义代码,而无需将子类分类并拆分为多个文件。

另一个好处是您只需要实现您关心的委托方法。如果默认行为没问题,那么您无需做任何事情。

因此,在您的示例中,花类将具有某种带有“waterMe”方法的委托协议。代表(可能是花园类,或其他一些东西)可以实现这个,但是你选择的时候,它只会在花类需要的时候调用。值得注意的是,尽管该委托在框架中最有用,在框架中,您有一个以通用方式工作的对象,可以重写以完成不同的事情。在您自己的代码中,将自定义行为添加到您自己的对象通常更直接,除非您真的要以通用方式使用它。换句话说,不要试图花时间制作一个通常表现得很好的课程,而实际上你只会让它以一种特定的方式表现。

答案 1 :(得分:0)

在某些情况下,委托模式非常有用,如Marc所示。问题是Apple使用它们的次数远远超过它们的用途。特别是,Apple已经尝试(并且失败了,imo)使用代理实现了一个基本的事件机制。

这违背了委托模式的目的,即将类的行为模块化为组件。事件不代表行为:它们表示(通常是独立的)根据事件发生而发生的动作(即,动作取决于事件,但效果可能在很大程度上独立于对象提出了这个事件。)

这个问题的最大问题在于,事件只能有一个委托处理程序,而在其他语言中有许多有效的情况,我们可能希望拥有一个事件的几个独立使用者。例如,在C#中,+=运算符用于向对象的事件添加事件处理程序,允许无限数量的事件观察者。遗憾的是,Objective-C没有任何内置的事件机制,Apple的核心框架类也没有使用事件模式。

使用代表进行事件限制至少可以说。此外,消费者与生产者紧密联系,因为只有一个消费者。因此,对于另一个消费者来说,改变生产者的代表是不可能的(这是可能的,但这并不明智)。

Apple如何使用代理实现实际事件的示例是tableView:didSelectRowAtIndexPath方法。那是一个事件。为什么只允许一件事依赖于被选中的行? “解决方案”(尽管它更像是一种解决方法)将由父级为其子级公开自己的委托及其自己的伪事件委托方法。但这只意味着更多的工作和更多的错误机会。

Apple使用委托模型正确的示例(就模式而言)是tableView:cellForRowAtIndexPath方法。此方法允许tableView的行为被消费者卸载和定义,并且提供多个实现没有意义。

关于委托模式的一个好的事情是它同样支持组合以及继承。也就是说,您可以派生一个实现其自己的委托方法的控制器类,然后您可以实例化它,或者您可以使用通用控件简单地编写您的类,并自己为该控件提供委托方法。

但是,我也不是将当前(父)对象作为组件(子)对象的委托传递的粉丝,因为这会导致类的污染和概念不匹配(整个类现在提供行为仅仅是其中一个组件,而不是属于类本身的概念)。更好的方法是提供一个实现必要的委托方法的特殊映射对象。然而,由于这在Objective-C中不方便,遗憾的是,前者最常用于惯例。

答案 2 :(得分:0)

As it stands, when the flower wants water it calls self.delegate giveWaterToFlower. delegate is set to MyGarden by default, so the giveWaterToFlower method in MyGarden is called when the flower wants water. If you implemented a wateringCan object, then wateringCan could do [pinkFlower setDelegate:self]; Then, when the flower wants water, self.delegate giveWaterToFlower would call the giveWaterToFlower method in wateringCan, which might give more or less water than the default. Similiarly, you could implement a gardenHose object, that also provides a giveWaterToFlower method. The gardenhose might only reach a certain distance with the garden, so might only override the delegate in specific flowers.

But the design pattern is that flower has delegated it's giveWaterToFlower method to another object. The flower object has no idea who is providing it's giveWaterToFlower method. You could include a default giveWaterToFlower method within flower and initialise your flower objects with self.delegate = self;