0

この Property List の値を変更したいprofileData.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>profiles</key>
    <array>
        <dict>
            <key>Name</key>
            <string>Default Profile</string>
            <key>size</key>
            <integer>0</integer>
        </dict>
    </array>
    <key>settings</key>
    <dict>
        <key>length</key>
        <string>cm</string>
    </dict>
</dict>
</plist>

sizeキーの整数を に設定したい1。私はこれを次のようにしました:

// reading Property List as described in Property List Programming Guide
NSError *error = nil;
NSPropertyListFormat format;
NSString *plistPath;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                     NSUserDomainMask, YES) objectAtIndex:0];
plistPath = [rootPath stringByAppendingPathComponent:@"profileData.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
    plistPath = [[NSBundle mainBundle] pathForResource:@"profileData" ofType:@"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSMutableDictionary *temp = (NSMutableDictionary *)[NSPropertyListSerialization
                                      propertyListWithData:plistXML
                                      options:NSPropertyListMutableContainersAndLeaves
                                      format:&format
                                      error:&error];
if (!temp) {
    DNSLog(@"Error reading plist: %@, format: %ld", error, (long)format);
}

// getting the right dictionary
NSMutableArray *profiles = [temp objectForKey:@"profiles"];
NSMutableDictionary profile0 = [profiles objectAtIndex:0];

// Setting new integer
[profile0 setObject:[NSNumber numberWithInt:1] forKey:@"size"];

// saving objects in reverse
[profiles replaceObjectAtIndex:0 withObject:profile0];
[temp setObject:profiles forKey:@"profiles"];

// writing property list
[temp writeToFile:plistPath atomically:YES];

このコードは機能します。

私の質問:これはこれを行うための最良の方法ですか? ディスクからのプロパティ リストの読み取りは問題ありません。しかし、プロパティ リストのすべての「レベル」を個別の変更可能なオブジェクトに保存し、その後、このすべてのオブジェクトをプロパティ リストの最高レベルまで逆に設定する必要がありますか?

より多くのレベルを持つプロパティ リストを想像しても、この方法でこれを行うのは少し複雑に思えます。この方法でそれを行うことが可能であれば、それは素晴らしいことです:

[temp setObject:[NSNumber numberWithInt:1] forKeyPath:@"profiles.0.size"];
4

1 に答える 1

0
/*The nature of using a plist or XML in XCode requires you to load the entire contents of the file into memory before doing anything with it (unless you're using a 3rd-party DOM parser for XML), just as you've done here. NSArray & NSDictionary then have the `writeToFile: atomically:` method you're already using to write changes back to disk. 

Of course you can access and change the temp NSMutableDictionary at whichever level you like, but if you want to save the changes then this is pretty much how you have to do it. 

As you imagined, this can be bad for larger/complex data or if you're saving changes all the time. 

I'd suggest you refactor this into multiple methods - right now you're loading the data, making the changes, AND writing back out to file all in the same place, which means you have to do all of that each time.

Break things up so you can do these tasks independently:

 - load plist from disk 
 - create temp copy of plist data as @property
 - make a change to tempdata @property (passing in whatever argument you need); 
 - write changes to disk

This would allow you to make multiple changes to the temp data as needed & only update the data model on disk when you have a bunch to do at once or need to ensure it's up to date (say, before the app goes into the background). This would cut the # of read/writes, but requires you to consider how fresh you need your data model to be & when's the best time to update the changes. 

If you're not stuck w/using plists, you could also look into using JSON, which is faster & lighter, and XCode now has pretty good native support, but writing to a file is the exact same process. For persisting larger, more complex data sets and making easier changes to individual items you might want to consider CoreData.   

..................................
1 other note ... In these lines: 

    NSMutableArray *profiles = [temp objectForKey:@"profiles"];
    NSMutableDictionary profile0 = [self.profiles objectAtIndex:0];

You're declaring `*profiles` within the method, not as a property of the object, so you shouldn't refer to it as `self.profiles`. Surprised this works w/o causing an error (unless you have both? In that case it won't work as expected). After refactoring to separate methods an @property is what you'll want.*/

以下のコメントに基づいて編集:

なるほど-その場合、上記の提案はかなり近いようですが、メソッド名はsetValue:(id) forKeyPath:(NSString *)、ではありませんsetObject...。また、NSNumber には Obj-C リテラル構文を使用できます。

多分次のようなものを試してください: [temp setValue:@1 forKeyPath:@"temp.profiles.0.size"];

temp(注: これはテストしていません。keyPath にフル パスが必要かどうかはわかりません)。Apple のKVC プログラミング ガイドKVC プロトコル リファレンスを参照してください。

このアプローチが速度やパフォーマンスに大きな影響を与えるとは思いませんが、テストしていません。また、これは、アクセスしているオブジェクトが特定のキーを持つ辞書であることも前提としています。構造またはキーが変更された場合、ここにはイントロスペクションまたはエラー処理はありません。最初のアプローチの利点は、アクセスできるオブジェクトの種類とメソッド/プロパティについて明示していることですが、これは一種の制限のないものです。

于 2014-07-17T16:51:29.690 に答える