KVC updates

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@20828 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-Macdonald 2005-03-02 11:46:21 +00:00
parent c5b59c788c
commit 826de16ee7
6 changed files with 453 additions and 88 deletions

View file

@ -1,3 +1,15 @@
2005-03-02 Richard Frith-Macdonald <rfm@gnu.org>
* Headers/Foundation/NSKeyValueCoding.h:
* Source/NSArray.m:
* Source/NSKeyValueCoding.m:
* Source/Additions/GSObjCRuntime.m:
On the way back from FOSDEM I updated tje key-value-coding stuff to
match the lastest version from Apple/MacOS-X panther.
I don't use KVC in my code, and have not testcases for it, so would
appreciate it if anyone who does could try this out and get bug
reports back to me ASAP.
2005-03-01 Adam Fedor <fedor@gnu.org>
* Source/NSBundle.m (+gnustepBundle,

View file

@ -28,7 +28,9 @@
#include <Foundation/NSObject.h>
@class NSArray;
@class NSMutableArray;
@class NSDictionary;
@class NSError;
@class NSString;
#ifndef STRICT_OPENSTEP
@ -66,24 +68,92 @@ GS_EXPORT NSString* const NSUnknownKeyException;
+ (BOOL) useStoredAccessor;
/**
* Invoked when -valueForKey: / -storedValueForKey: are called with a key,
* which can't be associated with an accessor method or instance variable.
* Subclasses may override this method to add custom handling. NSObject
* raises an NSUnknownKeyException, with a userInfo dictionary containing
* NSTargetObjectUserInfoKey with the receiver an NSUnknownUserInfoKey with
* the supplied key entries.
* Returns a dictionary built from values obtained for the specified keys.<br />
* By default this is derived by calling -valueForKey: for each key.
* Any nil values obtained are represented by an [NSNull] instance.
*/
- (NSDictionary*) dictionaryWithValuesForKeys: (NSArray*)keys;
/**
* Deprecated ... use -valueForUndefinedKey: instead.
*/
- (id) handleQueryWithUnboundKey: (NSString*)aKey;
/**
* Invoked when -takeValue:forKey: / -takeStoredValue:forKey: are called with
* Deprecated, use -setValue:forUndefinedKey: instead.
*/
- (void) handleTakeValue: (id)anObject forUnboundKey: (NSString*)aKey;
/**
* <strong>Not implemented</strong> ... I don't know what this method
* is good for ... do we need to copy MacOS-X and implement it?
*/
- (NSMutableArray*) mutableArrayValueForKey: (NSString*)aKey;
/**
* <strong>Not implemented</strong> ... I don't know what this method
* is good for ... do we need to copy MacOS-X and implement it?
*/
- (NSMutableArray*) mutableArrayValueForKeyPath: (NSString*)aKey;
/**
* This method is invoked by the NSKeyValueCoding mechanism when an attempt
* is made to set an null value for a scalar attribute. This implementation
* raises an NSInvalidArgument exception. Subclasses my override this method
* to do custom handling. (E.g. setting the value to the equivalent of 0.)
*/
- (void) setNilValueForKey: (NSString*)aKey;
/**
* Sets the value if the attribute associated with the key in the receiver.
* The object is converted to a scalar attribute where applicable (and
* -setNilValueForKey: is caalled if a nil value is supplied).
* Tries to use a standard accessor of the form setKey: where 'Key' is the
* supplied argument with the first letter convertyed to uppercase.<br />
* If the receiver's class allows +accessInstanceVariablesDirectly
* it continues with instance variables:
* <list>
* <item>_key</item>
* <item>_isKey</item>
* <item>key</item>
* <item>isKey</item>
* </list>
* Invokes -setValue:forUndefinedKey: if no accessor mechanism can be found
* and raises NSInvalidArgumentException if the accesor method doesn't take
* exactly one argument or the type is unsupported (e.g. structs).
* If the receiver expects a scalar value and the value supplied
* is the NSNull instance or nil, this method invokes
* -setNilValueForKey: .
*/
- (void) setValue: (id)anObject forKey: (NSString*)aKey;
/**
* Retrieves the object returned by invoking -valueForKey:
* on the receiver with the first key component supplied by the key path.
* Then invokes -setValue:forKeyPath: recursively on the
* returned object with rest of the key path.
* The key components are delimated by '.'.
* If the key path doesn't contain any '.', this method simply
* invokes -setValue:forKey:.
*/
- (void) setValue: (id)anObject forKeyPath: (NSString*)aKey;
/**
* Invoked when -setValue:forKey: / -takeStoredValue:forKey: are called with
* a key which can't be associated with an accessor method or instance
* variable. Subclasses may override this method to add custom handling.
* NSObject raises an NSUnknownKeyException, with a userInfo dictionary
* containing NSTargetObjectUserInfoKey with the receiver an
* NSUnknownUserInfoKey with the supplied key entries.
* NSUnknownUserInfoKey with the supplied key entries.<br />
* Called when the key passed to -setValue:forKey: cannot be used.
*/
- (void) handleTakeValue: (id)anObject forUnboundKey: (NSString*)aKey;
- (void) setValue: (id)anObject forUndefinedKey: (NSString*)aKey;
/**
* Uses -setValue:forKey: to place the values from aDictionary in the
* receiver.
*/
- (void) setValuesForKeysWithDictionary: (NSDictionary*)aDictionary;
/**
* Returns the value associated with the supplied key as an object.
@ -108,7 +178,7 @@ GS_EXPORT NSString* const NSUnknownKeyException;
* <item>getKey</item>
* <item>key</item>
* </list>
* Invokes -handleQueryWithUnboundKey: if no accessor mechanism can be
* Invokes -handleTakeValue:forUnboundKey: if no accessor mechanism can be
* found and raises NSInvalidArgumentException if the accesor method takes
* takes any arguments or the type is unsupported (e.g. structs).
*/
@ -173,7 +243,8 @@ GS_EXPORT NSString* const NSUnknownKeyException;
* exactly one argument or the type is unsupported (e.g. structs).
* If the receiver expects a scalar value and the value supplied
* is the NSNull instance or nil, this method invokes
* -unableToSetNilForKey: .
* -unableToSetNilForKey: .<br />
* Deprecated ... use -setValue:forKey: instead.
*/
- (void) takeValue: (id)anObject forKey: (NSString*)aKey;
@ -184,44 +255,67 @@ GS_EXPORT NSString* const NSUnknownKeyException;
* returned object with rest of the key path.
* The key components are delimated by '.'.
* If the key path doesn't contain any '.', this method simply
* invokes -takeValue:forKey:.
* invokes -takeValue:forKey:.<br />
* Deprecated ... use -setValue:forKeyPath: instead.
*/
- (void) takeValue: (id)anObject forKeyPath: (NSString*)aKey;
/**
* Iterates over the dictionary invoking -takeValue:forKey:
* on the receiver for each key-value pair, converting NSNull to nil.
* on the receiver for each key-value pair, converting NSNull to nil.<br />
* Deprecated ... use -setValuesForKeysWithDictionary: instead.
*/
- (void) takeValuesFromDictionary: (NSDictionary*)aDictionary;
/**
* This method is invoked by the NSKeyValueCoding mechanism when an attempt
* is made to set an null value for a scalar attribute. This implementation
* raises an NSInvalidArgument exception. Subclasses my override this method
* to do custom handling. (E.g. setting the value to the equivalent of 0.)
* Deprecated ... use -setNilValueForKey: instead.
*/
- (void) unableToSetNilForKey: (NSString*)aKey;
/**
* Returns a boolean indicating whether the object pointed to by aValue
* is valid for settting as an attribute of the receiver using the name
* aKey. On success (YES response) it may return a new value to be used
* in aValue. On failure (NO response) it may return an error in anError.<br />
* The method works by calling a method of the receiver whose name is of
* the form validateKey:error: if the receiver has implemented such a
* method, otherwise it simply returns YES.
*/
- (BOOL) validateValue: (id*)aValue
forKey: (NSString*)aKey
error: (NSError**)anError;
/**
* Returns the result of calling -validateValue:forKey:error: on the receiver
* using aPath to determine the key value in the same manner as the
* -valueForKeyPath: method.
*/
- (BOOL) validateValue: (id*)aValue
forKeyPath: (NSString*)aKey
error: (NSError**)anError;
/**
* Returns the value associated with the supplied key as an object.
* Scalar attributes are converted to corresponding objects.
* The value-NSKeyValueCoding use the public accessors
* in favor of the private ones.
* Scalar attributes are converted to corresponding objects.<br />
* The search order is:<br/>
* Accessor methods:
* <list>
* <item>getKey</item>
* <item>key</item>
* </list>
* If the receiver's class allows +accessInstanceVariablesDirectly
* it continues with private accessors:
* <list>
* <item>_getKey</item>
* <item>_key</item>
* </list>
* If the receiver's class allows +accessInstanceVariablesDirectly
* it continues with instance variables:
* and then instance variables:
* <list>
* <item>key</item>
* <item>_key</item>
* </list>
* Invokes -handleQueryWithUnboundKey:
* Invokes -setValue:forUndefinedKey:
* if no accessor mechanism can be found
* and raises NSInvalidArgumentException if the accesor method takes
* any arguments or the type is unsupported (e.g. structs).
@ -238,6 +332,16 @@ GS_EXPORT NSString* const NSUnknownKeyException;
*/
- (id) valueForKeyPath: (NSString*)aKey;
/**
* Invoked when -valueForKey: / -storedValueForKey: are called with a key,
* which can't be associated with an accessor method or instance variable.
* Subclasses may override this method to add custom handling. NSObject
* raises an NSUnknownKeyException, with a userInfo dictionary containing
* NSTargetObjectUserInfoKey with the receiver an NSUnknownUserInfoKey with
* the supplied key entries.<br />
*/
- (id) valueForUndefinedKey: (NSString*)aKey;
/**
* Iterates over the array sending the receiver -valueForKey:
* for each object in the array and inserting the result in a dictionary.

View file

@ -1511,7 +1511,7 @@ GSObjCGetValue(NSObject *self, NSString *key, SEL sel,
}
if (type == NULL)
{
return [self handleQueryWithUnboundKey: key];
return [self valueForUndefinedKey: key];
}
else
{
@ -1834,11 +1834,11 @@ GSObjCSetValue(NSObject *self, NSString *key, id val, SEL sel,
}
if (type == NULL)
{
[self handleTakeValue: val forUnboundKey: key];
[self setValue: val forUndefinedKey: key];
}
else if ((val == nil || val == null) && *type != _C_ID && *type != _C_CLASS)
{
[self unableToSetNilForKey: key];
[self setNilValueForKey: key];
}
else
{

View file

@ -1251,7 +1251,7 @@ compare(id elem1, id elem2, void* context)
unsigned count = [self count];
volatile id object = nil;
results = [NSMutableArray array];
results = [NSMutableArray arrayWithCapacity: count];
for (i = 0; i < count; i++)
{

View file

@ -595,8 +595,8 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
path. (Using '/' here is safe; it isn't the path separator
everywhere, but it is on all systems that have PROCFS_EXE_LINK.)
*/
if ([_executable_path length] > 0 &&
[_executable_path characterAtIndex: 0] != '/')
if ([_executable_path length] > 0
&& [_executable_path characterAtIndex: 0] != '/')
{
_executable_path = nil;
}

View file

@ -53,6 +53,27 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
}
- (NSDictionary*) dictionaryWithValuesForKeys: (NSArray*)keys
{
NSMutableDictionary *dictionary;
NSEnumerator *enumerator;
id key;
dictionary = [NSMutableDictionary dictionaryWithCapacity: [keys count]];
enumerator = [keys objectEnumerator];
while ((key = [enumerator nextObject]) != nil)
{
id value = [self valueForKey: key];
if (value == nil)
{
value = [NSNull null];
}
[dictionary setObject: value forKey: key];
}
return dictionary;
}
- (id) handleQueryWithUnboundKey: (NSString*)aKey
{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
@ -64,6 +85,8 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
NSException *exp = [NSException exceptionWithName: NSUnknownKeyException
reason: @"Unable to find value for key"
userInfo: dict];
GSOnceMLog(@"This method is deprecated, use -valueForUndefinedKey:");
[exp raise];
return nil;
}
@ -80,10 +103,168 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
NSException *exp = [NSException exceptionWithName: NSUnknownKeyException
reason: @"Unable to set value for key"
userInfo: dict];
GSOnceMLog(@"This method is deprecated, use -setValue:forUndefinedKey:");
[exp raise];
}
- (NSMutableArray*) mutableArrayValueForKey: (NSString*)aKey
{
[self notImplemented: _cmd];
return nil;
}
- (NSMutableArray*) mutableArrayValueForKeyPath: (NSString*)aKey
{
[self notImplemented: _cmd];
return nil;
}
- (void) setNilValueForKey: (NSString*)aKey
{
/* Backward compatibility hack */
if ([self methodForSelector: @selector(unableToSetNilForKey:)]
== [NSObject instanceMethodForSelector: @selector(unableToSetNilForKey:)])
{
[self unableToSetNilForKey: aKey];
}
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%x: Given nil value to set for key \"%@\"",
NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, aKey];
}
- (void) setValue: (id)anObject forKey: (NSString*)aKey
{
SEL sel = 0;
const char *type = 0;
int off;
unsigned size = [aKey length];
if (size > 0)
{
const char *name;
char buf[size+6];
char lo;
char hi;
strcpy(buf, "_set");
[aKey getCString: &buf[4]];
lo = buf[4];
hi = islower(lo) ? toupper(lo) : lo;
buf[4] = hi;
buf[size+4] = ':';
buf[size+5] = '\0';
name = &buf[1]; // setKey:
type = NULL;
sel = GSSelectorFromName(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = buf; // _setKey:
sel = GSSelectorFromName(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
if ([[self class] accessInstanceVariablesDirectly] == YES)
{
buf[size+4] = '\0';
buf[3] = '_';
buf[4] = lo;
name = &buf[3]; // _key
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
buf[1] = '_';
name = &buf[1]; // _isKey
if (GSObjCFindVariable(self,
name, &type, &size, &off) == NO)
{
buf[4] = lo;
name = &buf[4]; // key
if (GSObjCFindVariable(self,
name, &type, &size, &off) == NO)
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
name = &buf[2]; // isKey
GSObjCFindVariable(self,
name, &type, &size, &off);
}
}
}
}
}
else
{
GSOnceMLog(@"Key-value access using _setKey: isdeprecated:");
}
}
}
GSObjCSetValue(self, aKey, anObject, sel, type, size, off);
}
- (void) setValue: (id)anObject forKeyPath: (NSString*)aKey
{
NSRange r = [aKey rangeOfString: @"."];
if (r.length == 0)
{
[self setValue: anObject forKey: aKey];
}
else
{
NSString *key = [aKey substringToIndex: r.location];
NSString *path = [aKey substringFromIndex: NSMaxRange(r)];
[[self valueForKey: key] setValue: anObject forKeyPath: path];
}
}
- (void) setValue: (id)anObject forUndefinedKey: (NSString*)aKey
{
NSDictionary *dict;
NSException *exp;
/* Backward compatibility hack */
if ([self methodForSelector: @selector(handleTakeValue:forUnboundKey:)]
== [NSObject instanceMethodForSelector:
@selector(handleTakeValue:forUnboundKey:)])
{
[self handleTakeValue: anObject forUnboundKey: aKey];
}
dict = [NSDictionary dictionaryWithObjectsAndKeys:
(anObject ? anObject : @"(nil)"),
@"NSTargetObjectUserInfoKey",
(aKey ? aKey : @"(nil)"),
@"NSUnknownUserInfoKey",
nil];
exp = [NSException exceptionWithName: NSInvalidArgumentException
reason: @"Unable to set nil value for key"
userInfo: dict];
[exp raise];
}
- (void) setValuesForKeysWithDictionary: (NSDictionary*)aDictionary
{
NSEnumerator *enumerator = [aDictionary keyEnumerator];
NSString *key;
while ((key = [enumerator nextObject]) != nil)
{
[self setValue: [aDictionary objectForKey: key] forKey: key];
}
}
- (id) storedValueForKey: (NSString*)aKey
{
unsigned size;
@ -94,17 +275,11 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
}
size = [aKey cStringLength];
if (size < 1)
{
[NSException raise: NSInvalidArgumentException
format: @"storedValueForKey: ... empty key"];
return NO; // avoid compiler warnings.
}
else
if (size > 0)
{
SEL sel = 0;
const char *type = NULL;
unsigned off;
int off;
const char *name;
char buf[size+5];
char lo;
@ -158,8 +333,13 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
}
}
}
return GSObjCGetValue(self, aKey, sel, type, size, off);
if (sel != 0 || type != NULL)
{
return GSObjCGetValue(self, aKey, sel, type, size, off);
}
}
[self handleTakeValue: nil forUnboundKey: aKey];
return nil;
}
@ -174,12 +354,7 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
}
size = [aKey length];
if (size < 1)
{
[NSException raise: NSInvalidArgumentException
format: @"takeStoredValue:forKey: ... empty key"];
}
else
if (size > 0)
{
SEL sel;
const char *type;
@ -228,8 +403,13 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
}
}
}
GSObjCSetValue(self, aKey, anObject, sel, type, size, off);
}
if (sel != 0 || type != NULL)
{
GSObjCSetValue(self, aKey, anObject, sel, type, size, off);
return;
}
}
[self handleTakeValue: anObject forUnboundKey: aKey];
}
@ -254,19 +434,14 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
- (void) takeValue: (id)anObject forKey: (NSString*)aKey
{
unsigned size;
SEL sel = 0;
const char *type = 0;
int off;
unsigned size = [aKey length];
size = [aKey length];
if (size < 1)
GSOnceMLog(@"This method is deprecated, use -setValue:forKey:");
if (size > 0)
{
[NSException raise: NSInvalidArgumentException
format: @"takeValue:forKey: ... empty key"];
}
else
{
SEL sel;
const char *type;
int off;
const char *name;
char buf[size+6];
char lo;
@ -304,8 +479,8 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
}
}
}
GSObjCSetValue(self, aKey, anObject, sel, type, size, off);
}
GSObjCSetValue(self, aKey, anObject, sel, type, size, off);
}
@ -313,6 +488,7 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
{
NSRange r = [aKey rangeOfString: @"."];
GSOnceMLog(@"This method is deprecated, use -setValue:forKeyPath:");
if (r.length == 0)
{
[self takeValue: anObject forKey: aKey];
@ -333,6 +509,7 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
NSNull *null = [NSNull null];
NSString *key;
GSOnceMLog(@"This method is deprecated, use -setValue:forKeyPath:");
while ((key = [enumerator nextObject]) != nil)
{
id obj = [aDictionary objectForKey: key];
@ -348,28 +525,69 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
- (void) unableToSetNilForKey: (NSString*)aKey
{
GSOnceMLog(@"This method is deprecated, use -setNilValueForKey:");
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%x: Given nil value to set for key \"%@\"",
NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, aKey];
}
- (id) valueForKey: (NSString*)aKey
- (BOOL) validateValue: (id*)aValue
forKey: (NSString*)aKey
error: (NSError**)anError
{
unsigned size;
NSString *name;
const char *str = [aKey cString];
SEL sel;
BOOL (*imp)(id,SEL,id*,id*);
size = [aKey length];
if (size < 1)
if (aValue == 0 || str == 0 || *str == '\0')
{
[NSException raise: NSInvalidArgumentException
format: @"valueForKey: ... empty key"];
return nil;
[NSException raise: NSInvalidArgumentException format: @"nil argument"];
}
name = [NSString stringWithFormat: @"validate%c%s:error:",
islower(*str) ? toupper(*str) : *str, str + 1];
sel = NSSelectorFromString(name);
if (sel != 0
&& (imp = (BOOL (*)(id,SEL,id*,id*))[self methodForSelector: sel]) != 0)
{
return (*imp)(self, sel, aValue, anError);
}
return YES;
}
- (BOOL) validateValue: (id*)aValue
forKeyPath: (NSString*)aKey
error: (NSError**)anError
{
NSRange r = [aKey rangeOfString: @"."];
BOOL result;
if (r.length == 0)
{
result = [self validateValue: aValue forKey: aKey error: anError];
}
else
{
SEL sel = 0;
const char *type = NULL;
unsigned off;
NSString *key = [aKey substringToIndex: r.location];
NSString *path = [aKey substringFromIndex: NSMaxRange(r)];
result = [[self valueForKey: key]
validateValue: aValue forKeyPath: path error: anError];
}
return result;
}
- (id) valueForKey: (NSString*)aKey
{
unsigned size;
SEL sel = 0;
int off;
const char *type = NULL;
size = [aKey length];
if (size > 0)
{
const char *name;
char buf[size+5];
char lo;
@ -390,36 +608,40 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
sel = GSSelectorFromName(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
buf[4] = hi;
name = buf; // _getKey
sel = GSSelectorFromName(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
buf[4] = lo;
buf[3] = '_';
name = &buf[3]; // _key
sel = GSSelectorFromName(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
sel = 0;
}
}
if (sel == 0 && [[self class] accessInstanceVariablesDirectly] == YES)
{
buf[4] = lo;
buf[3] = '_';
name = &buf[4]; // key
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
buf[4] = hi;
name = buf; // _getKey
sel = GSSelectorFromName(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
buf[4] = lo;
buf[3] = '_';
name = &buf[3]; // _key
GSObjCFindVariable(self, name, &type, &size, &off);
sel = GSSelectorFromName(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
if (sel == 0)
{
buf[4] = lo;
buf[3] = '_';
name = &buf[4]; // key
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
{
name = &buf[3]; // _key
GSObjCFindVariable(self, name, &type, &size, &off);
}
}
}
return GSObjCGetValue(self, aKey, sel, type, size, off);
}
return GSObjCGetValue(self, aKey, sel, type, size, off);
}
@ -443,6 +665,33 @@ NSString* const NSUnknownKeyException = @"NSUnknownKeyException";
}
- (id) valueForUndefinedKey: (NSString*)aKey
{
NSDictionary *dict;
NSException *exp;
/* Backward compatibility hack */
if ([self methodForSelector: @selector(handleQueryWithUnboundKey:)]
== [NSObject instanceMethodForSelector:
@selector(handleQueryWithUnboundKey:)])
{
return [self handleQueryWithUnboundKey: aKey];
}
dict = [NSDictionary dictionaryWithObjectsAndKeys:
self,
@"NSTargetObjectUserInfoKey",
(aKey ? aKey : @"(nil)"),
@"NSUnknownUserInfoKey",
nil];
exp = [NSException exceptionWithName: NSUnknownKeyException
reason: @"Unable to find value for key"
userInfo: dict];
[exp raise];
return nil;
}
- (NSDictionary*) valuesForKeys: (NSArray*)keys
{
NSMutableDictionary *dict;