iphone - ARC `BAD_ACCESS` 使用 NSString 的类别方法时

标签 iphone objective-c ios cocoa-touch exc-bad-access

我这样调用我的实用方法:

NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"dd.MM.yy HH:mm"];
NSString *dateString = [dateFormat stringFromDate:[NSDate date]];

return [[Environment sharedInstance].versionLabelFormat replaceTokensWithStrings:
     @"VERSION", APP_VERSION, 
     @"BUILD", APP_BULD_NUMBER, 
     @"DATETIME" , dateString, 
     nil ];

这是 NSString类别法

-(NSString *)replaceTokensWithStrings:(NSString *)firstKey, ... NS_REQUIRES_NIL_TERMINATION{

    NSString *result = self;

        va_list _arguments;
        va_start(_arguments, firstKey);

        for (NSString *key = firstKey; key != nil; key = va_arg(_arguments, NSString*)) {

            // The value has to be copied to prevent crashes
            NSString *value = [(NSString *)(va_arg(_arguments, NSString*))copy];

            if(!value){
                // Every key has to have a value pair otherwise the replacement is invalid and nil is returned

                NSLog(@"Premature occurence of nil. Each token must be accompanied by a value: %@", result);
                return nil;
            }

            result = [result replaceToken:key withString:value];
        }
        va_end(_arguments);

    // Check if there are any tokens which were not yet replaced (for example if one value was nil)

    if([result rangeOfString:@"{"].location == NSNotFound){
        return result;
    } else {
        NSLog(@"Failed to replace tokens failed string still contains tokens: %@", result);
        return nil;
    }
}

否 在下一行我必须添加 copy声明否则会有一个带有dateString的僵尸:

NSString *value = [(NSString *)(va_arg(_arguments, NSString*))copy];

更具体地说,僵尸报告告诉我的是:

 1 Malloc       NSDateFormatter stringForObjectValue:
   Autorelease  NSDateFormatter stringForObjectValue:
 2 CFRetain     MyClass versionString:
 3 CFRetain     replaceToken:withString:
 2 CFRelease    replaceToken:withString:
 1 CFRelease    replaceTokensWithStrings:   ( One release too much!)
 0 CFRelease    MyClass versionString:
-1 Zombie       GSEventRunModal

尽管 copy语句似乎解决了问题我想了解代码不符合 ARC 的原因,以便 BAD_ACCESS没有 copy 就会发生对于值字符串。

最佳答案

正如其他人所说,问题在于您从可能与 ARC 不兼容的可变参数列表中检索对象的方式。

va_arg 有一种有趣的方式来返回 ARC 可能不知道的特定类型的值。我不确定这是 clang 中的错误还是 ARC 的预期行为。我会澄清这个问题并相应地更新帖子。

作为解决方法,只需在参数处理中使用 void 指针并以 ARC 安全方式将它们正确转换为对象即可避免此问题:

for (NSString *key = firstKey; key != nil; key = (__bridge NSString *)va_arg(_arguments, void *)) {
    NSString *value = (__bridge NSString *)va_arg(_arguments, void *);
    NSAssert(value != NULL, @"Premature occurence of nil.");
    result = [result stringByReplacingToken:key
                                 withString:value];
}

编辑:__bridge 转换告诉 ARC 不要对所有权做任何事情。它只是期望对象是活着的,不会转移或放弃所有权。然而,keyvalue 变量在使用时保持对对象的强引用。

第二次编辑:似乎 clang/ARC 应该知道 va_arg 中的类型并发出警告或做正确的事情(see this, for example)。

我尝试重现您的问题,但没有成功。一切都适合我:

$ clang --version
> Apple clang version 4.0 (tags/Apple/clang-421.10.48) (based on LLVM 3.1svn)

您使用哪个 Xcode 版本?

关于iphone - ARC `BAD_ACCESS` 使用 NSString 的类别方法时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11576293/

相关文章:

ios - 如何在 Swift 中的滑出菜单的主 UIView 后面添加 UIView?

iphone - 使用更新的配置文件重建 IPA 文件

ios - C 回调如何通过用户数据指针返回 Objective-C 对象?

sql-server - 从 native iOS 应用程序查询 SQL Server 数据库

ios - 从 NSString 时间戳返回正确的 NSDate

iphone - 如何在 HTML 文档中找到数字然后用作 NSString?

iphone - CoreData删除对象

ios - 是否有类/库/框架可以在 iOS 中以 SWIFT 语言调用 Web 服务?

iOS 单元测试依赖

ios - 无法使用 cocoapods 安装 Firebase。它说 "Unable to get local issuer certificate"