0

我有一个创建 ObjectA 的视图控制器。然后 ObjectA 创建 ObjectB。ObjectB 需要从视图控制器获取一些值(我需要在创建对象后可以更改的值)。

我试图找出检索值的最佳方法。我的第一个想法是协议。到目前为止,我只在一个对象中创建了协议:

#import <UIKit/UIKit.h>

@protocol AnalysisTypeTableDelegate;

@interface AnalysisTypeTableViewController : UITableViewController

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

@property (strong, nonatomic) NSArray *dataSource;

@property (nonatomic) BOOL allowRefresh;

@end

@protocol AnalysisTypeTableDelegate <NSObject>

-(void)returnAnalysisType:(NSString *)type;

@end

如何创建协议类(例如创建新文件、objective-c 协议)?

那么如何将它们链接在一起呢?ObjectA 有一个协议,我会做类似的事情吗:

// View controller and objectB both conform to myProtocol

// view controller creates objectA
myObjectA.delegate = self

// when objectA creates objectB
myObjectB.delegate = self.delegate

还是有更好的方法来检索我需要的值?

编辑:

我想我需要做这样的事情:

objectA 的协议:

@protocol objectADelegate

-(NSDictionary)requestViewController;

@end

objectB 的协议:

@protocol objectBDelegate

-(NSDictionary *)requestObjectA;

-(void)updateList:(NSSarray *)list;

@end

在我的对象B

-(NSDictionary *)requestObjectA {
    NSDictionary *extents = [self.delegate requestObjectA];
}

-(void)serverCall {
    // make server call, get list
    ...
    // update myObjectA with new list
    [self.delegate updateList:newList];

在我的对象A

-(NSDictionary *)requestObjectA {
    return [self.delegate requestViewController];
}

-(void)updateList:(NSArray)list {
    // updates list
}

在视图控制器中

-(NSDictionary *)requestViewController;
    return self.mapkit.exents;
}
4

1 回答 1

1

我假设您是说这是从您的视图控制器调用的:

myObjectA.delegate = self

并且您正在从对象 A 调用以下行:

myObjectB.delegate = self.delegate

从技术上讲,你可以做到这一点,尽管我觉得这个结构有点令人困惑。而且它甚至有点问题,因为您没有为视图控制器提供任何机制来说“好的,我不想再成为 adelegate了”,因为它大概只知道myObjectA. 您没有任何机制让视图控制器告诉myObjectB它不想再成为它delegate,因为大概视图控制器甚至不知道B' 的存在。


使用模型-视图-控制器模式

的概念delegate是“我想告诉我的delegate东西,或者提出一些要求”。另一方面,如果您的目标是在它们之间共享数据,我倾向于从委托协议模式转向模型视图控制器(MVC) 模式,我们将专注于“模型”部分:

  • 创建一个Model类来保存模型数据;

  • 让视图控制器实例化一个Model类对象;

  • 将指向Model对象的指针从一个视图控制器或对象传递给下一个视图控制器(或以其他方式使此模型对象可被其他类访问,例如,通过单例模式、应用程序委托属性等);

  • 确保您的Model类具有与正在维护的数据关联的属性(这样,其他类可以使用Model将为您合成的访问器方法)。

这在概念上与您用delegate指针概述的内容非常相似,但不包含这样delegate做的含义,即您不仅在访问数据,而且您可以告诉控制器做某事。在委托协议模式中,委托通常应该能够delegate在任何时候选择退出。


处理模型数据变化的不同方法

有几种方法可以将更改反映到其他对象中的模型对象(例如,告诉视图控制器模型数据已更改并且它必须反映该更改)。这些包括:

  1. 使用代表。这可能是我在这里列出的最糟糕的方法,因为如果你想告诉多个对象模型数据的变化,你必须有多个委托(或它们的数组)。这是对代表的不良使用。

  2. 重新加载viewDidAppear。如果您只处理确保视图控制器更新其视图以反映在另一个视图控制器上更改的数据,您可以在视图再次出现时刷新视图。这是最原始的解决方案,并且仅在您处理重新出现的视图控制器时才有效,例如,更改数据的模态视图已被解除。在那些简单的情况下,这通常是最简单的解决方案。

    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
    
        [self updateField1Label];
        [self updateField2Label];
        [self.tableView reloadData];
    }
    
  3. 通过注册通知NSNotificationCenter。这样做的好处是多个对象可以将自己作为观察者添加到同一个通知中。例如,在 .m 文件中为您的通知定义一个键:

    NSString * const kNotificationField1Changed = @"com.log139.field1";
    

    然后,在 .h 文件中定义对该键的外部引用:

    extern NSString * const kNotificationField1Changed;
    

    然后,想要收到更改通知的对象可以注册该通知:

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleField1Notification:)
                                                 name:kNotificationField1Changed
                                               object:nil];
    

    显然,您必须编写那个“处理程序”方法:

    - (void)handleField1Notification:(NSNotification*)notification
    {
        self.field1Label.text = self.model.name;
    }
    

    最后,该模型字段可以发布通知的任何更新,例如:

    - (void)saveAndPop:(UITextField *)textField
    {
        self.model.field1 = textField.text;
    
        [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationField1Changed
                                                            object:self
                                                          userInfo:nil];
    
        [self.navigationController popViewControllerAnimated:YES];
    }
    

    如果您这样做,您只需要确保当注册通知的对象超出范围时,它还必须取消注册该通知:

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:kNotificationField1Changed
                                                  object:nil];
    
  4. 使用键值观察。同样,多个对象可以注册为模型属性的观察者。假设您已经为您的对象定义了三个属性(通过@property) 。Model任何想要收到更改通知的对象都可以将自己添加为这些属性的观察者,例如:

    [self.model addObserver:self forKeyPath:@"field1" options:0 context:NULL];
    [self.model addObserver:self forKeyPath:@"field2" options:0 context:NULL];
    [self.model addObserver:self forKeyPath:@"field3" options:0 context:NULL];
    

    然后你只需要编写一个observeValueForKeyPath方法:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        if ([keyPath isEqualToString:@"field1"])
            [self updateField1Label];
    
        else if ([keyPath isEqualToString:@"field2"])
            [self updateField2Label];
    
        else if ([keyPath isEqualToString:@"field3"])
            [self.field3TableView reloadData];
    }
    

    这里唯一棘手的是,如果其中一个对象是一个集合(例如 a NSMutableArray),您必须编写集合访问器。有关更多示例,请参阅该文档。例如,虽然,我有一个模型,它的属性是一个宠物名称数组,NSMutableArray *petNames. 因此,我必须为我的模型编写以下两种方法:

    - (void)insertObject:(NSString *)petName inPetNamesAtIndex:(NSUInteger)index
    {
        [self.petNames insertObject:petName atIndex:index];
    }
    
    - (void)removeObjectFromPetNamesAtIndex:(NSUInteger)index
    {
        [self.petNames removeObjectAtIndex:index];
    }
    

    我没有打电话[self.model.petNames addObject:aPetName],而是打电话[self.model insertObject:aPetName inPetNamesAtIndex:0];(通过这样做,通知会自动发生)。

简而言之,如果您有多个对象需要在模型数据发生更改时收到通知,我认为键值观察是最优雅的方法,但如果处理它需要一点时间来解决它收藏品。我认为仅仅发布通知是一个相对简单的选择。


参考:

于 2013-02-14T15:46:29.613 回答