Key/value observing not firing for property defined as: @property (readwrite, retain) __attribute__((NSObject)) CGImageRef thumbnailImage
I have a class with a property definition like so:
// Interface
@property (retain) __attribute__((NSObject)) CGImageRef thumbnailImage;
// Implementation
@synthesize thumbnailImage;
The problem is that self.thumbnailImage = newCGImageRef;
does no开发者_如何学编程t cause it to fire a key/value observation event and observeValueForKeyPath:...
is never called.
I tested the code with another property (BOOL) of the same class and it works just fine for that one. Shouldn't this also work for the property above?
Note: I tried setting the property manually ([self setValue:newValue forKey:@"thumbnailImage"]
, which resulted in the error below:
setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key thumbnailImage.
__attribute__((NSObject))
tells the compiler to treat structure pointers as an object therefore this allows you to retain/release the object (often used for blocks). This attribute has no effect on the fact that CFImageRef
is a struct pointer and KVC does not automatically wrap struct pointers though it will wrap structs. To solve your issue you will need to intercept KVC on undefined keys to set and return the correct object, or just use UIImage
instead.
KVC Documentation:
Automatic wrapping and unwrapping is not confined to NSPoint, NSRange, NSRect, and NSSize—structure types (that is, types whose Objective-C type encoding strings start with {) can be wrapped in an NSValue object.
TestCF_KVC.h
#import <Foundation/Foundation.h>
typedef struct
{
int x, y, z;
} AStruct;
@interface TestCF_KVC : NSObject {}
@property (nonatomic, retain) NSString *text;
@property (nonatomic, assign) AStruct aStruct;
@property (nonatomic, retain) __attribute__((NSObject)) CFStringRef cftext;
-(void)test;
@end
TestCF_KVC.m
#import "TestCF_KVC.h"
@implementation TestCF_KVC
@synthesize text, aStruct, cftext;
-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@"undefined key set for %@", key);
if([key isEqualToString:@"cftext"])
{
self.cftext = (CFStringRef)value;
}
else
{
[super setValue:value forUndefinedKey:key];
}
}
-(id)valueForUndefinedKey:(NSString *)key
{
NSLog(@"undefined key get for %@", key);
if([key isEqualToString:@"cftext"])
{
return (NSString*)self.cftext;
}
return [super valueForUndefinedKey:key];
}
-(void)test
{
NSString *txt = @"text worked";
AStruct astr = { .x=1, .y=5, .z=10 };
CFStringRef cftxt = (CFStringRef)@"cftext worked";
//Test a normal NSString for KVC
[self setValue:txt forKey:@"text"];
txt = [self valueForKey:@"text"];
NSLog(@"text[%s]: %@", @encode(NSString), [self valueForKey:@"text"]);
//Test a struct for KVC
NSValue *value = [NSValue value:&astr withObjCType:@encode(AStruct)];
[self setValue:value forKey:@"aStruct"];
[[self valueForKey:@"aStruct"] getValue:&astr];
NSLog(@"aStruct[%s]: %d %d %d", @encode(AStruct), aStruct.x, aStruct.y, aStruct.z);
//Test a Core Foundation type for KVC
[self setValue:(NSString*)cftxt forKey:@"cftext"];
cftxt = (CFStringRef)[self valueForKey:@"cftext"];
NSLog(@"cftext[%s]: %@", @encode(CFStringRef), (NSString*)cftxt);
}
@end
Log Output From Calling -test:
text[{NSString=#}]: text worked
aStruct[{?=iii}]: 1 5 10
undefined key set for cftext
undefined key get for cftext
cftext[^{__CFString=}]: cftext worked
精彩评论