簡體   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