【问题标题】:Bad Access Error when trying to pass error through functions尝试通过函数传递错误时出现错误访问错误
【发布时间】:2012-08-10 21:00:19
【问题描述】:

我正在尝试在我的班级中实现一些错误处理。我认为我对处理错误的工作原理并不完全了解,但我注意到人们建议将函数中的错误声明为 (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 定义了“指向将要自动释放的对象的指针(如果是,何时)”?

【问题讨论】:

  • 如果你去掉__autoreleasing限定符,只使用NSError **怎么办?
  • @nielsbot 我认为在 ARC 中使用指向指针的指针时不指定生命周期限定符是错误的。
  • 您是否尝试过对domain 使用字符串文字,对infodict 使用nil?您可能会将某些内容传递给已释放的错误对象。我还看到过看似无关的错误会导致代码每次都在同一行中断的情况。尝试运行分析器并修复所有警告。
  • __autoreleasing 与 ARC 有关,它是正确的。我看到的唯一问题是,在 do1:error: 中,他应该在取消引用之前检查 error 是否为 nil,但这不是他所感叹的问题。我猜在一些没有发布的代码中还有其他事情发生。
  • 我认为我的 NSError 对象做得很好,我检查了一下。似乎整个 errorBoom 不再可用了。 _autorelease 是做什么的?我想也许它导致错误对象在此期间被释放,而 NSLog 尚未执行?

标签: objective-c nserror


【解决方案1】:

好的,我想我想通了,借助两个有用的网站:

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;
}

欢迎您留下有关问题的任何 cmet,如果我确信我正确理解了问题,那就太好了。

【讨论】:

  • 我遇到了同样的情况,在 [self giveErrornow:&tempErr] 行获得 EXE_BAD_ACCESS。有什么想法吗?
  • 我们必须在@autoreleasepool末尾保留错误的副本,否则会在@autoreleasepool之后释放对象。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    • 2018-07-17
    • 1970-01-01
    相关资源
    最近更新 更多