4

【簡潔明瞭に】

カスタムセグエを書きました。

-(void)perform {
UIView *preV = ((UIViewController *)self.sourceViewController).view;
UIView *newV = ((UIViewController *)self.destinationViewController).view;

[preV.window insertSubview:newV aboveSubview:preV];
newV.center = CGPointMake(preV.center.x + preV.frame.size.width, newV.center.y);
[UIView animateWithDuration:0.4
 animations:^{
     newV.center = CGPointMake(preV.center.x, newV.center.y);
     preV.center = CGPointMake(0- preV.center.x, newV.center.y);}
    completion:^(BOOL finished){ [preV removeFromSuperview]; }];
}

セグエがトリガーされた場合、例外はありません。ただし、割り当てを解除 destinationViewControllerします。

別のセグエをトリガーするボタンdestinationViewControllerがクリックされると、アプリがクラッシュします。

削除しようとし[preV removeFromSuperview] ましたが、無駄でした。

[詳細]

私は最近 Object-C を使い始め、プッシュ セグエをシミュレートするカスタム セグエを作成しました。

初めてトリガーされたときは、すべて正常に動作します。

しかし、その後、どのセグエがトリガーされても、アプリがクラッシュし、EXC_BAD_ACCESSエラーが発生します。


私の最初の推測では、これはメモリ管理に関係しているということです。そこにあるものは割り当てを解除する必要がありますが、それが何であるかわかりません。

私の 2 番目の推測は、これは と によって提供されるインフラストラクチャに関係しているということUIViewですUIWindow。繰り返しになりますが、私の知識と経験の不足により、本当の問題が何であるかを理解することはできません。

を使用してナビゲーション バーを非表示にすることで、実際には簡単なアプローチでプッシュ セグエを作成できることはrootviewcontrollerわかっています。コードの生地。


【アップデート】

Phillip Mills と Joachim Isaksson の提案に感謝します。いくつかの実験を行い、ブレークポイントと Zombie ツールを利用した後、

これは私が実現するものです:

  1. カスタム セグエがボタンによってトリガーされた後、アプリは次のセグエもボタンによってトリガーされた場合にのみクラッシュします。を使用して次のセグエをトリガーしviewDidAppearても、クラッシュは発生しません。

  2. クラッシュの背後にある主な理由:

Objective-C メッセージが割り当て解除されたオブジェクト (ゾンビ) に送信されました

[#、イベントタイプ、refCt、ライブラリ、発信者]

0   Malloc  1   UIKit   -[UIClassSwapper initWithCoder:]
1   Retain  2   UIKit   -[UIRuntimeConnection initWithCoder:]
2   Retain  3   UIKit   -[UIRuntimeConnection initWithCoder:]
3   Retain  4   UIKit   -[UIRuntimeConnection initWithCoder:]
4   Retain  5   UIKit   -[UIRuntimeConnection initWithCoder:]
5   Retain  6   UIKit   -[UIRuntimeConnection initWithCoder:]
6   Retain  7   UIKit   -[UIRuntimeConnection initWithCoder:]
7   Retain  8   UIKit   -[UIRuntimeConnection initWithCoder:]
8   Retain  9   UIKit   UINibDecoderDecodeObjectForValue
9   Retain  10  UIKit   UINibDecoderDecodeObjectForValue
10  Retain  11  UIKit   -[UIStoryboardScene setSceneViewController:]
11  Retain  12  UIKit   -[UINib instantiateWithOwner:options:]
12  Release 11  UIKit   -[UINibDecoder finishDecoding]
13  Release 10  UIKit   -[UINibDecoder finishDecoding]
14  Release 9   UIKit   -[UIRuntimeConnection dealloc]
15  Release 8   UIKit   -[UIRuntimeConnection dealloc]
16  Release 7   UIKit   -[UIRuntimeConnection dealloc]
17  Release 6   UIKit   -[UIRuntimeConnection dealloc]
18  Release 5   UIKit   -[UINibDecoder finishDecoding]
19  Release 4   UIKit   -[UIRuntimeConnection dealloc]
20  Release 3   UIKit   -[UIRuntimeConnection dealloc]
21  Release 2   UIKit   -[UIRuntimeConnection dealloc]
22  Retain  3   UIKit   -[UIStoryboardSegue initWithIdentifier:source:destination:]
23  Retain  4   ProjectX    -[pushlike perform]
24  Retain  5   UIKit   -[UINib instantiateWithOwner:options:]
25  Retain  6   UIKit   +[UIProxyObject addMappingFromIdentifier:toObject:forCoder:]
26  Retain  7   UIKit   -[UIProxyObject initWithCoder:]
27  Retain  8   UIKit   -[UIRuntimeConnection initWithCoder:]
28  Retain  9   UIKit   UINibDecoderDecodeObjectForValue
29  Retain  10  UIKit   UINibDecoderDecodeObjectForValue
30  Release 9   UIKit   -[UINib instantiateWithOwner:options:]
31  Release 8   UIKit   +[UIProxyObject removeMappingsForCoder:]
32  Release 7   UIKit   -[UINibDecoder finishDecoding]
33  Release 6   UIKit   -[UIRuntimeConnection dealloc]
34  Release 5   UIKit   -[UINibDecoder finishDecoding]
35  Release 4   UIKit   -[UINibDecoder finishDecoding]
36  Release 3   ProjectX    -[pushlike perform]
37  Retain  4   libsystem_sim_blocks.dylib  _Block_object_assign
38  Retain  5   UIKit   -[UIApplication _addAfterCACommitBlockForViewController:]
39  Release 4   UIKit   -[UIStoryboardSegue dealloc]
40  Release 3   UIKit   _UIApplicationHandleEvent
41  Release 2   UIKit   -[UIStoryboardScene dealloc]
42  Retain  3   UIKit   _applyBlockToCFArrayCopiedToStack
43  Release 2   UIKit   _applyBlockToCFArrayCopiedToStack
44  Release 1   UIKit   __destroy_helper_block_739
45  Release 0   UIKit   _applyBlockToCFArrayCopiedToStack
46  Zombie  -1  UIKit   -[UIApplication sendAction:to:from:forEvent:]

つまり、(私が間違っていなければ)

destinationViewControllerカスタム セグエは、別のセグエをトリガーするボタン ( の ) がクリックされた後にオブジェクト C メッセージが送信される、オブジェクトの割り当てを解除する何かをトリガーしました。


詳細

prepareForSegueビュー間でデータを渡す必要がないため、1 つも呼び出されていません。

私のセグエはすべて同じ方法でトリガーされます:

- (void)viewDidLoad
{
    [super viewDidLoad];
    CGRect buttonFrame = CGRectMake( 10, 40, 200, 50 );
    UIButton *button = [[UIButton alloc] initWithFrame: buttonFrame];
    [button setTitle: @"Go" forState: UIControlStateNormal];
    [button addTarget:self action:@selector(nextView) forControlEvents:UIControlEventTouchUpInside];
    [button setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
    [self.view addSubview:button]; 
}

- (void)nextView{
    [self performSegueWithIdentifier:@"push" sender:self];
}

私はARCを有効にしているので、自分で割り当て解除を実際に行っていません..


[更新 2]

ゾンビ化したオブジェクトがdestinationViewControllerカスタムセグエです。

カスタム セグエを呼び出さなくremoveFromSuperviewても、オブジェクトがゾンビに変わるのを止めることはできません。

作成したカスタム セグエの代わりに、通常のモデル セグエまたはプッシュ セグエ (rootViewController を使用) を使用する限り、ゾンビは発生せず、すべて正常に動作します。

4

3 に答える 3

5

セグエの実行後に新しいコントローラーが保持されないという理由だけで、クラッシュが発生しています。

あなたがすることはこれです:

  • src および dest コントローラがインスタンス化されます
  • あなたはあなたのアニメーションを実行します
  • 完了したら、srcビューを削除します
  • srcコントローラーは解放されますが、ウィンドウはrootViewController まだそれを指しており、宛先ビューコントローラーはウィンドウの階層に追加されていません。

これは意図したとおりに機能します。

-(void)perform {
  UIView *preV = ((UIViewController *)self.sourceViewController).view;
  UIView *newV = ((UIViewController *)self.destinationViewController).view;

  UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
  newV.center = CGPointMake(preV.center.x + preV.frame.size.width, newV.center.y);
  [window insertSubview:newV aboveSubview:preV];

  [UIView animateWithDuration:0.4
                   animations:^{
                       newV.center = CGPointMake(preV.center.x, newV.center.y);
                       preV.center = CGPointMake(0- preV.center.x, newV.center.y);}
                   completion:^(BOOL finished){
                       [preV removeFromSuperview];
                       window.rootViewController = self.destinationViewController;
                   }];
}
于 2012-11-01T08:52:09.693 に答える
2

iOS では、viewController とその .view の関係は特別です。

これらのランタイム呼び出し ( initWithCoder:UIStoryboardSegue initWithIdentifier:source:destination:、など) はすべて、裏で viewController に関係する何かにアクセスしようとする試みを指しています。実行したことの 1 つは、スーパービューで期待されていた場所からメインの .view を削除したことです。

removeFromSuperviewsourceViewController の .view でa を実行すると、大混乱を招くことになります。

プッシュ セグエではないビューが必要な場合は、セグエをモーダル セグエにすることができます。これにより、ナビゲーション バーをいじる必要がなくなりますが、CocoaTouch ランタイムがセグエの作業 (およびその後のクリーンアップ) を行うことができます。

コントロールを同じviewControllerにとどめたい場合は、コードを変更して実行する[preV setHidden:YES]か、[preV setAlpha:0]. それはまだそこにあるので、ゾンビに変わることはありません。上記の 2 つのアクションのいずれかを逆にすることで元に戻すことができます。

[preV removeFromSuperview] を削除 (または呼び出しをコメント アウト) して、newV で行っていることから戻ったときにスライドして戻すこともできます。

編集

考慮すべきもう1つのことは、 variableでの __block Storage TypepreVの使用です。これは、ローカルで宣言しているため、およびスコープを離れており、元のviewControllerが原因でそれが消えている可能性があるためです。完了ブロックでは、ref-count が 0 になり、そこに到達するまでに削除された変数への参照で終わる可能性があります。 __blockこれを防ぐためのものです。

于 2012-10-26T06:23:20.243 に答える