objective-c NSMutableDictionary 的深度可变副本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1950361/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 22:39:47  来源:igfitidea点击:

deep mutable copy of a NSMutableDictionary

objective-ciphone

提问by Z S

I am trying to create a deep-copy of a NSMutableDictionary and assign it to another NSMutableDictionary. The dictionary contains a bunch of arrays, each array containing names, and the key is an alphabet (the first letter of those names). So one entry in the dictionary is 'A' -> 'Adam', 'Apple'. Here's what I saw in a book, but I'm not sure if it works:

我正在尝试创建 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;
}
  • should the [onecopy release] be there or not?
  • Here's how I'm going to call this method:

    self.namesForAlphabets = [self.allNames mutableDeepCopy];

  • [onecopy release] 是否应该在那里?
  • 下面是我将如何调用这个方法:

    self.namesForAlphabets = [self.allNames mutableDeepCopy];

Will that be ok? Or will it cause a leak? (assume that I declare self.namesForAlphabets as a property, and release it in dealloc).

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

采纳答案by e.James

IMPORTANT: The question (and my code below) both deal with a very specific case, in which the NSMutableDictionary contains onlyarrays of strings. These solutions will not workfor more complex examples. For more general case solutions, see the following:

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



Answer for this specific case:

针对此特定案例的答案:

Your code should work, but you will definitely need the [oneCopy release]. The new dictionary will retain the copied objects when you add them with setValue:forKey, so if you do not call [oneCopy release], all of those objects will be retained twice.

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

A good rule of thumb: if you alloc, retainor copysomething, you must also releaseit.

一个很好的经验法则:如果你allocretain或者copy什么,你也release必须。

Note: here is some sample code that would work for certain cases only. This works because your NSMutableDictionary contains only arrays of strings (no further deep copying required):

注意:这里是一些适用于某些情况的示例代码。这是有效的,因为您的 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;
}

回答by Wevah

Because of toll-free bridging, you can also use the CoreFoundation function CFPropertyListCreateDeepCopy:

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

NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);

回答by Tom Dalling

Assuming all elements of the array implement the NSCoding protocol, you can do deep copies via archiving because archiving will preserve the mutability of objects.

假设数组的所有元素都实现了 NSCoding 协议,您可以通过归档进行深度复制,因为归档将保留对象的可变性。

Something like this:

像这样的东西:

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

This isn't particularly efficient, though.

不过,这并不是特别有效。

回答by dreamlax

Another technique that I have seen (which is not at all very efficient) is to use an NSPropertyListSerializationobject to serialise your dictionary, then you de-serialise it but specify that you want mutable leaves and containers.

我见过的另一种技术(根本不是很有效)是使用一个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];
}

Again, this is not at all efficient.

同样,这根本没有效率。

回答by ierceg

Trying to figure out by checking respondToSelector(@selector(mutableCopy))won't give the desired results as all NSObject-based objects respond to this selector (it's part of NSObject). Instead we have to query if an object conforms to NSMutableCopyingor at least NSCopying. Here's my answer based on this gistmentioned in the accepted answer:

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

For NSDictionary:

对于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

For NSArray:

对于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

Both methods have the same internal to-copy-or-not-to-copy logic and that could be extracted into a separate method but I left it like this for clarity.

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

回答by johan

Thought I'd update with an answer if you're using ARC.

如果您使用的是 ARC,我想我会用答案更新。

The solution Weva has provided works just fine. Nowadays you could do it like this:

Weva 提供的解决方案工作得很好。现在你可以这样做:

NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDict, kCFPropertyListMutableContainers));

回答by Tom Andersen

For ARC - note kCFPropertyListMutableContainersAndLeaves for truly deep mutability.

对于 ARC - 注意 kCFPropertyListMutableContainersAndLeaves 以获得真正的深度可变性。

    NSMutableDictionary* mutableDict = (NSMutableDictionary *)
      CFBridgingRelease(
          CFPropertyListCreateDeepCopy(kCFAllocatorDefault, 
           (CFDictionaryRef)someNSDict, 
           kCFPropertyListMutableContainersAndLeaves));

回答by Benjohn

Useful answers here, but CFPropertyListCreateDeepCopydoesn't handle [NSNull null]in the data, which is pretty normal with JSON decoded data, for example.

有用的答案在这里,但CFPropertyListCreateDeepCopy不处理[NSNull null]数据,例如,对于 JSON 解码的数据,这很正常。

I'm using this category:

我正在使用这个类别:

    #import <Foundation/Foundation.h>

    @interface NSObject (ATMutableDeepCopy)
    - (id)mutableDeepCopy;
    @end

Implementation (feel free to alter / extend):

实现(随意更改/扩展):

    @implementation NSObject (ATMutableDeepCopy)

    - (id)mutableDeepCopy
    {
        return [self copy];
    }

    @end

    #pragma mark - NSDictionary

    @implementation NSDictionary (ATMutableDeepCopy)

    - (id)mutableDeepCopy
    {
        return [NSMutableDictionary dictionaryWithObjects:self.allValues.mutableDeepCopy
                                                  forKeys:self.allKeys.mutableDeepCopy];
    }

    @end

    #pragma mark - NSArray

    @implementation NSArray (ATMutableDeepCopy)

    - (id)mutableDeepCopy
    {
        NSMutableArray *const mutableDeepCopy = [NSMutableArray new];
        for (id object in self) {
            [mutableDeepCopy addObject:[object mutableDeepCopy]];
        }

        return mutableDeepCopy;
    }

    @end

    #pragma mark - NSNull

    @implementation NSNull (ATMutableDeepCopy)

    - (id)mutableDeepCopy
    {
        return self;
    }

    @end

Example extensions – strings are left as normal copies. You could override this if you want to be able to in place edit them. I only needed to monkey with a deep down dictionary for some testing, so I've not implemented that.

示例扩展 – 字符串保留为普通副本。如果您希望能够就地编辑它们,您可以覆盖它。我只需要使用深入的字典进行一些测试,所以我还没有实现。