由于 NSDictionary 自动释放,应用程序在 iOS7 中崩溃,但在 8 和 9 中没有

Posted

技术标签:

【中文标题】由于 NSDictionary 自动释放,应用程序在 iOS7 中崩溃,但在 8 和 9 中没有【英文标题】:App crashes in iOS7 but not in 8 &9 due to NSDictionary autorelease 【发布时间】:2016-07-29 06:40:40 【问题描述】:
+ (NSString *)getValueforLocale:(NSString*) i18nkey :(NSString*)locale
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSLog(@"paths are : %@",paths);
    NSString *libraryDirectory = [paths objectAtIndex:0];
    NSLog(@"libraryDirectory : %@",libraryDirectory);
    NSString *filePath = [libraryDirectory stringByAppendingPathComponent:@"I8nDB"];
    filePath = [filePath stringByAppendingPathComponent:locale];
    NSLog(@"file path is : %@",filePath);
    BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
    if(fileExists)
    
        NSDictionary *dict = [[[NSDictionary alloc] initWithContentsOfFile:filePath]autorelease];
        NSDictionary *resourceBundle = [[[NSDictionary alloc] init]autorelease];
        NSString *keyValue = [[[NSString alloc]init]autorelease];
        resourceBundle = [dict valueForKey:@"hash"];
        keyValue=[resourceBundle valueForKey:i18nkey];
        NSLog(@"value for %@ is(container) : %@",i18nkey,keyValue);
        if(keyValue != nil || keyValue != NULL)
        
            return keyValue;
        
        else
        
            NSLog(@"key not found in the container file");
            NSString *path = [[NSBundle mainBundle] pathForResource:@"Localizable"
                                                             ofType:@"strings"
                                                        inDirectory:nil
                                                    forLocalization:locale];
            NSLog(@"path for %@ is : %@",locale,path);
            fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
            if(fileExists)
            
                NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
                NSLog(@"value for %@ is(resources) : %@",i18nkey,[dict objectForKey:i18nkey]);
                return [dict objectForKey:i18nkey];
            
            else
            
                return NULL;
            
        
    
    else
    
        NSLog(@"%@ locale does not exist in container",locale);
        NSString *path = [[NSBundle mainBundle] pathForResource:@"Localizable"
                                                         ofType:@"strings"
                                                    inDirectory:nil
                                                forLocalization:locale];
        NSLog(@"path for %@ in resources is : %@",locale,path);
        fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
        if(fileExists)
        
            NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
            NSLog(@"value for %@ is : %@",i18nkey,[dict objectForKey:i18nkey]);
            return [dict objectForKey:i18nkey];
        
        else
        
            return NULL;
        
    

如果我们从上面的代码中删除 Autorelease,如果不是应用程序崩溃,它可以在 ios7 中工作

我主要担心的是为什么它不会在 iOS8 和 9 中崩溃而只会在 iOS7 中崩溃 这些版本是否有与自动发布相关的变化

【问题讨论】:

问题代码可以在一个iOS版本中运行而不在其他版本中运行是正常的。因为 Apple 会在每个发行说明中更改系统。所以只有没有问题的代码才能在所有 iOS 中运行 【参考方案1】:

为什么不使用 ARC?那么你就不需要autorelease... 见http://rypress.com/tutorials/objective-c/memory-management

您的问题可能与ARC的设置有关。

【讨论】:

【参考方案2】:

在您的代码中,您只分配一个字典

NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];

所以你只需要关心它,另一个对象不属于你!所以你不需要释放或自动释放它们。

尝试流动代码

+ (NSString *)getValueforLocale:(NSString*) i18nkey :(NSString*)locale
    
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
        NSLog(@"paths are : %@",paths);
        NSString *libraryDirectory = [paths objectAtIndex:0];
        NSLog(@"libraryDirectory : %@",libraryDirectory);
        NSString *filePath = [libraryDirectory stringByAppendingPathComponent:@"I8nDB"];
        filePath = [filePath stringByAppendingPathComponent:locale];
        NSLog(@"file path is : %@",filePath);
        BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
        if(fileExists)
        
            NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
            //NSDictionary *resourceBundle = [[[NSDictionary alloc] init]autorelease];
            //NSString *keyValue = [[[NSString alloc]init]autorelease];
            NSDictionary *resourceBundle = [dict valueForKey:@"hash"];
            // relese dict here because not use after
            [dict release];
            NSString *keyValue=[resourceBundle valueForKey:i18nkey];
            NSLog(@"value for %@ is(container) : %@",i18nkey,keyValue);
            if(keyValue != nil || keyValue != NULL)
            
                return keyValue;
            
            else
            
                NSLog(@"key not found in the container file");
                NSString *path = [[NSBundle mainBundle] pathForResource:@"Localizable"
                                                                 ofType:@"strings"
                                                            inDirectory:nil
                                                        forLocalization:locale];
                NSLog(@"path for %@ is : %@",locale,path);
                fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
                if(fileExists)
                
                    // NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
                    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
                    NSLog(@"value for %@ is(resources) : %@",i18nkey,[dict objectForKey:i18nkey]);
                    return [dict objectForKey:i18nkey];
                
                else
                
                    return NULL;
                
            
        
        else
        
            NSLog(@"%@ locale does not exist in container",locale);
            NSString *path = [[NSBundle mainBundle] pathForResource:@"Localizable"
                                                             ofType:@"strings"
                                                        inDirectory:nil
                                                    forLocalization:locale];
            NSLog(@"path for %@ in resources is : %@",locale,path);
            fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
            if(fileExists)
            
                // NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
                NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
                NSLog(@"value for %@ is : %@",i18nkey,[dict objectForKey:i18nkey]);
                return [dict objectForKey:i18nkey];
            
            else
            
                return NULL;
            
        
    

【讨论】:

【参考方案3】:

在手动引用计数中,需要平衡保留和释放。

NSDictionary *dict = [[[NSDictionary alloc] initWithContentsOfFile:filePath]autorelease];
NSDictionary *resourceBundle = [[[NSDictionary alloc] init]autorelease];

保留和释放是平衡的,因为alloc(连同retainnewcopymutableCopy)返回一个保留实例,而autorelease 算作release

然而,在

NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];

你有一个过度释放,因为你是 autorelease 一些你没有保留的东西。

iOS版本与此完全无关。

【讨论】:

以上是关于由于 NSDictionary 自动释放,应用程序在 iOS7 中崩溃,但在 8 和 9 中没有的主要内容,如果未能解决你的问题,请参考以下文章

保留/自动释放已保留财产的额外好处是啥?

自动释放池导致 RubyCocoa 应用程序崩溃

NSCache

更改 NSDictionary 的 Xcode 自动格式

如何解决 NSDictionary 中的 NSInvalidArgumentException

从 NSArray 返回的自动释放对象?