1

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

我正在尝试创建自己的标签栏控制器和视图,因为我已经得出结论UITabBarController并且UITabBar不允许我做我需要做的事情(我需要彻底改变标签栏的外观)。

我已经对UIViewController和进行了子类化,分别UIView创建了我的类SYTabBarControllerSYTabBar

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

- (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:终止调用抛出异常 (lldb)

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

由于我是 Objective-C(和 C)的新手,所以我对内存管理知之甚少,我想我的变量做错了什么。我已经运行了 Instruments,有趣的是,我看到所有UIButton实例都活得很好。因此,按钮似乎在那里,只是指针不知何故消失了。

您可以提供的任何帮助将不胜感激。

4

3 回答 3

1

当您创建一个 NSDictionary 时,它会通过调用来复制您提供的键-copy,这会调用-copyWithZone:. 这些方法是NSCopying协议的一部分。

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

备择方案:

  • 改用一个NSMapTable

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

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

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

于 2012-12-27T08:22:42.270 回答
1

这里有两个错误。

首先,在 NSDictionary 的文档中,它的键必须符合<NSCopying>协议,即它们应该响应copyWithZone:,因为键在插入字典时被复制。(这仅适用于键,对应的对象不必是可复制的。)发生异常是因为UIButton(它只是 and 的子类UIControlUIView没有实现此方法(如果您阅读错误消息,就会清楚)。

因此,您不能将按钮用作字典中的键 - 如果您需要按钮和视图控制器之间的关联(顺便说一下,我怀疑这里存在设计问题,此解决方案似乎相当骇人听闻),请为按钮使用单独的键和视图控制器,并使用例如简单的线性搜索来查找相应按钮的视图控制器。

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

于 2012-12-27T08:23:27.733 回答
0

错误消息表明消息被发送到无法处理该消息的对象。在你的情况下copyWithZone:被发送到一个UIButton. copyWithZone:NSCopying协议中声明的方法,但 UIButton 不采用该协议。

发送此特定消息是因为您正在向 NSDictionary 添加一个条目,其中 UIButton 作为键。如NSDictionary 类参考中所述,复制一个键(使用copyWithZone:;键必须符合 NSCopying 协议),并将副本添加到新字典中。

于 2012-12-27T08:22:29.807 回答