简体   繁体   English

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

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

This is my first time using IB, but after spending a one or two intimate days with it I believe I'm beginning to understand it. 这是我第一次使用IB,但在花了一两个亲密的日子后,我相信我已经开始理解它了。 That's just my way of saying I might be overlooking something simple here: 这只是我的方式,我可能会忽略一些简单的事情:

I've set up a UIPickerView and joined it to its DataSource and Delegate object in IB (both different Classes in my case). 我已经设置了一个UIPickerView并将其加入到IB中的DataSource和Delegate对象(在我的情况下都是不同的类)。 This allows the picker to show up when I run the app, which is very encouraging when it hasn't been showing up in any previous test runs. 这允许选择器在我运行应用程序时显示,这在以前的任何测试运行中都没有显示时非常令人鼓舞。 ;) However, when I scroll the UIPickerView, the program crashes, and I can't find any of my code referenced in the backtrace. ;)但是,当我滚动UIPickerView时,程序崩溃了,我找不到我在回溯中引用的任何代码。 After quite a bit of troubleshooting, I think I've narrowed down the crash to two distinct cases, as far as the backtrace is concerned: 经过相当多的故障排除后,我认为我已经将崩溃范围缩小到两个不同的情况,就回溯问题而言:

the return value of -pickerView:numberOfRowsInComponent: > the number of rows displayed -pickerView的返回值:numberOfRowsInComponent:>显示的行数

  • The app crashes as soon as a motion is begun to select a new row 一旦动作开始选择新行,应用程序就会崩溃
  • The app crashes if I try to use -selectRow:inComponent:animated: 如果我尝试使用-selectRow应用程序崩溃:inComponent:animated:

backtrace (ignoring main): 回溯(忽略主要):

#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 ()

the return value of -pickerView:numberOfRowsInComponent: < the number of rows displayed -pickerView的返回值:numberOfRowsInComponent:<显示的行数

  • The app crashes after the motion ceases and the row is selected 在动画停止并选择行后应用程序崩溃
  • The app does not crash if I try to use -selectRow:inComponent:animated: inComponent:动画,如果我尝试使用-selectRow的应用程序不会崩溃:

backtrace (ignoring main): 回溯(忽略主要):

#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 ()

My delegate and datasource implementations follow: 我的委托和数据源实现如下:

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

Investigated Apple documentation a bit and it proved my previous guess. 调查了Apple的文档,这证明了我之前的猜测。 From Resource Programming Guide : 资源编程指南

Objects in the nib file are created with a retain count of 1 and then autoreleased. 创建nib文件中的对象,保留计数为1,然后自动释放。 As it rebuilds the object hierarchy, however, UIKit reestablishes connections between the objects using the setValue:forKey: method, which uses the available setter method or retains the object by default if no setter method is available. 但是,在重建对象层次结构时,UIKit使用setValue:forKey:方法重新建立对象之间的连接,该方法使用可用的setter方法,或者如果没有可用的setter方法,则默认保留对象。 If you define outlets for nib-file objects, you should also define a setter method for accessing that outlet. 如果为nib-file对象定义出口,则还应定义用于访问该出口的setter方法。 Setter methods for outlets should retain their values, and setter methods for outlets containing top-level objects must retain their values to prevent them from being deallocated. 出口的Setter方法应保留其值,并且包含顶级对象的出口的setter方法必须保留其值以防止它们被释放。 If you do not store the top-level objects in outlets, you must retain either the array returned by the loadNibNamed:owner:options: method or the objects inside the array to prevent those objects from being released prematurely. 如果未将顶级对象存储在出口中,则必须保留loadNibNamed:owner:options:方法返回的数组或数组内的对象,以防止这些对象过早释放。

So the top level objects are created autoreleased and you must retain them in your code. 因此,顶级对象是自动释放的,您必须将它们保留在代码中。 There's also described recommended way to handle that: 还有描述推荐的方法来处理:

For both Mac OS X and UIKit, the recommended way to manage the top-level objects in a nib file is to create outlets for them in the File's Owner object and then define setter methods to retain and release those objects as needed. 对于Mac OS X和UIKit,管理nib文件中顶级对象的推荐方法是在File的Owner对象中为它们创建出口,然后定义setter方法以根据需要保留和释放这些对象。 Setter methods give you an appropriate place to include your memory-management code, even in situations where your application uses garbage collection. 即使在应用程序使用垃圾收集的情况下,Setter方法也为您提供了包含内存管理代码的适当位置。 One easy way to implement your setter methods is to use the @property syntax and let the compiler create them for you. 实现setter方法的一种简单方法是使用@property语法,让编译器为您创建它们。

I've tested this approach in a sample code - defined outlets for delegate and data source objects in file owner class and connected them in IB. 我已经在示例代码中测试了这种方法 - 为文件所有者类中的委托和数据源对象定义了出口,并在IB中将它们连接起来。 And in file owner class defined a property for those outlets: 并且在文件所有者类中为这些出口定义了一个属性:

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

Worked fine. 工作得很好。

From your last comment may follow that the delegate for your UIPickerView is being deleted and after that your picker.delegate references to an invalid memory... 从您的上一条评论可能会删除您的UIPickerView的委托被删除,然后您的picker.delegate引用无效的内存...
Possible solutions: 可能的解决方案:

  1. Ensure that your delegate object will be valid while you're using your picker - retain it somewhere and release when your picker is being destroyed (eg in picker's parent view controller dealloc method) 确保您的委托对象在您使用选择器时有效 - 将其保留在某处并在您的选择器被销毁时释放(例如在选择器的父视图控制器dealloc方法中)
  2. In your dealloc method set picker's delegate property to nil - it must remove the crash, but it will also stop handling picker events. 在你的dealloc方法中,将picker的delegate属性设置为nil - 它必须删除崩溃,但它也将停止处理picker事件。

You said your pickerview's delegate and datasource are different classes. 你说你的pickerview的委托和数据源是不同的类。 Where are you setting these up? 你在哪里设置这些? In your xib or setting the connections programatically? 在您的xib中或以编程方式设置连接? Is it possible that the objects you have created for the delegate and datasource are not retained? 您是否可能不保留为委托和数据源创建的对象?

So the next time when they need to be referenced, they have been released and you get an exception. 因此,下次需要引用它们时,它们已被释放,您将获得异常。

Why do you want to use different objects as delegate and datasource? 为什么要将不同的对象用作委托和数据源? Why not implement them in your viewcontroller itself? 为什么不在viewcontroller本身实现它们呢?

I would say without too much detailed investigation, that you should make sure every object in IB is connected through properties that retain, to File's Owner. 我会说没有太多详细的调查,你应该确保IB中的每个对象都通过保留的属性连接到文件所有者。 That is the number one reason I have seen for crashes. 这是我遇到崩溃的首要原因。 As soon as something is referred to, or even not referred to, but not a child of file's owner in some way, it causes a crash. 一旦某个东西被引用,甚至没有引用,但不是某个文件所有者的子代,它就会导致崩溃。 Start with no connections, no delegates, aside from the ones needed to make this chain. 除了制作这个链条所需的连接外,没有任何连接,没有代表。 If this works without a crash, make one connection, then test, then repeat. 如果这没有崩溃,请建立一个连接,然后测试,然后重复。 Scrolling crashes almost always happen because something was autoreleased. 滚动崩溃几乎总是发生,因为某些东西是自动释放的。

If you got object b without using [[B alloc] init] expect it to be gone after the run loop proceeds. 如果你在没有使用[[B alloc] init]的情况下得到了对象b,那么在运行循环开始后它会被消失。 (After the first time you are able to touch your view). (在您第一次触摸视图后)。 The cure is to tell object b to retain, generally after making a reference to it in another object, 治疗方法是告诉对象b保留,通常是在另一个对象中引用它之后,

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

The other solution to this is through IB. 对此的另一个解决方案是通过IB。 in the headers do this: 在标题中执行此操作:

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

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

@end

Then you need to go into interface builder, and drag a connection from myReferenceToDelegate on object A to the object B. Once this is done, make sure File's owner has this type of connection to A. 然后你需要进入界面构建器,并将对象A上的myReferenceToDelegate的连接拖到对象B.完成后,确保文件的所有者与A有这种类型的连接。

Thiese interface builder connections can be tricky, because they don't tell you much about the problem, and they don't do as much as you might thing behind the scenes. Thiese接口构建器连接可能很棘手,因为它们并没有告诉您太多关于这个问题的信息,并且它们不会像您在幕后那样做很多事情。

Good luck solving this one. 祝你好运解决这个问题。

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

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