无法在NSMutableSet中添加或删除对象

时间:2010-05-13 12:29:12

标签: iphone objective-c

检查:

- (IBAction)toggleFavorite {
    DataManager *data = [DataManager sharedDataManager];
    NSMutableSet *favorites = data.favorites;

    if (thisEvent.isFavorite == YES) {
        NSLog(@"Toggling off");
        thisEvent.isFavorite = NO;
        [favorites removeObject:thisEvent.guid];
        [favoriteIcon setImage:[UIImage imageNamed:@"notFavorite.png"] forState:UIControlStateNormal];
    }
    else {
        NSLog(@"Toggling on, adding %@", thisEvent.guid);
        thisEvent.isFavorite = YES;
        [favorites addObject:thisEvent.guid];
        [favoriteIcon setImage:[UIImage imageNamed:@"isFavorite.png"] forState:UIControlStateNormal];
    }
    NSLog(@"favorites array now contains %d members", [favorites count]);
}

这是从自定义UIButton触发的。 UI部分工作得很好 - 切换用于按钮的图像,我可以从其他东西看到thisEvent.isFavorite BOOL正在愉快地切换。我还可以在调试器中看到我正在获取我的DataManager单例。

但这是我的NSLog:

2010-05-13 08:24:32.946 MyApp[924:207] Toggling on, adding 05db685f65e2
2010-05-13 08:24:32.947 MyApp[924:207] favorites array now contains 0 members
2010-05-13 08:24:33.666 MyApp[924:207] Toggling off
2010-05-13 08:24:33.666 MyApp[924:207] favorites array now contains 0 members
2010-05-13 08:24:34.060 MyApp[924:207] Toggling on, adding 05db685f65e2
2010-05-13 08:24:34.061 MyApp[924:207] favorites array now contains 0 members
2010-05-13 08:24:34.296 MyApp[924:207] Toggling off
2010-05-13 08:24:34.297 MyApp[924:207] favorites array now contains 0 members

最糟糕的是,这个USED工作,我不知道我做了什么打破它。

- 编辑:根据请求,我的共享数据单例代码:

·H

#import <Foundation/Foundation.h>

@interface DataManager : NSObject {
    NSMutableArray *eventList;
    NSMutableSet *favorites;
}

@property (nonatomic, retain) NSMutableArray *eventList;
@property (nonatomic, retain) NSMutableSet *favorites;

+(DataManager*)sharedDataManager;

@end

的.m:

#import "DataManager.h"

static DataManager *singletonDataManager = nil;

@implementation DataManager

@synthesize eventList;
@synthesize favorites;

+(DataManager*)sharedDataManager {
    @synchronized(self) {
        if (!singletonDataManager) {
            singletonDataManager = [[DataManager alloc] init];
        }
    }
    return singletonDataManager;
}

- (DataManager*)init {
    if (self = [super init]) {
        eventList = [[NSMutableArray alloc] init];
        favorites = [[NSMutableSet alloc] init];
    }
    return self;
}

------编辑编辑编辑------

在@ TechZen的建议中,我将访问者方法移到了数据管理器单例中。现在看来是这样的:

#import "DataManager.h"

static DataManager *singletonDataManager = nil;

@implementation DataManager

@synthesize eventList;
@synthesize favorites;

+(DataManager*)sharedDataManager {
    @synchronized(self) {
        if (!singletonDataManager) {
            singletonDataManager = [[DataManager alloc] init];
        }
    }
    return singletonDataManager;
}

- (DataManager*)init {
    if (self = [super init]) {
        eventList = [[NSMutableArray alloc] init];
        favorites = [[NSMutableSet alloc] init];
    }
    return self;
}


#pragma mark -
#pragma mark Data management functions

- (void)addToFavorites:(NSString *)guid
{
    [self.favorites addObject:guid];
    NSLog(@"Item added--we now have %d faves.", [favorites count]);
}

- (void)removeFromFavorites:(NSString *)guid
{
    [favorites removeObject:guid];
    NSLog(!"Item removed--we now have %d faves.", [self.favorites count]);
}


@end

我创建了我的viewcontroller,调用[[DataManager sharedManager] addToFavorites:Event.guid]而不是将项目添加到收藏夹设置本身,但我留下了那里的日志记录。

这是我的日志:

2010-05-13 13:25:52.396 EverWondr[8895:207] Toggling on, adding 05db685f65e2
2010-05-13 13:25:52.397 EverWondr[8895:207] Item added--we now have 0 faves.
2010-05-13 13:25:52.398 EverWondr[8895:207] favorites array now contains 0 members
2010-05-13 13:25:53.578 EverWondr[8895:207] Toggling off
2010-05-13 13:25:53.579 EverWondr[8895:207] favorites array now contains 0 members

所以.... DataManager对象甚至无法向自己的属性添加任何内容!并且它不会像它是一个非可变类型那样抛出异常,它只是默默地失败!

为了好玩,我经历了并将其更改为NSMutableArray,我对此更为熟悉。同样的行为。

1 个答案:

答案 0 :(得分:4)

正如上面提到的phellicks,你可能不会从data.favorites返回一个可变集。虽然如此,你应该得到一个编译器警告。

05db685f65e2不是真正的指导。它看起来更像是一个对象的地址。你应该检查'thisEvent.guid`上的类型,以确保你有一个对象和正确的对象类型。

与您的主要问题无关,我会补充说明(1):

NSMutableSet *favorites = data.favorites;

......毫无意义,只是增加了另一种可能的错误来源。没有理由不直接在代码中使用data.favorites。 (见下文(3))

(2)当访问外部对象,甚至是单例时,最好将对外部对象的引用作为类的属性,特别是在像数据模型这样的关键对象的情况下。这使您可以控制和跟踪对外部对象的访问。

(3)不要将单身人士视为裸体全局变量。这将导致悲伤。而是以特定方法包装对数据模型内部数据的访问。例如,不是直接访问data.favorites,而是创建一个方法,如:

- (void) addToFavoritesGuid:(id) aGuid;

- (void) addToFavoritesGuid:(GuidClass *) aGuid;

这将使您的数据模型控制其内部,并使其能够拒绝添加不属于那里的对象。

修改

来自评论:

  

好的,我真的是   返回...我只是使用调试步骤   通过我的单身人士的初始化者。   检查我的DataManager的ivars   对象,我看到了我的最爱,哪个   在init中用收藏夹初始化   = [[NSMutableSet alloc] init];实际上是作为NSCFSet创建的,   我不知道那是什么,也不知道是什么   制作它..

NSSet就像所有集合和字符串实际上是一个类集群,即所有共享相同接口的子类集合。当你创建一个集合时,你得到的实际类可能会有所不同,具体取决于它的创建方式。在这种情况下,您将获得NS-Core-Foundation-Set,这是NSSet的标准核心类。

因此,您的问题是favorites被初始化为可变集,但被分配给不可变集。这就是为什么你不能添加任何东西。

这个初始化:

favorites = [[NSMutableSet alloc] init];

......正在被处理:

NSMutableSet *favorites = data.favorites;

如果你有一个实例变量并且你创建了一个同名的局部变量,那么本地符号将在它创建的范围内占主导地位。这似乎有效,因为作为NSSet的子类,NSMutableSet响应所有方法和NSSet的属性。

但是,在构建时,必须从链接器获得大量警告。你不应该忽视这些错误。您应该将它们视为致命错误,因为这是它们在运行时的状态。

解决您的问题:

(1)将data.favorites声明为可变数组并直接访问它。将另一个局部变量分配给同一地址不会给您带来任何好处。

(2)将Favorite作为当前对象的可变数组属性声明。从data.favorites初始化它,如:     self.favorites = [NSMutableSet setWithCapacity:[data.favorites count]];     [self.favorites setSet:data.favorites];     // ...添加或删除项目     data.favorites = self.favorites;

(3)将data.favorites中添加或删除对象的所有逻辑移动到数据模型对象中的自定义方法(见上文)

三是最好的选择。

Edit02

看起来类集群隐藏了集群中所有类的真实类。我运行了以下测试代码:

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    NSSet *s=[NSSet setWithObject:@"setWithObject"];
    NSMutableSet *m=[NSMutableSet setWithCapacity:1];
    [m addObject:@"Added String"];
    NSMutableSet *n = [[NSMutableSet alloc] initWithCapacity:1];
    [self showSuperClasses:s];
    [self showSuperClasses:m];
    [self showSuperClasses:n];
    [self showSuperClasses:@"Steve"];   
}

- (void) showSuperClasses:(id) anObject{
    Class cl = [anObject class];
    NSString *classDescription = [cl description];
    while ([cl superclass]) 
    {
        cl = [cl superclass];
        classDescription = [classDescription stringByAppendingFormat:@":%@", [cl description]];
    }
    NSLog(@"%@ classes=%@",[anObject class], classDescription);
}

...得到了这个输出:

 NSCFSet classes=NSCFSet:NSMutableSet:NSSet:NSObject
 NSCFSet classes=NSCFSet:NSMutableSet:NSSet:NSObject
 NSCFSet classes=NSCFSet:NSMutableSet:NSSet:NSObject
 NSCFString classes=NSCFString:NSMutableString:NSString:NSObject

显然,调试器和类函数的报告在确定属于集群的任何实例的真实类时是无用的。它曾经不是这样的。这是最近的变化。我认为它是Core Foundation的“免费桥接”的一部分。

您可以将项目添加到收藏夹,因为两个类中的收藏夹的所有定义都是NSMutableSet。

在任何情况下,您的问题是您在同一个类中有两个单独的收藏夹定义。您正从链接器收到警告:

Local declaration of "favorites" hides instance variable

我认为问题可以解释为运行时混淆两个收藏夹。您将对象添加到一个收藏夹,但您记录另一个收藏夹。

本地重新定义收藏夹完全没有用处。 删除它并查看问题是否仍然存在。