NSDictionary的键值不等于set键

Can*_*ğlu 1 macos cocoa objective-c nsdictionary

我正在为OS X创建一个图像编辑应用程序.我有以下代码基本上返回与给定图像关联的窗口,如果没有为该图像创建窗口,则创建一个:

+(TNRWindow*)windowForImage:(NSImage*)img{
    static NSMutableDictionary *imageMapping;
    static int uidCounter;
    if(!imageMapping){
        imageMapping = [NSMutableDictionary dictionary];

    }
    TNRWindow *window = [imageMapping objectForKey:img];
    if(!window){
        window = [[TNRWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 720) styleMask:(NSResizableWindowMask|NSTitledWindowMask|NSClosableWindowMask) backing:NSBackingStoreBuffered defer:NO];
        [window center];
        window.uid = [NSNumber numberWithInt:uidCounter++];
        [imageMapping setObject:window forKey:img];
    }
    return window;

}
Run Code Online (Sandbox Code Playgroud)

我已经看到这个方法在连续调用时使用相同的NSImage实例返回一个新的,不同的窗口.我已经分析了代码,并意识到[imageMapping setObject:window forKey:img];没有设置正确的密钥.当我跨过该行时,它会创建一个键值对,但键与img对象不同.

这是img对象:

(lldb) po img
<NSImage 0x600000078fc0 Size={1311.5999999999999, 875} Reps=(
    "NSBitmapImageRep 0x6100000ba4c0 Size={1311.5999999999999, 875} 
     ColorSpace=sRGB IEC61966-2.1 colorspace BPS=16 BPP=48 
     Pixels=5465x3646 Alpha=NO Planar=NO Format=0 
     CurrentBacking=<CGImageRef: 0x6180003a31e0> CGImageSource=0x61000016c900"
)>
Run Code Online (Sandbox Code Playgroud)

这是我设置字典条目后的关键:

(lldb) po [[imageMapping keyEnumerator] allObjects]
<__NSArrayM 0x618000255930>(
<NSImage 0x610000279440 Size={1311.5999999999999, 875} Reps=(
    "NSBitmapImageRep 0x6180000baca0 Size={1311.5999999999999, 875} 
    ColorSpace=sRGB IEC61966-2.1 colorspace BPS=16 BPP=48 
    Pixels=5465x3646 Alpha=NO Planar=NO Format=0 
    CurrentBacking=<CGImageRef: 0x6180003a31e0> CGImageSource=0x61000016c900"
)>
)
Run Code Online (Sandbox Code Playgroud)

对象本身和NSBitmapImageReps是不同的,但背景CGImageRefCGImageSource是相同的.该对象似乎是完全有效的,但是当我调用[imageMapping objectForKey:img];它时,它返回我,nil因为图像对象本身不是字典中的键.没有多个线程调用此方法.这里到底发生了什么,我该如何纠正这种行为?

Tom*_*mmy 5

NSDictionary 复制密钥 - 在整个文档中经常重复以下文本:

复制每个密钥(使用copyWithZone:;密钥必须符合NSCopying协议),并将副本添加到新字典中.

随后通过值而不是身份来比较密钥.因此,不同的地址正是意味着要发生的事情.

我敢打赌,NSImage没有实现isEqual:以匹配副本.所以你需要使用非NSImage关键词.

如果您只想通过身份而不是按价值进行comapre,则可以使用NSValuevia +valueWithNonretainedObject:.

例如

....
    NSValue *key = [NSValue valueWithNonretainedObject:img];
    TNRWindow *window = [imageMapping objectForKey:key];
    if(!window){
        ...
        [imageMapping setObject:window forKey:key];
    }
Run Code Online (Sandbox Code Playgroud)

编辑:NSDictionary并且CFDictionary是免费的桥接,你可以为Core Foundation级别的几乎所有东西指定自定义函数,这将允许你创建一个保留密钥而不是复制的NSDictionary.这真的取决于你最开心的工作水平.