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 不再可用了?那是因为“__autoreleasing”,我试图理解它的实际含义,但到目前为止所有的解释对我来说都不是很有用,因为我在这个领域很新......我希望你能帮助我!

编辑:

好的,我想我找到了错误。现在我知道是什么原因造成的,但我不知道具体原因。我编写了一个简单的示例应用程序,它提取了“Bad Access”-Error 发生的主要来源:

    - (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];
        //}
    }

所以显然激活了自动释放池,使得 err 变量在能够从外部函数(AppFinishLaunching ...)读取之前被释放。现在为什么会发生这种情况?我知道“@autoreleasepool”在使用后会释放变量。在原始上下文中,我有一个 while 循环,这就是我这样做的原因。这里只是出于理解的原因。那么@"autoreleasepool" 是如何工作的呢?命令 (*__autorelease *) 实际发生了什么。

我想在修复它之前我必须完全理解它的概念。NSError (*__autorelease *) err 是否为 ARC 定义了一个“指向将要自动释放的对象的指针(如果是,何时)”?

4

2 回答 2

2

好的,我想我想通了,在两个有用的网站的帮助下:

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

(不幸的是,第二个是德语:-))

无论如何,基本概念如下:

  1. 通过引用定义变量,例如:

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

    总是使 ARC 将其重写为:

        (NSError *__autoreleasing * err)
    

    和重写

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

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

    所以错误对象被标记为自动释放。

  2. 现在如文档中所述:

    在自动释放池块结束时,在块内收到自动释放消息的对象会收到释放消息——对象每次在块内收到自动释放消息时都会收到释放消息

所以基本上从 1 和 2 发生的是,当取消注释 @autoreleasepool 块时,错误正在创建

    giveErrornow:(NSError *__autoreleasing *)err 

函数,它是从自动释放池中调用的,所以 *errtest 在它可以被使用之前被释放和释放

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

是的,基本上就是这样。处理 out 参数时似乎是一个普遍问题,并且可以更改有问题的代码,例如:

- (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

最后,我能够找到原因并修复它。在我的例子中,在autoreleaseNSError块内分配的对象被释放,导致崩溃。我创建了 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 回答