代表人数iPhoneOS中的通知

时间:2010-02-09 21:33:52

标签: iphone delegates notifications

我试图从子视图控制器中调用我的根视图控制器中的方法,这样当我更改选项时,它们将自动更新根视图,而根视图将更新其他几个视图控制器。对于第二部分,我使用了通知,但为此我首先尝试使用委托,因为它(所以我一直相信)是一个很好的编程习惯。我无法使其工作,并且知道我可以轻松地设置另一个通知来完成这项工作。我应该继续尝试实施代理还是仅使用通知?

7 个答案:

答案 0 :(得分:58)

对许多情况来说,委派是一种很好的编程习惯,但这并不意味着如果你不熟悉它就必须使用它。委托和通知都有助于将视图控制器彼此分离,这是一件好事。通知可能更容易编码,并提供多个对象可以观察一个通知的优势。对于代理人来说,如果不修改委托对象就不可能做到这一点(并且很不寻常)。

委派的一些好处:

  • 委派对象和委托之间的联系更加清晰,尤其是在强制实施委托时。
  • 如果必须从委托者向委托传递多种类型的消息,则委派可以通过为每条消息指定一个委托方法来使此更清楚。 对于通知,您可以使用多个通知名称,但所有通知都以观察者一侧的相同方法结束(可能需要讨厌的switch语句)。

只有您可以决定哪种模式更适合您。在任何情况下,您都应该考虑不让视图控制器发送通知或委托消息。在许多情况下,视图控制器应该更改模型,然后模型应该通知其观察者或其代理它已被更改。

实现委托模式很简单:

  1. 在您的ChildViewController.h中,声明委托必须在以后实现的委托协议:

    @protocol ChildViewControllerDelegate <NSObject>
    @optional
    - (void)viewControllerDidChange:(ChildViewController *)controller;
    @end
    
  2. 在文件的顶部,创建一个实例变量来保存指向ChildViewController中委托的指针:

    @protocol ChildViewControllerDelegate;
    @interface ChildViewController : UIViewController {
        id <ChildViewControllerDelegate> delegate;
        ...
    }
    @property (assign) id <ChildViewControllerDelegate> delegate;
    ...
    @end
    
  3. 在RootViewController.h中,使您的类符合委托协议:

    @interface RootViewController : UIViewController <ChildViewControllerDelegate> {
    ...
    
  4. 在RootViewController实现中,实现委托方法。此外,在创建ChildViewController实例时,必须分配委托。

    @implement RootViewController
    ...
    // in some method:
    ChildViewController *controller = [[ChildViewController alloc] initWithNibName:...
    controller.delegate = self;
    ...
    - (void)viewControllerDidChange:(ChildViewController *)controller {
        NSLog(@"Delegate method was called.");
    }
    ...
    
  5. 在ChildViewController实现中,在适当的时间调用委托方法:

    @implementation ChildViewController
    ...
    // in some method:
    if ([self.delegate respondsToSelector:@selector(viewControllerDidChange:)]) {
        [self.delegate viewControllerDidChange:self];
    }
    ...
    
  6. 就是这样。 (注意:我已经从内存中写了这个,所以可能有一些拼写错误/错误。)

答案 1 :(得分:25)

我想补充一下:

  

接收通知的对象可以   事件发生后才做出反应   发生了。这是一个重要的   与代表团的区别。该   代表有机会拒绝   或修改提议的操作   委托对象。观察   另一方面,物体不能   直接影响即将来临   操作

答案 2 :(得分:11)

通常,如果您需要根据模型中数据的更改来更新UI,您可以让视图控制器观察相关的模型数据,并在收到更改通知时更新其视图。

我认为代表团更正式,更像Peter Hosey最近分享的区别:

  

区别在于授权   for to-one(和双向)   沟通,而通知   是为了多对,单向   通信。

此外,我发现(完全)更新viewWillAppear:中的视图工作正常(但这不是性能受到关注的最佳解决方案)。

答案 3 :(得分:6)

通知可以使程序的运行时行为变得更加复杂。可以把它想象成具有多个目的地的goto。未定义这些目的地的顺序。如果您遇到崩溃,那么堆栈跟踪信息很少。

在某些情况下,使用通知是有意义的 - 通常是将模型更改或全局状态更改传达给您的视图。例如,网络已关闭,应用程序将辞职等等!

在iOS中学习委托模式是值得的。代理在调试时为您提供完整的堆栈跟踪。它们可以显着简化运行时行为,同时仍然实现解耦对象的目标。

答案 4 :(得分:2)

代表们有点难以习惯,但我认为这是最好的做法,就像Apple一样,他们只是工作

我总是使用正式的协议声明。这在我的脑海里更合乎逻辑,而且在代码中非常清楚。 我建议使用UIView来更改您的选项而不是控制器。我总是使用一个主控制器并且有一个控制器可以控制的许多子类UIView。(但是,您可以修改控制器的以下代码,如果您确实需要控制器而不是正常视图。)在子视图的头文件中,使其如下所示:

// ChildView.h
#import <UIKit/UIKit.h>

@protocol ChildViewDelegate; // tells the compiler that there will be a protocol definition later

@interface ChildViewController : UIView {
    id <ChildViewDelegate> delegate;
    // more stuff
}

// properties and class/instance methods

@end

@protocol ChildViewDelegate // this is the formal definition

- (void)childView:(ChildView *)c willDismissWithButtonIndex:(NSInteger)i; // change the part after (ChildView *)c to reflect the chosen options

@end

可以在ChildView的实现中的某处调用@protocol和第二个@end之间的方法,然后您的根视图控制器可以是接收“通知”的委托。

.m文件应该是这样的:

// ChildView.m
#import "ChildView.h"

@implementation ChildView

- (id)initWithDelegate:(id<ChildViewDelegate>)del { // make this whatever you want
    if (self = [super initWithFrame:CGRect(0, 0, 50, 50)]) { // if frame is a parameter for the init method, you can make that here, your choice
        delegate = del; // this defines what class listens to the 'notification'
    }
    return self;
}

// other methods

// example: a method that will remove the subview

- (void)dismiss {
    // tell the delegate (listener) that you're about to dismiss this view
    [delegate childView:self willDismissWithButtonIndex:3];
    [self removeFromSuperView];
}

@end

然后根视图控制器的.h文件将包含以下代码:

// RootViewController.h
#import "ChildView.h"

@interface RootViewController : UIViewController <ChildViewDelegate> {
    // stuff
}

// stuff

@end

实现文件将实现ChildView.h中协议中定义的方法,因为它将在ChildView调用它时运行。在那种方法中,把你收到通知时发生的东西放好。

答案 5 :(得分:0)

在这种情况下,您不需要使用委派或通知,因为您实际上不需要在视图之间直接通信。正如gerry3所说,您需要更改数据模型本身,然后让所有其他视图响应该更改。

您的数据模型应该是所有视图控制器都可以访问的独立对象。 (懒惰的方式是将其作为app委托的属性驻留。)当用户在View A中进行更改时,View A的控制器会将该更改写入数据模型。然后,只要打开视图B到Z,他们的控制器就会读取数据模型并适当地配置视图。

这样,视图和控制器都不需要彼此了解,并且所有更改都发生在一个中心对象中,因此可以轻松跟踪它们。

答案 6 :(得分:0)

您的根视图控制器是否真的需要了解更改,或仅了解子视图?

如果根控制器不必知道,让设置发出通知,其他视图正在寻找对我来说似乎是一个更好的答案,因为它简化了代码。没有必要引入比你更多的复杂性。