1

这是我第一次使用 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

4 回答 4

4

稍微调查了一下 Apple 文档,它证明了我之前的猜测。从资源编程指南

nib 文件中的对象创建时保留计数为 1,然后自动释放。然而,当它重建对象层次结构时,UIKit 使用 setValue:forKey: 方法重新建立对象之间的连接,该方法使用可用的 setter 方法,如果没有可用的 setter 方法,则默认保留对象。如果您为 nib 文件对象定义了 outlet,您还应该定义一个 setter 方法来访问该 outlet。outlet 的 setter 方法应该保留它们的值,而包含顶级对象的 outlet 的 setter 方法必须保留它们的值,以防止它们被释放。如果您不在 outlet 中存储顶级对象,则必须保留 loadNibNamed:owner:options 返回的数组:

因此,顶级对象是自动发布的,您必须将它们保留在代码中。还描述了处理该问题的推荐方法:

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

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

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

工作得很好。

于 2009-12-11T08:56:48.973 回答
0

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

  1. 确保您的委托对象在您使用选择器时有效 - 将其保留在某处并在您的选择器被销毁时释放(例如,在选择器的父视图控制器 dealloc 方法中)
  2. 在您的 dealloc 方法中,将选取器的委托属性设置为 nil - 它必须消除崩溃,但它也会停止处理选取器事件。
于 2009-12-10T10:05:46.153 回答
0

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

因此,下次需要引用它们时,它们已被释放,并且您会遇到异常。

为什么要使用不同的对象作为委托和数据源?为什么不在视图控制器本身中实现它们?

于 2009-12-13T06:43:37.950 回答
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。完成后,确保文件的所有者与 A 具有这种类型的连接。

这些界面构建器连接可能很棘手,因为它们不会告诉您太多有关问题的信息,而且它们不会像您在幕后所做的那样做很多事情。

祝你好运解决这个问题。

于 2010-02-24T11:03:15.187 回答