3

クラスにエラー処理を実装しようとしています。エラーの処理がどのように機能するかを完全に理解しているとは思いませんが、関数内のエラーを ( NSError *__autoreleasing *error) として宣言するようにアドバイスされていることに気付いたので、そうしました。関数を介してエラーを渡す際に問題があります。

次の問題が発生します: 分解すると、次のように問題が発生するようです: (申し訳ありませんが、実際のコードは長すぎますが、問題にとって最も重要なものを抽出しようとしました! 私のコードの範囲に問題が含まれていることを願っています)

次のメソッドを持つObjectAがあるとします。

    -(NSString *) do1: (NSString *) withstuff error:(NSError *__autoreleasing *)error{
         //...
         //error happens
         *error = [[NSError alloc] initwithDomain: domain code: blah userinfo: infodict];
         return nil;
    }
    -(BOOL) do2error:(NSError *__autoreleasing *)error{
         NSString *doesntmatter = [self do1: @"whatever" error: error];
         if (doesntmatter == nil){
              return NO;
         }
    }

別のクラス (AppDelegate オブジェクト) で、次のように呼び出します。

       ObjectA* ob1 = [[ob1 alloc] init];
       NSError *errorBoom = nil;
       if ([ob1 do2error:&errorBoom] == NO){
            NSLog(@"error: %@",errorBoom); //---> bad access error 
       }

errorBoom にアクセスできなくなったようですか? 「__autoreleases」のせいでしょうか、それが実際に何を意味するのかを理解しようとしましたが、これまでのすべての説明は、私がこの分野で非常に新しいため、あまり役に立ちませんでした...これで私を助けてくれることを願っています!

編集:

わかりました、エラーを追跡したと思います。これで原因はわかったのですが、正確な理由はわかりません。「Bad Access」エラー発生の主な原因を抽出する簡単なサンプル アプリケーションを作成しました。

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions
    {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        // Override point for customization after application launch.
        NSError *errtest = nil;
        BOOL testBool = [self messaroundwitherr:&errtest];
        NSLog(@"Filled NSError? : %@",errtest); //<--Causes Bad Access.

        self.window.backgroundColor = [UIColor whiteColor];
        [self.window makeKeyAndVisible];
        return YES;
    } 
    -(BOOL) giveErrornow:(NSError *__autoreleasing *)err {
        NSMutableDictionary *errInfo = [[NSMutableDictionary alloc] init];
        [errInfo setObject:@"I feel like giving you an error!" forKey:NSLocalizedDescriptionKey];
        *err = [[NSError alloc] initWithDomain:@"nonesense"
                                  code:0
                              userInfo:errInfo];
        return  NO;
    }
    -(BOOL) messaroundwitherr:(NSError *__autoreleasing *)err{
        //@autoreleasepool { --> uncommenting that causes the error
            return [self giveErrornow: err];
        //}
    }

したがって、どうやら自動解放プールをアクティブにすると、外部関数 (AppFinishLaunching...) から読み取ることができるようになる前に、err 変数の割り当てが解除されます。なぜそれが正確に起こっているのですか?「@autoreleasepool」は、使用後に変数の割り当てを解除することを知っています。元のコンテキストでは、while ループがありました。それが理由です。ここでは、理解のためだけです。では、@"autoreleasepool" はどのように機能するのでしょうか? コマンド (*__autorelease *) で実際に何が起こるか。

修正する前に、その概念を完全に理解する必要があると思います。NSError (*__autorelease *) err は、ARC の「自動解放されるオブジェクトのポインターへのポインター (そうであれば、いつ)」を定義しますか?

4

2 に答える 2

2

わかりました、2 つの便利な Web サイトの助けを借りて、私はそれを理解したと思います。

http://blog.pioneeringsoftware.co.uk/2012/03/06/out-parameters-when-arcing

http://openbook.galileocomputing.de/apps_entwickeln_fuer_iphone_und_ipad/apps_02_005.html#dodtpc7187bdd-6422-4c4f-92d2-c60983032cf5

(2 つ目は残念ながらドイツ語です :-))

とにかく、基本的なコンセプトは次のとおりです。

  1. 次のような参照による変数の定義:

        (NSError ** err) or (NSError *__autoreleasing * err) 
    

    ARC は常に次のように書き換えます。

        (NSError *__autoreleasing * err)
    

    と書き直し

         *err = [[NSError alloc] initWithDomain:@"nonesense"
                              code:0
                          userInfo:errInfo]
    

         *err = .....]retain] autorelease];
    

    そのため、エラー オブジェクトは autorelease としてマークされます。

  2. ドキュメントに記載されているように:

    自動解放プール ブロックの最後に、ブロック内で自動解放メッセージを受信したオブジェクトに解放メッセージが送信されます。オブジェクトは、ブロック内で自動解放メッセージが送信されるたびに解放メッセージを受信します。

したがって、基本的に 1 と 2 から何が起こるかというと、@autoreleasepool ブロッ​​クのコメントを外すと、エラーが

    giveErrornow:(NSError *__autoreleasing *)err 

関数は autoreleasepool 内から呼び出されるため、*errtest は解放され、使用できるようになる前に割り当てが解除されます。

    NSLog(@"Filled NSError? : %@",errtest)

ええ、基本的にはそれだけです。パラメータを扱うときの一般的な問題のようで、問題のあるコードは次のように変更できます。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    NSError *errtest = nil;
    BOOL testBool = [self messaroundwitherr:&errtest];
    NSLog(@"Filled NSError? : %@",errtest); //<--Causes Bad Access.

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
} 
-(BOOL) giveErrornow:(NSError *__autoreleasing *)err {
    NSMutableDictionary *errInfo = [[NSMutableDictionary alloc] init];
    [errInfo setObject:@"I feel like giving you an error!" forKey:NSLocalizedDescriptionKey];
    *err = [[NSError alloc] initWithDomain:@"nonesense"
                              code:0
                          userInfo:errInfo];
    return  NO;
}
-(BOOL) messaroundwitherr:(NSError *__autoreleasing *)err{
    NSError *tempErr;
    BOOL retVal;
    @autoreleasepool {
        retVal = [self giveErrornow:&tempErr];
    }
    *err = tempErr;
    return retVal;
}

問題についてコメントを残していただければ幸いです。私が問題を正しく理解していることを確認できれば幸いです。

于 2012-08-14T20:26:38.290 に答える
1

最後に、理由をトラップして修正することができました。私の場合、autoreleaseブロックNSError内で割り当てられたオブジェクトが解放され、クラッシュが発生しました。error( ) オブジェクトのコピーを作成し、その外部ブロックを割り当てました。これで問題が解決しました。NSError

- (void)processError:(NSError **) error {

    NSError *unzippingError = nil; // error value which will be assigned inside `@autoreleasepool` block
    NSError *err = nil; // it will hold the copy of error outside block

    @autoreleasepool {
        unzippingError = [NSError errorWithDomain:@"Domain" code:-1
                                         userInfo:@{NSLocalizedDescriptionKey:@"Error message"}];

        //copy the error in another variable to keep reference after @autoreleasepool block
        err = [unzippingError copy];
    }

    if (error) {
        * error =  err;
    }
}
于 2018-08-08T20:56:00.370 に答える