为什么这段代码可以在iPhone 5、4和4s上运行,但在5s上却不行?

7
我有以下代码用于添加一些UIBarButton到我的项目中。这在以下情况下都能正常工作:iOS 7.1上的所有设备:
  • 模拟器 iPhone Retina 3.5 英寸;
  • 模拟器 iPhone Retina 4 英寸;
  • 模拟器 iPhone Retina 4 英寸 (64 位);
  • iPhone 4;
  • iPhone 4s。

但我没有 iPhone 5 设备进行测试。

在新的 iPhone 5s 上无法正常工作,有何不同?

以下是代码:

    -(void)setupNavigationBar
{
    self.saveSearchButton =  [UIButton buttonWithType:UIButtonTypeCustom];
    [self.saveSearchButton setImage:[UIImage imageNamed:@"Save Search"] forState:UIControlStateNormal];
    [self.saveSearchButton setImage:[UIImage imageNamed:@"Save Search Active"] forState:UIControlStateHighlighted|UIControlStateSelected];
    [self.saveSearchButton addTarget:self action:@selector(saveSearchButtonpressed)forControlEvents:UIControlEventTouchUpInside];
    [self.saveSearchButton setFrame:CGRectMake(0, 0, 23, 31)];

     self.changeLayutButton =  [UIButton buttonWithType:UIButtonTypeCustom];
    [self.changeLayutButton setImage:[UIImage imageNamed:@"View List"] forState:UIControlStateNormal];
    [self.changeLayutButton setImage:[UIImage imageNamed:@"View Grid"] forState:UIControlStateHighlighted|UIControlStateSelected];
    [self.changeLayutButton addTarget:self action:@selector(changeViewLayoutButtonPressed)forControlEvents:UIControlEventTouchUpInside];
    [self.changeLayutButton setFrame:CGRectMake(0, 0, 23, 31)];

    self.sortByButton =  [UIButton buttonWithType:UIButtonTypeCustom];
    [self.sortByButton setImage:[UIImage imageNamed:@"Sort By"] forState:UIControlStateNormal];
    [self.sortByButton setImage:[UIImage imageNamed:@"Sort By Active"] forState:UIControlStateHighlighted|UIControlStateSelected];
    [self.sortByButton addTarget:self action:@selector(sortByButtonPressed)forControlEvents:UIControlEventTouchUpInside];
    [self.sortByButton setFrame:CGRectMake(0, 0, 23, 31)];

    UIBarButtonItem *fixedItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
    fixedItem.width = 20;

    UIBarButtonItem *saveSearchButton = [[UIBarButtonItem alloc] initWithCustomView:self.saveSearchButton];
    UIBarButtonItem *changeViewButton = [[UIBarButtonItem alloc] initWithCustomView:self.changeLayutButton];
    UIBarButtonItem *sortByButton = [[UIBarButtonItem alloc] initWithCustomView:self.sortByButton];


    NSArray *barbuttonitems = @[sortByButton, fixedItem, changeViewButton, fixedItem,saveSearchButton];

    self.navigationItem.rightBarButtonItems = barbuttonitems;

    self.lastLayoutUsed = [[NSUserDefaults standardUserDefaults]objectForKey:@"LastLayoutUsed"];

    if ([self.lastLayoutUsed isEqualToString:@"GridLayout"]){
        self.changeLayutButton.selected = YES;
        [NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(highlightButton:) userInfo:self.changeLayutButton repeats:NO];
    }
}

我已经逐行检查过代码,所有属性都不为nil,并且具有有效值。我也检查了所有图像的大小是否正确。

为什么模拟器上每个设备都能正常工作,而实际的 iPhone 5S 却不能?

此外,由于 iPhone 4S 和 5S 两部手机具有完全相同的屏幕宽度和 iOS 版本,我真的很困惑?

按钮根本没有显示出来。没有编译器警告和控制台错误。

更新

在 iPhone 5 上测试了上述代码,它完美地工作了。这使我相信它必须与 iPhone 5S 的64位版本有关?

更新2

从该方法中删除了所有代码,并将其更改为像这样的一个非常简单的按钮:

         self.saveSearchButton =  [UIButton buttonWithType:UIButtonTypeCustom];
[self.saveSearchButton setTitle:@"Save" forState:UIControlStateNormal];
    [self.saveSearchButton setTintColor:[UIColor blueColor]];
    UIBarButtonItem *saveSearchButton = [[UIBarButtonItem alloc] initWithCustomView:self.saveSearchButton];
    self.navigationItem.rightBarButtonItem = saveSearchButton;

现在这个问题在任何设备或模拟器上都无法运行。

我在这里做错了什么?

更新3 - 解决方案!

好吧,原来只是一个简单的问题引起了这么多麻烦。我的UIButtons被声明为weak而不是strong - 我曾经认为UI元素需要是弱的,因为它们经常离开视图?

我在评论区得到了帮助,找到了这个解决方案。

这也不能解释为什么在iPhone 4S和iPhone 5上声明为weak时它仍然有效。在64位模拟器上声明为weak也可以正常工作。

这是否意味着必须使用64位模拟器作为指南,并且在实际测试中必须进行测试,因为似乎模拟器在UIKit测试方面并不准确?

我很想更多地了解这个问题。


抱歉,我应该说得更清楚。按钮根本没有显示出来。没有编译器警告,也没有控制台警告。 - Robert J. Clegg
如果我猜的话,这可能与图像命名有关。尝试不使用任何空格,并确保命名的大小写正确。据我所知,iOS始终区分大小写,而MacOS则不是。 - manecosta
是的,在目录中所有图像都是X2尺寸。除了逐步执行代码之外,还有其他调试方法吗?感谢您一直以来的帮助 - 我很感激。 - Robert J. Clegg
@Tander,你尝试过清理项目吗?在清理时按住Alt键,这样它就会清理整个构建文件夹。 - Tancrede Chazallet
1
这只是一个猜测,但你是否尝试在需要的地方使用双精度/浮点数,例如这里 CGRectMake(0, 0, 23, 31) -> CGRectMake(0.0, 0.0, 23.0, 31.0)? - Volker
显示剩余13条评论
3个回答

6
问题在于[UIButton buttonWithType:UIButtonTypeCustom]并不像苹果文档中所述那样留下一个对象实例。而UIBarButton需要某种实例。https://developer.apple.com/library/ios/documentation/uikit/reference/UIButton_Class/UIButton/UIButton.html 解决方案如苹果文档所述,使用alloc+init构造函数创建按钮。

buttonWithType:

“此方法是用于创建具有特定配置的按钮对象的便捷构造函数。如果您子类化UIButton,则此方法不返回您子类的实例。如果要创建特定子类的实例,则必须直接分配/初始化该按钮。”


我完全同意你的观点,按钮对象的实例确实需要 buttonType:...,但这告诉我如果缺少它,它不应该在任何设备上工作,这不仅限于 iPhone 5s 设备,这将是一个 iOS 问题。 - Popeye
显然,苹果公司原谅了程序员过去使用这种结构的行为。但在像iPhone5S这样的新设备上,他们实施了更严格的方式。为什么以及如何?只有苹果知道。他们会让我们知道吗?不,至少不是清晰和直接地。我们必须处理他们运营事务的方式,唯一能做的就是准确地按照AppleDocs阅读。 - Totumus Maximus
问题在于这不是设备的问题,而是iOS的问题。无论我是否错过了它或者使用了[[UIButton alloc] init],我总是不得不加上它,否则无论是什么设备或iOS版本都无法正常工作。你有没有任何文档可以证明这是基于设备而不是iOS变得更加严格了? - Popeye
1
这是我根据问题和经验做出的假设。解决这个问题的方法在于他所拥有的弱引用以及因为“buttonWithType:”不会创建实例,所以参考计数不会增加,只要引用离开声明它的函数作用域,按钮就会被置空。然而,基于这个问题中的陈述,这在旧设备上是有效的,这意味着ARC的工作方式已经发生了改变(如果他使用了它),或者是“initWithCustomView:”。我找不到确凿的证据,所以这都是假设。 - Totumus Maximus
1
buttonWithType:UIButtonTypeCustom 不会创建一个 UIButton 的子类。这个答案在这里是不正确的。请参考 Brian Slick 下面的答案。在这种情况下,最终的修复方法应该是将 UIButtons 声明为 strong,据我所知。如果我错了,请纠正我。 - dbulnes
显示剩余2条评论

2
@TotumusMaximus 你误解了文档的意思。请注意第一部分:
如果你创建一个 UIButton 的子类...
这意味着,如果你尝试执行 `[MyAwesomeButton buttonWithType:UIButtonTypeCustom]`,将创建一个 UIButton 实例,而不是 MyAwesomeButton 实例。为了获得一个 MyAwesomeButton 实例,你需要执行 `[[MyAwesomeButton alloc] init]`。在 OP 的问题中没有涉及到任何按钮子类,因此文档中的这部分内容不适用于此处。

1
我也得出了同样困惑的结论。我对自己说,“buttonWithType并不会创建一个子类,因此,被投票选中的答案是错误的。”这是否意味着真正的解决方案是给这些UIButtons强引用并完成它? - dbulnes

2
如果你的属性是弱引用,并且你直接将一个新创建的对象分配给它们,那么按照ARC的规则,它们应该立即被释放并设置为nil,因为没有其他东西持有它们的强引用。在其他设备/模拟器上工作的事实是实现的偶然性。
现在视图不再在内存压力条件下卸载,所以对于UI元素,没有真正需要使用弱引用。如果你仍然想使用弱引用,请使用正确的模式:创建一个本地(强)引用,将其添加到父视图中,然后再分配给属性 - 在这一点上,按钮将拥有一个所有权引用(父视图),因此不会被ARC删除:
UIButton *button = [UIButton buttonWithType:whatever];
[self.view addSubview:button];
self.button = button;

感谢解释。那有助于我的理解。为简单起见,我将把所有UI属性声明为strong。 - Robert J. Clegg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接