单例 - 从数据存储中读取

时间:2011-11-15 17:23:08

标签: objective-c ios

我正在尝试通过在我的Xcode项目中不使用全局变量来做正确的事情。我成功地创造了一个单身人士。我有一个简单的数据存储,我将其命名为“myContactsStore”,其中包含一个数组,数组中的每个对象只有3个实例变量(name,phoneNum和eMail)。当我执行创建数组的视图控制器时,我在创建,修改,保存数组中的数据方面没有问题。

我的问题是尝试从另一个视图控制器访问数据存储。我已经到了一半了,正如我能够通过在for循环中使用以下代码从另一个视图控制器打印整个测试数组的内容所证明的那样:

NSLog(@"%@", myContactsStore.description);

这是输出:

"Mary, 0938420839, PaulDoe@Mac.com",
"John, 9932097372, PaulDoe@Mac.com",
"Mary, 0726756893, RedCat@iwon.com",
"Mary, 8556327199, xxxbct@mac.com",
"John, 0640848317, xxxbct@mac.com"

如何只访问一个实例变量?例如,我想在另一个视图控制器中创建一个只读数组,该控制器只包含“myContactsStore”数组中每个联系人的电子邮件地址。我尝试了几件事,但我是新手,我必须遗漏一些非常基本的东西。

感谢您的帮助以及您可能有时间包含的任何代码示例。

5 个答案:

答案 0 :(得分:3)

虽然Singletons是一种跨类共享数据的简单方法,但它们会导致整体设计中的内聚问题导致您的课程开始“导入世界”。这个问题恰好是给定类的正确依赖关系,当你尝试从类设计而不是从用例设计时,这很容易被忽略。你想问自己,“这个班级使用什么数据?”然后创建一个协议(抽象),为类提供所需数据的视图。在您的情况下,一个类写入数据存储。它不需要创建数据存储,也不需要知道数据是否在数组中维护。完全按顺序执行这些步骤并直接跟随,否则您会错过我的观点。在单独的.h文件中尝试协议,如下所示:

@protocol MyDataStore <NSObject> {
}

在第一个类中导入此标头,用于创建联系人并声明协议类型的属性。

#import "MyDataStore.h"

@interface MyContactCreator : UIViewController
@property (nonatomic, retain) id<MyDataStore> dataStore;

-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil dataStore:(id<MyDataStore>)aDataStore;

@end

我投入了一个自定义的init方法,以防您当前以编程方式而不是通过InterfaceBuilder实例化视图控制器。在您的实现中,您将执行以下操作:

@implementation MyContactCreator

//other methods...

-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil dataStore:(id<MyDataStore>)aDataStore
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.dataStore = aDataStore;
    }
    return self;
}

//other methods...
@end

如果您使用Interface Builder创建视图控制器,则可以将自定义对象拖动到播放状态,并将其称为“MyDataStoreImpl”。这里的想法是,您将数据存储区提供给视图控制器,而不是直接创建它并了解它。此外,您还要推迟担心 数据存储如何工作,直到您真正需要为止。稍后在您创建联系人的视图控制器中,您将使用数据存储来创建它们。假设信息来自标准屏幕元素,您可以编写如下代码:

-(void) addContactTapped:(id)sender
{
   [self.datastore createContactWithName:txtNameField.text phoneNumber:txtPhoneField.text email:txtEmailField.text];
}

你的编辑会尖叫你(因为你的二年级老师会在你的拼写作业中使用红色标记),因为数据存储区不响应你发送的信息。您返回并将该方法添加到协议中:

@protocol MyDataStore <NSObject> {
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
}

在您想要电子邮件地址列表的其他视图控制器类中,您将导入相同的数据存储协议。您还可以声明一个数据源属性,就像我们上面使用免费的自定义init方法或Interface Builder传递数据源一样。这个视图控制器(假设它是一个表视图控制器)可能会有一些方法,如:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.datasource numberOfContacts];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *email = [self.datasource emailForContactNumber:indexPath.row];
    UITableViewCell *cell = //create tableview cell with the email string
    return cell;
}

你的编辑将开始用小红线和所有人尖叫。您可以在此处添加更多方法。

@protocol MyDataStore <NSObject> {
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
-(NSInteger) numberOfContacts;
-(NSString*) emailForContactNumber:(NSInteger)index;
}

小红线消失,最后你可以开始考虑如何存储和检索联系人。创建一个名为MyDataStoreImpl的独立类,它扩展NSObject并导入并遵循“MyDataStore”协议。填写所有方法的实现,您应该启动并运行。它可以像在内部NSMutableArray属性中存储包含联系人信息的NSDisctionary对象一样简单。

- (id)init {
    self = [super init];
    if (self) {
        self.allContacts = [NSMutableArray array];
    }
    return self;
}
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
{
    NSDictionary *newContact = [NSDictionary dictionaryWithObjectsAndKeys:
                                aName, @"name", aPhoneNumber, @"phone", anEmail, @"email",
                                nil];
    [self.allConstacts addObject:newContact];
}
-(NSInteger) numberOfContacts;
{
    return [self.allContacts count];
}

-(NSString*) emailForContactNumber:(NSInteger)index;
{
    [[self.allContacts objectAtIndex:index] valueForKey:@"email"];
}

这里的优势很多。您可以稍后重新实现数据源以从plist文件,网络服务器或数据库读取/写入,而无需触及任何控制器。此外,您的应用程序将更容易针对性能进行优化,因为您可以将读取写入方法设计为直接从源中提取而不是将数据从一个数组简单地复制到另一个数组,就像您担心如何过早管理它一样。所有上述内容在没有经过测试的情况下抛在一起,并且可能存在错误,但是为了说明如何在没有单例的情况下在控制器之间正确共享数据,同时保持可测试且易于维护的代码库。

答案 1 :(得分:1)

我总是使用Mike Gallagher的SynthesizeSingleton.h。他有一篇关于这个主题的内容非常丰富的文章here。你应该检查一下。它使Singleton类的创建变得非常简单。

答案 2 :(得分:1)

您可以将其粘贴到任何类,使其成为单身人士:

+(MySingleton *)singleton {
    static dispatch_once_t pred;
    static MySingleton *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[MySingleton alloc] init];
        shared.someVar = someValue;
    });
    return shared;
}

-(void) dealloc {
    abort();
    [someVar release];
    [super dealloc];
}

更好的是,您可以将该类添加为应用程序委托的ivar,这是您已经拥有的单例。您可以像这样得到它的引用:

// AppDelegate or whatever name
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];

然后您在该委托上作为ivar访问数据存储区,并可选择实现iOS上可用的几种持久性技术之一,基本上通过直接文件访问或NSCoding甚至NSUserDefaults来删除文件(您不应该这样做,但它很方便小任务),核心数据或SQLite。

如果您仅使用此数据存储类在控制器之间传递数据,则可以直接执行此操作。例如:

CitySelectionVC *citySel = [[CitySelectionVC alloc] initWithCities:self.cities];
[self.navigationController pushViewController:citySel animated:TRUE];
[citySel release];

答案 3 :(得分:0)

假设您的myContactsStore对象有一个返回内部NSArray对象的方法contactsArray,您可以这样做:

NSArray *emails = [myContactsStore.contentsArray valueForKey:@"email"];
NSLog(@"output: %@", emails);

应该输出:

output: (
  "PaulDoe@Mac.com",
  "PaulDoe@Mac.com",
  "RedCat@iwon.com",
  "xxxbct@mac.com",
  "xxxbct@mac.com"
)

答案 4 :(得分:0)

您只需枚举数组并检索所需的数据即可。像这样:

NSMutableArray *emails = [[NSMutableArray alloc] initWithCapacity:myContactsStore.count];
    for (ContactClassName *contact in myContactsStore) {
        NSString *email = contact.eMail; // I'm assuming your ivar eMail is also a property
        if (email) [emails addObject:email];
    }

您现在拥有一个仅包含电子邮件列表的NSMutableArray。如果你想让这个列表成为不可变的,只需:

NSArray *emailList = [emails copy];
相关问题