有時候會想用 *.plist 來儲存資料,這時候若是從檔案內讀進資料建立 NSMutableDictionary 時,某個資料是 NSMutableString 時,想要對他 setString 時,就會出現 'Attempt to mutate immutable object with setString:' 的錯誤訊息,程式也就不正常結束了。

猜是可能是從檔案讀進來建的資料結構有問題,如只轉成 NSString 而已,雖然我有用 [obj isKindOfClass:[NSMutableString class] ] 來判斷出來是 NSMutableString 狀態,但在這個情境下還是會出錯。

測試程式如下:

UntitledAppDelegate.h

#import <UIKit/UIKit.h>
@interface UntitledAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end

UntitledAppDelegate.m

#import "UntitledAppDelegate.h"

@implementation UntitledAppDelegate
@synthesize window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0];
    NSString *db_path = [[NSString alloc] initWithString:[documentsPath stringByAppendingPathComponent:@"test.plist"]];

    NSMutableDictionary *db;
    if( [[NSFileManager defaultManager] fileExistsAtPath:db_path] )
       db = [[NSMutableDictionary alloc] initWithContentsOfFile:db_path];
    else
       db = [[NSMutableDictionary alloc] init];
    
    if( [db objectForKey:@"test"] == nil )
    {
        NSLog( @"Init test" );
        [db setValue:[[NSMutableString alloc] initWithString:@"1"] forKey:@"test"];
    }
    else if( [[db objectForKey:@"test"] isKindOfClass:[NSMutableString class] ] )
    {
        NSLog( @"set test from disk data" );
        [[db objectForKey:@"test"] setString:@"2"];
    }
    else
    {
        NSLog(@"ERROR");
    }
    if( [db objectForKey:@"test"] && [[db objectForKey:@"test"] isKindOfClass:[NSMutableString class] ] )
    {
        NSLog( @"set test from memory" );
        [[db objectForKey:@"test"] setString:@"2"];
    }

    
    [db writeToFile:db_path atomically:YES];
    
    // Override point for customization after application launch
    [window makeKeyAndVisible];
    return YES;
}

- (void)dealloc {
    [window release];
    [super dealloc];
}

@end

執行結果:

第一次:

[Session started at 2010-05-12 09:06:16 +0800.]
2010-05-12 09:06:17.170 Untitled[1017:207] Init test
2010-05-12 09:06:17.171 Untitled[1017:207] set test from memory

再跑第二次:

[Session started at 2010-05-12 09:06:22 +0800.]
2010-05-12 09:06:23.488 Untitled[1022:207] set test from disk data
2010-05-12 09:06:23.489 Untitled[1022:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with setString:'
2010-05-12 09:06:23.494 Untitled[1022:207] Stack: (
    29287515,
    2479863049,
    29371451,
    29371290,
    723866,
    9974,
    2721159,
    2759094,
    2747188,
    2729599,
    2756705,
    37383513,
    29072256,
    29068360,
    2723349,
    2760623,
    9252,
    9106
)

目前暫時的解法,只好把它刪除再重新設定了!

原本:

[[db objectForKey:@"test"] setString:@"2"];

更新:

[db removeObjectForKey:@"test"];
[db setValue:[[NSMutableString alloc] initWithString:@"2"] forKey:@"test"];


changyy 發表在 痞客邦 PIXNET 留言(0) 人氣()