@@ -19,13 +19,14 @@ #include #import "OFObject.h" #import "OFObject+KeyValueCoding.h" +#import "OFArray.h" #import "OFMethodSignature.h" -#import "OFString.h" #import "OFNumber.h" +#import "OFString.h" #import "OFInvalidArgumentException.h" #import "OFOutOfMemoryException.h" #import "OFUndefinedKeyException.h" @@ -32,21 +33,24 @@ int _OFObject_KeyValueCoding_reference; @implementation OFObject (KeyValueCoding) - (id)valueForKey: (OFString *)key { + void *pool = objc_autoreleasePoolPush(); SEL selector = sel_registerName(key.UTF8String); OFMethodSignature *methodSignature = [self methodSignatureForSelector: selector]; id ret; if (methodSignature == nil) { size_t keyLength; char *name; - if ((keyLength = key.UTF8StringLength) < 1) + if ((keyLength = key.UTF8StringLength) < 1) { + objc_autoreleasePoolPop(pool); return [self valueForUndefinedKey: key]; + } if ((name = malloc(keyLength + 3)) == NULL) @throw [OFOutOfMemoryException exceptionWithRequestedSize: keyLength + 3]; @@ -62,24 +66,29 @@ free(name); } methodSignature = [self methodSignatureForSelector: selector]; - if (methodSignature == NULL) + if (methodSignature == NULL) { + objc_autoreleasePoolPop(pool); return [self valueForUndefinedKey: key]; + } switch (*methodSignature.methodReturnType) { case '@': case '#': + objc_autoreleasePoolPop(pool); return [self valueForUndefinedKey: key]; } } if (methodSignature.numberOfArguments != 2 || *[methodSignature argumentTypeAtIndex: 0] != '@' || - *[methodSignature argumentTypeAtIndex: 1] != ':') + *[methodSignature argumentTypeAtIndex: 1] != ':') { + objc_autoreleasePoolPop(pool); return [self valueForUndefinedKey: key]; + } switch (*methodSignature.methodReturnType) { case '@': case '#': ret = [self performSelector: selector]; @@ -105,14 +114,34 @@ CASE('Q', unsigned long long, numberWithUnsignedLongLong:) CASE('f', float, numberWithFloat:) CASE('d', double, numberWithDouble:) #undef CASE default: + objc_autoreleasePoolPop(pool); return [self valueForUndefinedKey: key]; } - return ret; + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (id)valueForKeyPath: (OFString *)keyPath +{ + void *pool = objc_autoreleasePoolPush(); + id ret = self; + + for (OFString *key in [keyPath componentsSeparatedByString: @"."]) + ret = [ret valueForKey: key]; + + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; } - (id)valueForUndefinedKey: (OFString *)key { @throw [OFUndefinedKeyException exceptionWithObject: self @@ -120,19 +149,21 @@ } - (void)setValue: (id)value forKey: (OFString *)key { + void *pool = objc_autoreleasePoolPush(); size_t keyLength; char *name; SEL selector; OFMethodSignature *methodSignature; const char *valueType; if ((keyLength = key.UTF8StringLength) < 1) { - [self setValue: value - forUndefinedKey: key]; + objc_autoreleasePoolPop(pool); + [self setValue: value + forUndefinedKey: key]; return; } if ((name = malloc(keyLength + 5)) == NULL) @throw [OFOutOfMemoryException @@ -155,18 +186,20 @@ if (methodSignature == nil || methodSignature.numberOfArguments != 3 || *methodSignature.methodReturnType != 'v' || *[methodSignature argumentTypeAtIndex: 0] != '@' || *[methodSignature argumentTypeAtIndex: 1] != ':') { - [self setValue: value - forUndefinedKey: key]; + objc_autoreleasePoolPop(pool); + [self setValue: value + forUndefinedKey: key]; return; } valueType = [methodSignature argumentTypeAtIndex: 2]; if (*valueType != '@' && *valueType != '#' && value == nil) { + objc_autoreleasePoolPop(pool); [self setNilValueForKey: key]; return; } switch (*valueType) { @@ -200,14 +233,37 @@ CASE('Q', unsigned long long, unsignedLongLongValue) CASE('f', float, floatValue) CASE('d', double, doubleValue) #undef CASE default: - [self setValue: value - forUndefinedKey: key]; + objc_autoreleasePoolPop(pool); + [self setValue: value + forUndefinedKey: key]; return; } + + objc_autoreleasePoolPop(pool); +} + +- (void)setValue: (id)value + forKeyPath: (OFString *)keyPath +{ + void *pool = objc_autoreleasePoolPush(); + OFArray *keys = [keyPath componentsSeparatedByString: @"."]; + size_t keysCount = keys.count; + id object = self; + size_t i = 0; + + for (OFString *key in keys) { + if (++i == keysCount) + [object setValue: value + forKey: key]; + else + object = [object valueForKey: key]; + } + + objc_autoreleasePoolPop(pool); } - (void)setValue: (id)value forUndefinedKey: (OFString *)key {