1

IB を使用するのはこれが初めてですが、1 日か 2 日親密に過ごした後、IB を理解し始めていると思います。これは、ここで簡単なことを見落としている可能性があるという私の言い方です。

UIPickerView をセットアップし、IB の DataSource および Delegate オブジェクトに結合しました (私の場合は両方とも異なるクラスです)。これにより、アプリを実行したときにピッカーが表示されるようになります。これは、以前のテスト実行で表示されていなかった場合に非常に心強いものです。;) しかし、UIPickerView をスクロールすると、プログラムがクラッシュし、バックトレースで参照されているコードが見つかりません。かなりのトラブルシューティングを行った後、バックトレースに関する限り、クラッシュを 2 つの異なるケースに絞り込んだと思います。

-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: メソッドを使用してオブジェクト間の接続を再確立します。このメソッドは、使用可能なセッター メソッドを使用するか、使用可能なセッター メソッドがない場合はデフォルトでオブジェクトを保持します。nib-file オブジェクトのアウトレットを定義する場合は、そのアウトレットにアクセスするためのセッター メソッドも定義する必要があります。アウトレットのセッター メソッドはその値を保持する必要があり、最上位オブジェクトを含むアウトレットのセッター メソッドは、割り当てが解除されないように値を保持する必要があります。最上位オブジェクトをアウトレットに保存しない場合は、loadNibNamed:owner:options によって返された配列を保持する必要があります。

そのため、最上位のオブジェクトは自動解放されて作成され、コード内に保持する必要があります。それを処理するための推奨される方法も説明されています。

Mac OS X と UIKit の両方で、nib ファイルの最上位オブジェクトを管理するための推奨される方法は、ファイルの所有者オブジェクトでそれらのアウトレットを作成し、必要に応じてそれらのオブジェクトを保持および解放するセッター メソッドを定義することです。セッター メソッドは、アプリケーションがガベージ コレクションを使用する状況でも、メモリ管理コードを含める適切な場所を提供します。セッター メソッドを実装する簡単な方法の 1 つは、@property 構文を使用して、コンパイラーにそれらを作成させることです。

サンプル コードでこのアプローチをテストしました。ファイル オーナー クラスでデリゲート オブジェクトとデータ ソース オブジェクトのアウトレットを定義し、IB でそれらを接続しました。そして、ファイル所有者クラスで、それらのアウトレットのプロパティを定義しました:

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

うまくいきました。

于 2009-12-11T08:56:48.973 に答える
0

あなたは、pickerviewのデリゲートとデータソースが異なるクラスであると言いました。これらをどこに設定しますか?xibで、またはプログラムで接続を設定しますか?デリゲートとデータソース用に作成したオブジェクトが保持されない可能性はありますか?

したがって、次に参照する必要があるときに、それらは解放され、例外が発生します。

デリゲートとデータソースとして異なるオブジェクトを使用したいのはなぜですか?ビューコントローラ自体にそれらを実装してみませんか?

于 2009-12-13T06:43:37.950 に答える
0

あまり詳細な調査を行わなくても、IB 内のすべてのオブジェクトが、保持するプロパティを介してファイルの所有者に接続されていることを確認する必要があります。これが、私が見たクラッシュの最大の理由です。何かが参照されるか、参照されていなくても、何らかの方法でファイルの所有者の子ではない場合、クラッシュが発生します。このチェーンを作成するために必要なものを除いて、接続も代理もなしで開始します。これがクラッシュせずに機能する場合は、1 つの接続を確立してから、テストを繰り返します。スクロール クラッシュは、ほとんどの場合、何かが自動解放されたために発生します。

[[B alloc] init] を使用せずにオブジェクト b を取得した場合は、実行ループが進行した後にそれがなくなることを期待してください。(初めてビューに触れることができるようになった後)。解決策は、オブジェクト b を保持するように指示することです。通常、別のオブジェクトで b を参照した後、

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

これに対するもう 1 つの解決策は、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 に答える
0

最後のコメントから、 UIPickerView のデリゲートが削除され、その後 picker.delegate が無効なメモリを参照している可能性があります...
可能な解決策:

  1. ピッカーを使用している間、デリゲート オブジェクトが有効であることを確認してください。どこかに保持し、ピッカーが破棄されているときに解放します (ピッカーの親ビュー コントローラーの dealloc メソッドなど)。
  2. dealloc メソッドで、ピッカーのデリゲート プロパティを nil に設定します。クラッシュを取り除く必要がありますが、ピッカー イベントの処理も停止します。
于 2009-12-10T10:05:46.153 に答える