繁体   English   中英

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

[英]Crash when scrolling a UIPickerView set up with IB

这是我第一次使用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:<显示的行数

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

回溯(忽略主要):

#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");
}

调查了Apple的文档,这证明了我之前的猜测。 资源编程指南

创建nib文件中的对象,保留计数为1,然后自动释放。 但是,在重建对象层次结构时,UIKit使用setValue:forKey:方法重新建立对象之间的连接,该方法使用可用的setter方法,或者如果没有可用的setter方法,则默认保留对象。 如果为nib-file对象定义出口,则还应定义用于访问该出口的setter方法。 出口的Setter方法应保留其值,并且包含顶级对象的出口的setter方法必须保留其值以防止它们被释放。 如果未将顶级对象存储在出口中,则必须保留loadNibNamed:owner:options:方法返回的数组或数组内的对象,以防止这些对象过早释放。

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

对于Mac OS X和UIKit,管理nib文件中顶级对象的推荐方法是在File的Owner对象中为它们创建出口,然后定义setter方法以根据需要保留和释放这些对象。 即使在应用程序使用垃圾收集的情况下,Setter方法也为您提供了包含内存管理代码的适当位置。 实现setter方法的一种简单方法是使用@property语法,让编译器为您创建它们。

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

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

工作得很好。

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

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

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

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

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

我会说没有太多详细的调查,你应该确保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.完成后,确保文件的所有者与A有这种类型的连接。

Thiese接口构建器连接可能很棘手,因为它们并没有告诉您太多关于这个问题的信息,并且它们不会像您在幕后那样做很多事情。

祝你好运解决这个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM