滚动使用IB设置的UIPickerView时崩溃

时间:2009-12-07 10:23:27

标签: iphone interface-builder uipickerview

这是我第一次使用IB,但是在用了一两个亲密的日子后,我相信我已经开始理解它了。这只是我的方式,我可能会忽略一些简单的事情:

我已经设置了一个UIPickerView并将其加入到IB中的DataSource和Delegate对象(在我的情况下都是不同的类)。这允许选择器在我运行应用程序时显示,这在以前的任何测试运行中都没有显示时非常令人鼓舞。 ;)但是,当我滚动UIPickerView时,程序崩溃了,我找不到我在回溯中引用的任何代码。经过相当多的故障排除后,我认为我已经将崩溃范围缩小到两个不同的情况,就回溯问题而言:

-pickerView的返回值:numberOfRowsInComponent:>显示的行数

  • 一旦动作开始选择新行,应用就会崩溃
  • 如果我尝试使用-selectRow应用程序崩溃:inComponent:animated:

回溯(忽略主要):

#0  0x955e8688 in objc_msgSend ()
#1  0x0167bea8 in -[UIPickerView table:cellForRow:column:reusing:] ()
#2  0x016773c1 in -[UIPickerView table:cellForRow:column:] ()
#3  0x017fef53 in -[UITable createPreparedCellForRow:column:] ()
#4  0x018077c8 in -[UITable _updateVisibleCellsNow] ()
#5  0x018027cf in -[UITable layoutSubviews] ()
#6  0x03ac42b0 in -[CALayer layoutSublayers] ()
#7  0x03ac406f in CALayerLayoutIfNeeded ()
#8  0x03ac38c6 in CA::Context::commit_transaction ()
#9  0x03ac353a in CA::Transaction::commit ()
#10 0x03acb838 in CA::Transaction::observer_callback ()
#11 0x007b8252 in __CFRunLoopDoObservers ()
#12 0x007b765f in CFRunLoopRunSpecific ()
#13 0x007b6c48 in CFRunLoopRunInMode ()
#14 0x000147ad in GSEventRunModal ()
#15 0x00014872 in GSEventRun ()
#16 0x0168a003 in UIApplicationMain ()

-pickerView的返回值:numberOfRowsInComponent:<显示的行数

  • 在动画停止并选择行后,应用程序崩溃
  • 如果我尝试使用-selectRow:inComponent:animated:
  • ,该应用不会崩溃

回溯(忽略主要):

#0  0x955e8688 in objc_msgSend ()
#1  0x0167700d in -[UIPickerView _sendSelectionChangedForComponent:] ()
#2  0x017f4187 in -[UIScroller _scrollAnimationEnded] ()
#3  0x016f732c in -[UIAnimator stopAnimation:] ()
#4  0x016f7154 in -[UIAnimator(Static) _advance:] ()
#5  0x00017739 in HeartbeatTimerCallback ()
#6  0x007b7ac0 in CFRunLoopRunSpecific ()
#7  0x007b6c48 in CFRunLoopRunInMode ()
#8  0x000147ad in GSEventRunModal ()
#9  0x00014872 in GSEventRun ()
#10 0x0168a003 in UIApplicationMain ()

我的委托和数据源实现如下:

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return (NSInteger)3;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    return (NSInteger)4;
}

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
  //it will probably be better to use the method following when creating the rows, so I can better customize it 
    return @"strings";
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
    NSLog(@"selected a row");
}

4 个答案:

答案 0 :(得分:4)

调查了Apple文档,这证明了我之前的猜测。来自Resource Programming Guide

  

创建nib文件中的对象   保留计数为1然后   自动释放。因为它重建了   然而,对象层次结构,UIKit   重新建立之间的联系   使用setValue的对象:forKey:   方法,它使用可用的   setter方法或保留对象   如果没有setter方法,则为default   可用。如果你定义出口   nib-file对象,你也应该   定义用于访问的setter方法   那个出口。 Setter方法   网点应保留其价值,   和出口的setter方法   包含顶级对象必须   保留他们的价值观以防止他们   从被解除分配。如果你不   存储顶级对象   出口,你必须保留   由...返回的数组   loadNibNamed:owner:options:方法或   数组中的对象   防止这些物体   过早释放。

因此,顶级对象是自动释放的,您必须将它们保留在代码中。还介绍了处理该方法的推荐方法:

  

对于Mac OS X和UIKit,   推荐的管理方式   nib文件中的顶级对象是   在文件中为它们创建出口   所有者对象然后定义setter   保留和释放这些方法   对象根据需要。塞特方法给出   你是一个合适的地方   你的内存管理代码,甚至是   您的应用程序使用的情况   垃圾收集。一个简单的方法   实现你的setter方法是   使用@property语法并让   编译器为你创建它们。

我在示例代码中测试了这种方法 - 为文件所有者类中的委托和数据源对象定义了出口,并将它们连接到IB中。并且在文件所有者类中为这些出口定义了一个属性:

@property (nonatomic, retain) NSObject<UIPickerViewDelegate>* myDelegate;
@property (nonatomic, retain) NSObject<UIPickerViewDataSource>* mySource;

工作得很好。

答案 1 :(得分:0)

从上一条评论中可能会发现UIPickerView的委托被删除,之后你的picker.delegate引用了无效的内存......
可能的解决方案:

  1. 确保您的委托对象在您使用选择器时有效 - 将其保留在某处并在您的选择器被销毁时释放(例如在选择器的父视图控制器dealloc方法中)
  2. 在你的dealloc方法中,将picker的delegate属性设置为nil - 它必须删除崩溃,但它也会停止处理picker事件。

答案 2 :(得分:0)

你说你的pickerview的委托和数据源是不同的类。你在哪里设置这些?在您的xib中或以编程方式设置连接?您是否可能不保留为委托和数据源创建的对象?

因此,下次需要引用它们时,它们已被释放并且您将获得异常。

为什么要将不同的对象用作委托和数据源?为什么不在viewcontroller中实现它们呢?

答案 3 :(得分:0)

我会说没有太多详细的调查,你应该确保IB中的每个对象都通过保留的属性连接到文件所有者。这是我遇到崩溃的首要原因。一旦某个东西被引用,甚至没有引用,但不是某个文件所有者的子代,它就会导致崩溃。除了制作这个链条所需的连接外,没有任何连接,没有代表。如果这没有崩溃,请建立一个连接,然后测试,然后重复。滚动崩溃几乎总是发生,因为某些东西是自动释放的。

如果你在不使用[[B alloc] init]的情况下得到了对象b,那么在运行循环开始之后它会消失。 (在您第一次触摸视图后)。解决方法是告诉对象b保留,通常是在另一个对象中引用它之后,

-(void)connectTo:(B*)b {
     self.myReference = b
     [B retain];
}

另一个解决方案是通过IB。在标题中执行此操作:

@interface a : NSObject{
    id<UIPickerViewDelegate> myReferenceToDelegate;
}

@property(nonatomic, retain) IBOutlet id<UIPickerViewDelegate> myReferenceToDelegate

@end

然后你需要进入界面构建器,并将对象A上的myReferenceToDelegate连接拖到对象B.完成后,确保File的所有者与A有这种连接。

Thiese界面构建器连接可能很棘手,因为它们并没有告诉您太多关于这个问题的信息,而且它们在幕后的效果也不尽如人意。

祝你好运解决这个问题。