1

私のアプリはシミュレーターで完全に動作していますが、デバイスでアプリをテストすると、最終的にランダムな EXC_BAD_ACCESS が発生し、アプリがクラッシュします。

数日間のテストの後、このエラーの原因となっているコードを見つけたと思います。ある時点で、いくつかのサブビューをコントローラーのメイン ビューに追加する必要があります。ユーザーがアプリを操作すると、それらのサブビューがスーパービューから削除され、新しいサブビューが追加されます。サブビューを削除しないとアプリはクラッシュしませんが、サブビューを削除すると、アプリは最終的に EXC_BAD_ACCESS を取得してクラッシュします。

削除されたサブビューは、すでに完全にリリースされているか、そのようなものであるときにリリースメッセージを取得するようです...これは私がARCを使用している最初のアプリなので、おそらく何かが欠けています...

関連するコードは次のとおりです。

#define kWordXXX 101
#define kWordYYY 102

...

// This is called after the user interaction, it removes the
// old subviews (if they exist) and add new ones
- (void)updateWords
{    
    [self removeWords];

    if (self.game.move.wordXXX) {
        WordView *wordXXX = [self wordViewForTypeXXX];
        wordXXX.tag = kWordXXX;
        // self.wordsView is the view where the subviews are added
        [self.wordsView addSubview:wordXXX]; 
    }

    if (self.game.move.wordYYY) {
        WordView *wordYYY = [self wordViewForTypeYYY];
        wordYYY.tag = kWordYYY;
        [self.wordsView addSubview:wordYYY];
    }
}

// Remove the old words if they exist
- (void)removeWords
{    
    WordView *wordXXX = (WordView *)[self.wordsView viewWithTag:kWordXXX];
    WordView *wordYYY = (WordView *)[self.wordsView viewWithTag:kWordYYY];

    if (wordXXX) {
        [wordXXX removeFromSuperview];
    }

   if (wordYYY) {
       [wordYYY removeFromSuperview];
   }
}

サブビューの作成方法は次のとおりです。私はこのコードを特に誇りに思っているわけではなく、リファクタリングが必要ですが、以前は機能しなかった理由を理解する必要があります。

- (WordView *)wordViewWithFrame:(CGRect)frame andType:(WordType)type
{
    WordView *wordView = nil;

    if (type == SystemWord) {
        frame.origin.y += 15;
        wordView = [[SystemWordView alloc] initWithFrame:frame];
        } else if (type == StartWord) {
        wordView = [[StartWordView alloc] initWithFrame:frame];
        } else if (type == UserWord) {
        wordView = [[UserWordView alloc] initWithFrame:frame];
    } else {
        wordView = [[RivalWordView alloc] initWithFrame:frame];
    }

    return wordView;
}

- (WordView *)wordViewForTypeXXX
{
    WordType type = self.game.move.wordType;
    WordView *wordView = nil;
    CGRect wordViewFrame = CGRectMake(0,
                                      0,
                                      self.scoreView.frame.size.width,
                                      35);

    wordView = [self wordViewWithFrame:wordViewFrame andType:type];

    wordView.word = self.game.move.word;

    return wordView;
}

- (WordView *)wordViewForTypeYYY
{
    WordType type = self.game.move.wordType;
    CGFloat y = self.game.move.word ? 35 : 0;
    WordView *wordView = nil;
    CGRect wordViewFrame = CGRectMake(0,
                                      y,
                                      self.scoreView.frame.size.width,
                                      35);

    wordView = [self wordViewWithFrame:wordViewFrame andType:type];

    wordView.word = self.game.move.word;

    if (self.game.move.word && [wordView isKindOfClass:[PlayerWordView class]]) {
        ((PlayerWordView *)wordView).points = [NSNumber     numberWithInteger:self.game.move.points];
    }

    return wordView;
}

これはしばらくの間機能し、その後クラッシュします。つまり、ビューが数回削除および追加され、すべて問題ないように見えますが、しばらくするとアプリは EXC_BAD_ACCESS を取得します。

どんな助けでも永遠に感謝します!

PS: 私の英語でごめんなさい

編集:デバイスでゾンビを使用できず、スタックトレースが表示されません。

EXC_BAD_ACCESS を取得した後、lldb で「bt」と入力すると、次のようになります。

 * thread #1: tid = 0x2503, 0x3bb735b0 libobjc.A.dylib`objc_msgSend + 16, stop reason = EXC_BAD_ACCESS (code=1, address=0x11d52465)  
  frame #0: 0x3bb735b0 libobjc.A.dylib`objc_msgSend + 16  
  frame #1: 0x3473f6fe Foundation`probeGC + 62  
  frame #2: 0x34745706 Foundation`-[NSConcreteMapTable removeObjectForKey:] + 34  
  frame #3: 0x360b3d5c UIKit`-[_UIImageViewPretiledImageWrapper dealloc] + 80  
  frame #4: 0x3bb75488 libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 168  
  frame #5: 0x33e16440 CoreFoundation`_CFAutoreleasePoolPop + 16  
  frame #6: 0x347ea184 Foundation`__NSThreadPerformPerform + 604  
  frame #7: 0x33ea8682 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14  
  frame #8: 0x33ea7ee8 CoreFoundation`__CFRunLoopDoSources0 + 212  
  frame #9: 0x33ea6cb6 CoreFoundation`__CFRunLoopRun + 646  
  frame #10: 0x33e19ebc CoreFoundation`CFRunLoopRunSpecific + 356  
  frame #11: 0x33e19d48 CoreFoundation`CFRunLoopRunInMode + 104  
  frame #12: 0x379dd2ea GraphicsServices`GSEventRunModal + 74  
  frame #13: 0x35d2f300 UIKit`UIApplicationMain + 1120  
  frame #14: 0x0005bd40 Dr. Cuaicap`main(argc=1, argv=0x2fda8d10) + 116 at main.m:16  
  frame #15: 0x3bfafb20 libdyld.dylib`start + 4  
4

5 に答える 5

0

参考までに。

これに似たクラッシュもありましたが、修正は実際にはコードではなく、アセット自体にありました。

Apple のドキュメントに記載されているように、アセットのサイズ変更可能な領域は 1 ピクセル x 1 ピクセルである必要があるため、必ずそうてください。

- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets

画像のスケーリングまたはサイズ変更中、キャップで覆われた領域はスケーリングまたはサイズ変更されません。代わりに、各方向のキャップで覆われていないピクセル領域が、左から右、上から下にタイル化され、画像のサイズが変更されます。この手法は、可変幅のボタンを作成するためによく使用されます。ボタンの角は同じ丸みを保ちますが、必要に応じて中央領域が拡大または縮小します。最高のパフォーマンスを得るには、サイズが 1x1 ピクセル領域のタイル領域を使用してください。

于 2013-10-25T10:16:30.457 に答える
0

通常、アプリがシミュレーターで動作し、デバイスでは動作しない場合は、メモリに依存すると思います。メモリ警告を確認してください。

于 2013-05-21T08:25:09.190 に答える
0

コメントからの議論 -

そうです、ゾンビはシミュレーターでのみ機能しますが、クラッシュはシミュレーターでも再現されると思います。デバイスではアプリがクラッシュするが、シミュレーターではクラッシュしないケースはほとんどありません。同じiOSバージョンのシミュレーターを使用していることを確認して、もう一度試してみてください。_UIImageViewPretiledImageWrapper に関する情報は見つかりませんでしたが、これが解決の鍵となります。

この回答_UIImageViewPretiledImageWrapperは、に関連していることを示唆してい-[UIImage resizableImageWithCapInsets:]ます。

OPからのコメント:

ある時点で、サイズ変更可能な UIImage に基づいて UIImageView を作成していましたが、UIImageView の最終サイズを設定する前に画像を設定していました。これは、UIImage のサイズ変更により、何らかの形でメモリが破損するようです。

于 2013-05-21T20:45:09.117 に答える
-1

WordView *wordXXX をインターフェイス宣言に移動して、インスタンス変数にします。

于 2013-05-21T08:49:15.560 に答える
-1

これは、メモリの解放に問題があるようです。宣言する

ワードビュー *ワードビュー

.h ファイルで。したがって、強力な参照が作成されます

于 2013-05-21T08:56:20.313 に答える