Obj-C:消失的变量

时间:2012-12-27 08:04:35

标签: objective-c variables memory-management uibutton nsdictionary

我是Objective-C的新手,可以使用一些帮助我解决一个让我感到难过的问题。我把它称为消失变量的问题。我将尝试在下面描述它。

我正在尝试创建自己的标签栏控制器和视图,因为我得出的结论是UITabBarControllerUITabBar不允许我做我需要做的事情(我需要更改)标签栏的外观非常明显。)

我已经将UIViewControllerUIView分类为分别创建我的课程SYTabBarControllerSYTabBar

SYTabBar中,我编写了一种方法,以编程方式将UIButton添加到我的视图中:

- (UIButton*) addButtonWithFrame:(CGRect)frame action:(SEL)action target:(id)target imageToken:(NSString*)imageToken
{
    UIButton *b = [[UIButton alloc]initWithFrame:frame];
    [b addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];

    UIImage
    *unselectedImage = [UIImage imageNamed:APPEND_STRING(@"TBI+Unselected+", @"imageToken")],
    *selectedImage   = [UIImage imageNamed:APPEND_STRING(@"TBI+Selected+", @"imageToken")],
    *selectedBgImage = [UIImage imageNamed:@"TBI+SelectedBG"];

    [b setImage:unselectedImage forState:UIControlStateNormal];
    [b setImage:selectedImage forState:UIControlStateHighlighted];
    [b setImage:selectedImage forState:UIControlStateSelected];
    [b setBackgroundImage:selectedBgImage forState:UIControlStateHighlighted];
    [b setBackgroundImage:selectedBgImage forState:UIControlStateSelected];
    [b setShowsTouchWhenHighlighted:NO];
    [b setAdjustsImageWhenHighlighted:NO];
    [b setAdjustsImageWhenDisabled:NO];
    [self addSubview:b];

    return b;
}

我在SYTabBarController中调用此方法来创建五个按钮:

    UIButton *button1 = [self.tabBar addButtonWithFrame:CGRectMake(0, 0, 64, 45)
                                                 action:@selector(changeView:)
                                                 target:self
                                             imageToken:@"Popular"];

    UIButton *button2 = [self.tabBar addButtonWithFrame:CGRectMake(64, 0, 64, 45)
                                                 action:@selector(changeView:)
                                                 target:self
                                             imageToken:@"Feed"];

    UIButton *button3 = [self.tabBar addButtonWithFrame:CGRectMake(64*2, 0, 64, 45)
                                                 action:@selector(changeView:)
                                                 target:self
                                             imageToken:@"Capture"];

    UIButton *button4 = [self.tabBar addButtonWithFrame:CGRectMake(64*3, 0, 64, 45)
                                                 action:@selector(changeView:)
                                                 target:self
                                             imageToken:@"Likes"];

    UIButton *button5 = [self.tabBar addButtonWithFrame:CGRectMake(64*4, 0, 64, 45)
                                                 action:@selector(changeView:)
                                                 target:self
                                             imageToken:@"Profile"];

然后,仍然在SYTabBarController中,我尝试将这五个按钮添加为字典中的键,该字典存储为属性(请参阅下面的代码)。该字典包含键入按钮的UIViewController个实例。我们的想法是,当按下五个按钮中的一个时,它将调用选择器changeView:,然后选择器将从字典中检索相应的UIViewController实例并显示其视图。

    self.viewControllersKeyedToButtons = [NSDictionary dictionaryWithObjectsAndKeys:

                                          rootViewController,
                                          button1,

                                          [[SYViewController_Feed alloc] init],
                                          button2,

                                          [[SYViewController_Capture alloc] init],
                                          button3,

                                          [[SYViewController_Likes alloc] init],
                                          button4,

                                          [[SYViewController_Profile alloc] init],
                                          button5,

                                          nil];

初始化字典时,我的代码崩溃了。我收到此错误消息:

  

2012-12-27 02:54:28.498 Smuvy [17931:c07] - [UIButton copyWithZone:]:无法识别的选择器发送到实例0x717fc10       2012-12-27 02:54:28.500 Smuvy [17931:c07] *由于未捕获的异常'NSInvalidArgumentException'终止应用程序,原因:' - [UIButton copyWithZone:]:无法识别的选择器发送到实例0x717fc10'       * 第一次抛出调用堆栈:       (0x174c012 0x1223e7e 0x17d74bd 0x173bbbc 0x173b94e 0x17ce714 0x1712b55 0x172d5a8 0x65d6 0x2c6e 0x1687b7 0x168da7 0x169fab 0x17b315 0x17c24b 0x16dcf8 0x3676df9 0x3676ad0 0x16c1bf5 0x16c1962 0x16f2bb6 0x16f1f44 0x16f1e1b 0x1697da 0x16b65c 0x2aad 0x29d5)       libc ++ abi.dylib:terminate调用抛出异常       (LLDB)

现在这就是谜。当我在调试器中跟踪变量button5时,我注意到在我尝试实例化字典之前,它就消失了!难怪我收到错误消息。

由于我是Objective-C(和C)的新手,我对内存管理知之甚少,我想我的变量做错了。我运行乐器并且有趣的是,我看到所有UIButton个实例都很活跃。因此,似乎按钮就在那里,它只是指针以某种方式消失了。

非常感谢您提供的任何帮助。

3 个答案:

答案 0 :(得分:1)

当您通过调用-copy来调用-copyWithZone:来创建NSDictionary it makes copies of the keys that you provide时。这些方法是NSCopying协议的一部分。

不幸的是,UIButton不符合该协议。它不可复制。当NSDictionary尝试向其发送-copy消息时,会引发异常,如您所发现的那样。

备选方案:

  • 改为使用NSMapTable

  • 使用+[NSValue valueWithNonretainedObject:]+[NSValue valueWithPointer:]将密钥包装在NSValue中。 (如果您使用ARC,NSValue将无法确保您的对象被保留,因此您需要以其他方式强烈引用该对象。)

  • 使用其他方法确定视图控制器和UIButton可以共享的密钥。 (例如,让他们都引用相同的字符串。)

  • 根本不要使用地图。也许将视图控制器保存在一个数组中,并使用UIButton上的tag将相应视图控制器的索引存储在数组中。

答案 1 :(得分:1)

这里有两个错误。

首先,在NSDictionary的文档中,声明其密钥必须符合<NSCopying>协议,即。即他们应该回复copyWithZone:,因为密钥在插入字典时会被复制。 (这仅适用于密钥,相应的对象无需可复制。)发生异常是因为UIButton(它只是UIControlUIView的子类)未实现此方法(如果您阅读了错误消息,则会很清楚。)

所以你不能将这些按钮用作字典中的键 - 如果你需要按钮和视图控制器之间的关联(顺便说一句,我怀疑这里有一个设计问题,这个解决方案看起来相当hackish),使用单独的键对于按钮和视图控制器,并使用例如简单的线性搜索来查找相应按钮的视图控制器。

或者,如果您不害怕使用CoreFoundation,您可以使用CFDictionary(与NSDictionary免费桥接)并提供不复制密钥的相应自定义密钥回调

答案 2 :(得分:0)

错误消息表示将消息发送到无法处理该消息的对象。在您的情况下,copyWithZone:会被发送到UIButton的实例。 copyWithZone:NSCopying协议中声明的方法,但UIButton不采用该协议。

发送此特定消息,因为您要将一个条目添加到NSDictionary,并将UIButton作为键。如NSDictionary class reference中所述,复制密钥(使用 copyWithZone:;密钥必须符合NSCopying协议),并将副本添加到新字典中。