2010年5月12日 星期三

[Objective C] NSMutableString in NSMutableDictionary - setString: 'Attempt to mutate immutable object with setString:'

有時候會想用 *.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"];


沒有留言:

張貼留言