1

我正在为 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;

}

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"
)>

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

(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"
)>
)

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

4

2 回答 2

5

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

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

密钥随后按值进行比较,而不是按身份进行比较。所以不同的地址正是要发生的事情。

我敢打赌,这NSImage并没有实现isEqual:以匹配副本。所以你需要使用其他东西而不是NSImage你的钥匙。

如果您只想按身份而不是按值进行比较,则可以使用NSValuevia +valueWithNonretainedObject:

例如

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

编辑:NSDictionary并且CFDictionary是免费桥接的,您可以在核心基础级别为几乎所有事物指定自定义函数,这将允许您创建一个保留键而不是复制的 NSDictionary。这真的取决于你最快乐的工作水平。

于 2013-11-03T01:18:59.300 回答
2

您可以将窗口与NSImage直接使用关联objc_setAssociatedObject()

您可以使用 NSImage 上的类别实现 is,如下所示:

界面:

@interface NSImage (AssociatedWindow)
@property ( nonatomic, weak ) NSWindow * associatedWindow ; // change to strong if appropriate
@end

执行:

@implementation NSImage (AssociatedWindow)
@dynamic associatedWindow ;
-(void)setAssociatedWindow:(NSWindow*)window
{
    objc_setAssociatedObject( self, kAssociatedWindowKey, window, OBJC_ASSOCIATION_ASSIGN ) ; // assume you want a weak ref, otherwise use OBJC_ASSOCIATION_RETAIN_NONATOMIC
}

-(NSWindow*)associatedWindow
{
    return objc_getAssociatedObject( self, kAssociatedWindowKey ) ;
}

-(NSWindow*)ensureAssociatedWindow
{
    NSWindow * result = self.associatedWindow ;
    if ( !result )
    {
        NSWindow * newWindow = // create window
        self.associatedWindow = newWindow ;
        result = newWindow ;
    }
    return result ;
}    
@end

像这样使用:NSWindow * theWindow = [ theImage ensureAssociatedWindow ]

于 2013-11-03T01:15:05.030 回答