NSMutableDictionary的深度可变副本

Z S*_*Z S 36 iphone objective-c

我正在尝试创建NSMutableDictionary的深层副本并将其分配给另一个NSMutableDictionary.字典包含一堆数组,每个数组包含名称,键是字母表(这些名称的第一个字母).因此字典中的一个条目是'A' - >'Adam','Apple'.这是我在书中看到的内容,但我不确定它是否有效:

- (NSMutableDictionary *) mutableDeepCopy
{
    NSMutableDictionary * ret = [[NSMutableDictionary alloc] initWithCapacity: [self count]];
    NSArray *keys = [self allKeys];

    for (id key in keys)
    {
        id oneValue = [self valueForKey:key]; // should return the array
        id oneCopy = nil;

        if ([oneValue respondsToSelector: @selector(mutableDeepCopy)])
        {
            oneCopy = [oneValue mutableDeepCopy];
        }
        if ([oneValue respondsToSelector:@selector(mutableCopy)])
        {
            oneCopy = [oneValue mutableCopy];
        }

        if (oneCopy == nil) // not sure if this is needed
        {   
            oneCopy = [oneValue copy];
        }
        [ret setValue:oneCopy forKey:key];

        //[oneCopy release];
    }
    return ret;
}
Run Code Online (Sandbox Code Playgroud)
  • 应该[onecopy release]是否存在?
  • 以下是我将如何调用此方法:

    self.namesForAlphabets = [self.allNames mutableDeepCopy];

那可以吗?还是会导致泄漏?(假设我将self.namesForAlphabets声明为属性,并在dealloc中释放它).

Wev*_*vah 75

由于免费桥接,您还可以使用CoreFoundation功能CFPropertyListCreateDeepCopy:

NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
Run Code Online (Sandbox Code Playgroud)

  • 这只适用于只有property-list-values的情况.如果它遇到任何其他东西它只返回NULL (13认同)
  • 我想你必须使用CFBridgeRelease.对于数组,mutableArray =(NSMutableArray*)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault,(CFArrayRef)oldArrat,kCFPropertyListMutableContainers)); (4认同)
  • 它遵循Core Foundation的"创建"规则,因此您需要确保释放或自动释放返回的字典(或者如果您想要保留它,则不要保留它). (2认同)

Tom*_*ing 11

假设阵列的所有元素都实现了NSCoding协议,您可以通过归档来执行深层复制,因为归档将保留对象的可变性.

像这样的东西:

id DeepCopyViaArchiving(id<NSCoding> anObject)
{
    NSData* archivedData = [NSKeyedArchiver archivedDataWithRootObject:anObject];
    return [[NSKeyedUnarchiver unarchiveObjectWithData:archivedData] retain];
}
Run Code Online (Sandbox Code Playgroud)

但这并不是特别有效.

  • 无论你投入什么.如果你输入一个NSMutableArray,你会得到一个NSMutableArray. (2认同)

e.J*_*mes 9

重要提示:问题(以及我的代码)都处理了一个非常具体的案例,其中NSMutableDictionary 包含字符串数组.这些解决方案不适用于更复杂的示例.有关更一般的案例解决方案,请参阅以下内容


这个具体案例的答案:

你的代码应该可行,但你肯定需要[oneCopy release].新词典将在您添加时保留复制的对象setValue:forKey,因此如果您不调用[oneCopy release],则所有这些对象将被保留两次.

一个好的经验法则:如果你alloc,retaincopy什么的,你也一定要release它.

注意:这里有一些示例代码适用于某些情况.这是有效的,因为您的NSMutableDictionary只包含字符串数组(不需要进一步深度复制):

- (NSMutableDictionary *)mutableDeepCopy
{
    NSMutableDictionary * ret = [[NSMutableDictionary alloc]
                                  initWithCapacity:[self count]];

    NSMutableArray * array;

    for (id key in [self allKeys])
    {
        array = [(NSArray *)[self objectForKey:key] mutableCopy];
        [ret setValue:array forKey:key];
        [array release];
    }

    return ret;
}
Run Code Online (Sandbox Code Playgroud)

  • 将'copy'替换为'mutableCopy'并且没关系. (3认同)

dre*_*lax 9

我见过的另一种技术(它根本不是非常有效)是使用一个NSPropertyListSerialization对象来序列化你的字典,然后你对它进行反序列化,但指定你想要可变的叶子和容器.


NSString *errorString = nil;
NSData *binData = 
  [NSPropertyListSerialization dataFromPropertyList:self.allNames
                                             format:NSPropertyListBinaryFormat_v1_0
                                        errorString:&errorString];

if (errorString) {
    // Something bad happened
    [errorString release];
}

self.namesForAlphabets = 
 [NSPropertyListSerialization propertyListFromData:binData
                                  mutabilityOption:NSPropertyListMutableContainersAndLeaves
                                            format:NULL
                                  errorDescription:&errorString];

if (errorString) {
    // something bad happened
    [errorString release];
}
Run Code Online (Sandbox Code Playgroud)

同样,这根本不是有效的.

  • 为什么这种方法效率低下? (2认同)

ier*_*ceg 5

尝试通过检查来找出答案respondToSelector(@selector(mutableCopy))不会给出所需的结果,因为所有NSObject基于 - 的对象都会响应此选择器(它是 的一部分NSObject)。相反,我们必须查询一个对象是否符合NSMutableCopying或至少符合NSCopying. 这是我根据已接受答案中提到的要点给出的答案:

为了NSDictionary

@implementation NSDictionary (MutableDeepCopy)

//  As seen here (in the comments): https://gist.github.com/yfujiki/1664847
- (NSMutableDictionary *)mutableDeepCopy
{
    NSMutableDictionary *returnDict = [[NSMutableDictionary alloc] initWithCapacity:self.count];

    NSArray *keys = [self allKeys];

    for(id key in keys) {
        id oneValue = [self objectForKey:key];
        id oneCopy = nil;

        if([oneValue respondsToSelector:@selector(mutableDeepCopy)]) {
            oneCopy = [oneValue mutableDeepCopy];
        } else if([oneValue conformsToProtocol:@protocol(NSMutableCopying)]) {
            oneCopy = [oneValue mutableCopy];
        } else if([oneValue conformsToProtocol:@protocol(NSCopying)]){
            oneCopy = [oneValue copy];
        } else {
            oneCopy = oneValue;
        }

        [returnDict setValue:oneCopy forKey:key];
    }

    return returnDict;
}

@end
Run Code Online (Sandbox Code Playgroud)

为了NSArray

@implementation NSArray (MutableDeepCopy)

- (NSMutableArray *)mutableDeepCopy
{
    NSMutableArray *returnArray = [[NSMutableArray alloc] initWithCapacity:self.count];

    for(id oneValue in self) {
        id oneCopy = nil;

        if([oneValue respondsToSelector:@selector(mutableDeepCopy)]) {
            oneCopy = [oneValue mutableDeepCopy];
        } else if([oneValue conformsToProtocol:@protocol(NSMutableCopying)]) {
            oneCopy = [oneValue mutableCopy];
        } else if([oneValue conformsToProtocol:@protocol(NSCopying)]){
            oneCopy = [oneValue copy];
        } else {
            oneCopy = oneValue;
        }

        [returnArray addObject:oneCopy];
    }

    return returnArray;
}

@end
Run Code Online (Sandbox Code Playgroud)

两种方法都具有相同的内部复制或不复制逻辑,并且可以将其提取到单独的方法中,但为了清楚起见,我将其保留为这样。