4

NSXMLパーサーに間違ったURLを送信すると、アプリがクラッシュする理由(RSSリーダー)を調べようとします。私はEXC_BAD_ACCESSを手に入れました。それで、いくつかの検索の後で、私はゾンビを使わなければならないことに気づきました。そこで、次の引数を環境に追加しました。

CFZombieLevel = 3
NSMallocStaclLogging = YES
NSDeallocateZombies = NO
MallocStackLoggingNoCompact = YES
NSZombieEnabled = YES
NSDebugEnabled = YES
NSAutoreleaseFreedObjectCheckEnabled = YES

malloc_error_breakブレークポイントとしても追加しました。次に、GUIに他のブレークポイントをいくつか追加し、[ビルドとデバッグ]を押しました。コンソールに次のメッセージが表示されます。

2010-08-28 18:41:49.761 RssReader[2850:207] *** -[XMLParser respondsToSelector:]: message sent to deallocated instance 0x59708e0

次のメッセージが表示されることもあります。 wait_fences: failed to receive reply: 10004003

「shellmalloc_history28500x59708e0」と入力すると、次のようになります。

...
ALLOC 0x5970870-0x59709d7 [size=360]: thread_a0aaa500 |start | main | UIApplicationMain | -[UIApplication _run] | CFRunLoopRunInMode | CFRunLoopRunSpecific | __CFRunLoopRun | __CFRunLoopDoSource1 | __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ | PurpleEventCallback | _UIApplicationHandleEvent | -[UIApplication sendEvent:] | -[UIApplication handleEvent:withNewEvent:] | -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] | -[UIApplication
...
----
FREE  0x5970870-0x59709d7 [size=360]: thread_a0aaa500 |start | main | UIApplicationMain | -[UIApplication _run] | CFRunLoopRunInMode | CFRunLoopRunSpecific | __CFRunLoopRun | __CFRunLoopDoSource1 | __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ | PurpleEventCallback | _UIApplicationHandleEvent | -[UIApplication sendEvent:] | -[UIApplication handleEvent:withNewEvent:] | -[UIApplication
...
ALLOC 0x59708e0-0x597090f [size=48]: thread_a0aaa500 |start | main | UIApplicationMain | -[UIApplication _run] | CFRunLoopRunInMode | CFRunLoopRunSpecific | __CFRunLoopRun | __CFRunLoopDoSource1 | __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ | PurpleEventCallback | _UIApplicationHandleEvent | -[UIApplication sendEvent:] | -[UIApplication handleEvent:withNewEvent:] | -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] | -[UIApplication 
... 
Binary Images:
    0x1000 -     0x6ff3 +RssReader ??? (???) <6EBB16BC-2BCE-CA3E-C76E-F0B078995E2D> /Users/svp/Library/Application Support/iPhone Simulator/4.0.1/Applications/AF4CE7CA-88B6-44D4-92A1-F634DE7B9072/RssReader.app/RssReader
    0xe000 -   0x1cfff3 +Foundation 751.32.0 (compatibility 300.0.0) <18F9E1F7-27C6-2B64-5B9D-BAD16EE5227A>
...

これは何を意味するのでしょうか?0x59708e0がどのオブジェクトであるかを知るにはどうすればよいですか?アプリがクラッシュする原因となるコードが見つかりません。私が知っている唯一のことは、それがrespondsToSelectorメッセージでなければならないということです。すべてのrespondsToSelectorメッセージにブレークポイントを追加しました。それらはヒットしますが、アプリはその時点ではクラッシュしません。また、1つを除いてコメントアウトしようとしたところ、アプリがクラッシュしました。コメントアウトされていないものはヒットしませんでした。メモリリークはどこにありますか?

次の紛らわしいことは、parseErrorOccurredデリゲートが呼び出されても、NSXMLパーサーがその作業を継続することです。エラーが2回スローされた後、アプリがクラッシュします。

Run with Peformance Toolでゾンビが無効になっているのはなぜですか?

編集:

今、私はこの指示を使用しました(投稿できません。申し訳ありません。スパム防止)これは機能しました。これはどういう意味ですか?

@Graham:パーサークラスでインスタンス化しますNSXMLParser

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {  
        ... 
    NSXMLParser *rssParser = [[NSXMLParser alloc] initWithData:responseData];  
    [rssParser setDelegate:self];
        ...
    [rssParser parse];
    //[rssParser release];
}

エラーを検索しているときに、リリース方法をコメントアウトしました。現在、rssParserがパーサークラスでリリースされることはありません。

私のRootViewControllerクラスでは、パーサーをインスタンス化します。

- (void)loadData {
    if (newsItems == nil) {
        [activityIndicator startAnimating];  

        XMLParser *rssParser = [[XMLParser alloc] init];  
        [rssParser parseRssFeed:@"http://feeds2.feedburner.com/TheMdnShowtest" withDelegate:self];  

        [rssParser release];
        rssParser = nil;

    } else {  
        [self.tableView reloadData];  
    }  
}

ここでリリースしなくてもクラッシュしません。しかし、割り当てごとにリリースを行う必要がありますか?または、自動リリースNSXMLParserする必要がありconnectionDidFinishLoadingますか?

4

4 に答える 4

1

XMLParserを割り当てている場所。そのコードを見てみましょう。あなたはそれを自動解放していませんよね?

どこかでリリースされています...プロパティに割り当てられていますか?そのプロパティの定義を見てみましょう。

後でrespondsToSelector:メソッドが呼び出されますが、それはどのメソッドでもかまいません。重要なのは、XMLParserが意図した前にリリースされたということです。

于 2010-08-29T01:53:32.190 に答える
1

すべてのゾンビはリークとして通知されるため、メモリリークで使用すると、ゾンビは無効になります。ゾンビツールを実行するには、[機器]メニューに移動して[ファイル]> [新規]を実行し、ゾンビツールのみを選択します。そうすると、ゾンビがメッセージを受信するとプログラムが停止し、小さなポップアップでリンクが表示されます。そのゾンビオブジェクトとその歴史に

于 2010-08-28T18:51:44.167 に答える
1

RootViewController.hで、プロパティrssParserを宣言しました。

@class XMLParser;

@interface RootViewController : UITableViewController {
    ...
    XMLParser *rssParser;
}
...
@property (retain, nonatomic) XMLParser *rssParser;

@end

RootViewController.mには、errorOccurredというメソッドがあります。

- (void)errorOccurred {
    [rssParser release];
    rssParser = nil;
    if ([activityIndicator isAnimating]) {
        [activityIndicator stopAnimating];
    }
}

XMLParser.mファイルで、errorOccurredを2回呼び出します。

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    ...

    if ([_delegate respondsToSelector:@selector(errorOccurred)])
        [_delegate errorOccurred];
    else  
    {   
        [NSException raise:NSInternalInconsistencyException  
                    format:@"Delegate doesn't respond to errorOccurred:"];  
    }
}

_delegateがどのように宣言されているかを確認するには、チュートリアルhttp://www.cocoadevblog.com/iphone-tutorial-creating-a-rss-feed-readerを参照してください。これはid変数であり、独自のsetterメソッドとgetterメソッドがあります(プロパティとして宣言することもできます)。2回目:

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
    ...

    if ([_delegate respondsToSelector:@selector(errorOccurred)])
        [_delegate errorOccurred];
    else  
    {   
        [NSException raise:NSInternalInconsistencyException  
                    format:@"Delegate doesn't respond to errorOccurred:"];  
    }
}  

rssParser変数のリリースは次のようになります。

RootViewController.mのloadDataでは、リリースしません。残念ながら、loadDataで実行するとクラッシュします。エラーが発生した場合(上記を参照)またはdeallocメソッドでのみリリースされます。しかし、それはプロパティとして宣言されているので、うまくいくはずだと思います。

- (void)loadData {
    if (newsItems == nil) {
        [activityIndicator startAnimating];  

        self.rssParser = [[XMLParser alloc] init];  
        [rssParser parseRssFeed:@"http://www.wrongurl.com/wrongrss.xml" withDelegate:self];  
    } else {  
        [self.tableView reloadData];  
    }  
}

XMLParser.mでは、parseメソッドの後にリリースします。

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {  
   ...

    NSXMLParser *rssParser = [[NSXMLParser alloc] initWithData:responseData];

    [rssParser setDelegate:self];

    [rssParser parse];

    [rssParser release];
    rssParser = nil;
}  

2つの変数名は同じ(rssParser)ですが、異なることに注意してください。RootViewControllerでXMLParserのインスタンスを作成し、XMLParser.mでNSXMLParserのインスタンスを作成しています。

ですから、新しいエラーが発生しないか、誰かがなぜこれが悪いのか説明するまでは、そのままにしておきます。

于 2010-08-30T11:42:23.187 に答える
1

ここでリリースするように設定したのと同じインスタンス変数であるrssParserもありますか...

- (void)errorOccurred {
    [rssParser release];
    rssParser = nil;
    if ([activityIndicator isAnimating]) {
        [activityIndicator stopAnimating];
    }
}

...deallocメソッドでリリースされましたか?これにより、ダブルリリースが発生し、EXEC_BAD_ACCESSが発生します。

于 2011-01-12T06:08:45.307 に答える