NSLog(@"Person age = %@",[self.p valueForKey:@"age"]); NSLog(@"Person name = %@",[self.p valueForKey:@"name"]); }
打印结果:
1 2 3 4 5
2020-12-1918:52:54.058580+0800 Runtime-KVC[99046:16759483] Person age = 30 2020-12-1918:52:54.058700+0800 Runtime-KVC[99046:16759483] Person name = LeeWong 2020-12-1918:52:54.058784+0800 Runtime-KVC[99046:16759483] ----------------------------- 2020-12-1918:52:54.058882+0800 Runtime-KVC[99046:16759483] Person age = LeeWong 2020-12-1918:52:54.058990+0800 Runtime-KVC[99046:16759483] Person name = 30
NSLog(@"kvoSetMethodCallTest father name %@",self.father.name); }
我们调用kvoSetMethodCallTest方法后控制台输出结果如下:
1 2 3
2020-12-1920:53:05.265737+0800 Runtime-KVC[1701:16847901] Person setName LeeWong 2020-12-1920:53:05.265856+0800 Runtime-KVC[1701:16847901] Person getname LeeWong 2020-12-1920:53:05.265937+0800 Runtime-KVC[1701:16847901] kvoSetMethodCallTest father name LeeWong
/* Given a value and a key that identifies an attribute, set the value of the attribute. Given an object and a key that identifies a to-one relationship, relate the object to the receiver, unrelating the previously related object if there was one. Given a collection object and a key that identifies a to-many relationship, relate the objects contained in the collection to the receiver, unrelating previously related objects if there were any.
The default implementation of this method does the following: 1. Searches the class of the receiver for an accessor method whose name matches the pattern -set<Key>:. If such a method is found the type of its parameter is checked. If the parameter type is not an object pointer type but the value is nil -setNilValueForKey: is invoked. The default implementation of -setNilValueForKey: raises an NSInvalidArgumentException, but you can override it in your application. Otherwise, if the type of the method's parameter is an object pointer type the method is simply invoked with the value as the argument. If the type of the method's parameter is some other type the inverse of the NSNumber/NSValue conversion done by -valueForKey: is performed before the method is invoked. 2. Otherwise (no accessor method is found), if the receiver's class' +accessInstanceVariablesDirectly property returns YES, searches the class of the receiver for an instance variable whose name matches the pattern _<key>, _is<Key>, <key>, or is<Key>, in that order. If such an instance variable is found and its type is an object pointer type the value is retained and the result is set in the instance variable, after the instance variable's old value is first released. If the instance variable's type is some other type its value is set after the same sort of conversion from NSNumber or NSValue as in step 1. 3. Otherwise (no accessor method or instance variable is found), invokes -setValue:forUndefinedKey:. The default implementation of -setValue:forUndefinedKey: raises an NSUndefinedKeyException, but you can override it in your application.
Compatibility notes: - For backward binary compatibility with -takeValue:forKey:'s behavior, a method whose name matches the pattern -_set<Key>: is also recognized in step 1. KVC accessor methods whose names start with underscores were deprecated as of Mac OS 10.3 though. - For backward binary compatibility, -unableToSetNilForKey: will be invoked instead of -setNilValueForKey: in step 1, if the implementation of -unableToSetNilForKey: in the receiver's class is not NSObject's. - The behavior described in step 2 is different from -takeValue:forKey:'s, in which the instance variable search order is <key>, _<key>. - For backward binary compatibility with -takeValue:forKey:'s behavior, -handleTakeValue:forUnboundKey: will be invoked instead of -setValue:forUndefinedKey: in step 3, if the implementation of -handleTakeValue:forUnboundKey: in the receiver's class is not NSObject's. */ - (void)setValue:(nullableid)value forKey:(NSString *)key;
2020-12-1921:42:29.523439+0800 Runtime-KVC[2388:16881195] Person setName (null) 2020-12-1921:42:29.523581+0800 Runtime-KVC[2388:16881195] Father setNilValueForKey age
/* Given a key that identifies an attribute or to-one relationship, return the attribute value or the related object. Given a key that identifies a to-many relationship, return an immutable array or an immutable set that contains all of the related objects. The default implementation of this method does the following: 1.Searches the class of the receiver for an accessor method whose name matches the pattern -get<Key>, -<key>, or -is<Key>, in that order. If such a method is found it is invoked. If the type of the method's result is an object pointer type the result is simply returned. If the type of the result is one of the scalar types supported by NSNumber conversion is done and an NSNumber is returned. Otherwise, conversion is done and an NSValue is returned (new in Mac OS 10.5: results of arbitrary type are converted to NSValues, not just NSPoint, NRange, NSRect, and NSSize). 2 (introduced in Mac OS 10.7). Otherwise (no simple accessor method is found), searches the class of the receiver for methods whose names match the patterns -countOf<Key> and -indexIn<Key>OfObject: and -objectIn<Key>AtIndex: (corresponding to the primitive methods defined by the NSOrderedSet class) and also -<key>AtIndexes: (corresponding to -[NSOrderedSet objectsAtIndexes:]). If a count method and an indexOf method and at least one of the other two possible methods are found, a collection proxy object that responds to all NSOrderedSet methods is returned. Each NSOrderedSet message sent to the collection proxy object will result in some combination of -countOf<Key>, -indexIn<Key>OfObject:, -objectIn<Key>AtIndex:, and -<key>AtIndexes: messages being sent to the original receiver of -valueForKey:. If the class of the receiver also implements an optional method whose name matches the pattern -get<Key>:range: that method will be used when appropriate for best performance. 3 Otherwise (no simple accessor method or set of ordered set access methods is found), searches the class of the receiver for methods whose names match the patterns -countOf<Key> and -objectIn<Key>AtIndex: (corresponding to the primitive methods defined by the NSArray class) and (introduced in Mac OS 10.4) also -<key>AtIndexes: (corresponding to -[NSArray objectsAtIndexes:]). If a count method and at least one of the other two possible methods are found, a collection proxy object that responds to all NSArray methods is returned. Each NSArray message sent to the collection proxy object will result in some combination of -countOf<Key>, -objectIn<Key>AtIndex:, and -<key>AtIndexes: messages being sent to the original receiver of -valueForKey:. If the class of the receiver also implements an optional method whose name matches the pattern -get<Key>:range: that method will be used when appropriate for best performance. 4 (introduced in Mac OS 10.4). Otherwise (no simple accessor method or set of ordered set or array access methods is found), searches the class of the receiver for a threesome of methods whose names match the patterns -countOf<Key>, -enumeratorOf<Key>, and -memberOf<Key>: (corresponding to the primitive methods defined by the NSSet class). If all three such methods are found a collection proxy object that responds to all NSSet methods is returned. Each NSSet message sent to the collection proxy object will result in some combination of -countOf<Key>, -enumeratorOf<Key>, and -memberOf<Key>: messages being sent to the original receiver of -valueForKey:. 5 Otherwise (no simple accessor method or set of collection access methods is found), if the receiver's class' +accessInstanceVariablesDirectly property returns YES, searches the class of the receiver for an instance variable whose name matches the pattern _<key>, _is<Key>, <key>, or is<Key>, in that order. If such an instance variable is found, the value of the instance variable in the receiver is returned, with the same sort of conversion to NSNumber or NSValue as in step 1. 6 Otherwise (no simple accessor method, set of collection access methods, or instance variable is found), invokes -valueForUndefinedKey: and returns the result. The default implementation of -valueForUndefinedKey: raises an NSUndefinedKeyException, but you can override it in your application.
Compatibility notes: - For backward binary compatibility, an accessor method whose name matches the pattern -_get<Key>, or -_<key> is searched for between steps 1 and 3. If such a method is found it is invoked, with the same sort of conversion to NSNumber or NSValue as in step 1. KVC accessor methods whose names start with underscores were deprecated as of Mac OS 10.3 though. - The behavior described in step 5 is a change from Mac OS 10.2, in which the instance variable search order was <key>, _<key>. - For backward binary compatibility, -handleQueryWithUnboundKey: will be invoked instead of -valueForUndefinedKey: in step 6, if the implementation of -handleQueryWithUnboundKey: in the receiver's class is not NSObject's. */ - (nullableid)valueForKey:(NSString *)key;
这个方法的默认实现如下:
按照-get, -, -is的顺序搜索该类的获取方法,若找到,则直接调用。如果方法调用的结果是id类型,直接把结果返回。如果方法调用的结果是能够被NSNumber转换的标量类型,则结果会被转为NSNumber返回。否则对于一般的标量类型,这些类型将会被转化为NSValue(在Mac OS 10.5及以后,不仅仅支持NSPoint, NRange, NSRect,以及NSSize这些类型
ViewController.m - (void)valueForKeyTestStep1 { self.son = [Son new]; id result = [self.son valueForKey:@"school"]; NSLog(@"valueForKeyTestStep1 result %@ result class %@",result,[result class]); }
libc++abi.dylib: terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Son 0x600003707240> valueForUndefinedKey:]: this class is not key value coding-compliant for the key school.' terminating with uncaught exception of type NSException
- (void)valueForKeyTestStep2 { self.son = [Son new]; id result = [self.son valueForKey:@"school"]; NSLog(@"valueForKeyTestStep2 result %@ result class %@",result,[result class]); }
Son.m
// school key 对应的值有几个 - (NSInteger)countOfSchool { NSLog(@"countOfSchool"); return2; }
- (void)valueForKeyTestStep3 { self.son = [Son new]; id result = [self.son valueForKey:@"school"]; NSLog(@"valueForKeyTestStep3 result %@ result class %@",result,[result class]); }
- (void)valueForKeyTestStep4 { self.son = [Son new]; id result = [self.son valueForKey:@"school"]; NSLog(@"valueForKeyTestStep4 result %@ result class %@",result,[result class]); }
libc++abi.dylib: terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Son 0x60000298fdc0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key school.' terminating with uncaught exception of type NSException
- (void)valueForKeyTestStep5 { self.son = [Son new]; id result = [self.son valueForKey:@"school"]; NSLog(@"valueForKeyTestStep5 result %@ result class %@",result,[result class]); }
- (void)valueForKeyTestStep6 { self.son = [Son new]; id result = [self.son valueForKey:@"school"]; NSLog(@"valueForKeyTestStep6 result %@ result class %@",result,[result class]); }
2020-12-2621:35:16.596774+0800 Runtime-KVC[48788:19185406] valueForUndefinedKey school 2020-12-2621:35:20.486420+0800 Runtime-KVC[48788:19185406] valueForKeyTestStep6 result Beijing result class __NSCFConstantString
/* Given a key that identifies an _ordered_ to-many relationship, return a mutable array that provides read-write access to the related objects. Objects added to the mutable array will become related to the receiver, and objects removed from the mutable array will become unrelated.
The default implementation of this method recognizes the same simple accessor methods and array accessor methods as -valueForKey:'s, and follows the same direct instance variable access policies, but always returns a mutable collection proxy object instead of the immutable collection that -valueForKey: would return. It also:
1 Searches the class of the receiver for methods whose names match the patterns -insertObject:in<Key>AtIndex: and -removeObjectFrom<Key>AtIndex: (corresponding to the two most primitive methods defined by the NSMutableArray class), and (introduced in Mac OS 10.4) also -insert<Key>:atIndexes: and -remove<Key>AtIndexes: (corresponding to -[NSMutableArray insertObjects:atIndexes:] and -[NSMutableArray removeObjectsAtIndexes:). If at least one insertion method and at least one removal method are found each NSMutableArray message sent to the collection proxy object will result in some combination of -insertObject:in<Key>AtIndex:, -removeObjectFrom<Key>AtIndex:, -insert<Key>:atIndexes:, and -remove<Key>AtIndexes: messages being sent to the original receiver of -mutableArrayValueForKey:. If the class of the receiver also implements an optional method whose name matches the pattern -replaceObjectIn<Key>AtIndex:withObject: or (introduced in Mac OS 10.4) -replace<Key>AtIndexes:with<Key>: that method will be used when appropriate for best performance. 2 Otherwise (no set of array mutation methods is found), searches the class of the receiver for an accessor method whose name matches the pattern -set<Key>:. If such a method is found each NSMutableArray message sent to the collection proxy object will result in a -set<Key>: message being sent to the original receiver of -mutableArrayValueForKey:. 3 Otherwise (no set of array mutation methods or simple accessor method is found), if the receiver's class' +accessInstanceVariablesDirectly property returns YES, searches the class of the receiver for an instance variable whose name matches the pattern _<key> or <key>, in that order. If such an instance variable is found, each NSMutableArray message sent to the collection proxy object will be forwarded to the instance variable's value, which therefore must typically be an instance of NSMutableArray or a subclass of NSMutableArray. 4 Otherwise (no set of array mutation methods, simple accessor method, or instance variable is found), returns a mutable collection proxy object anyway. Each NSMutableArray message sent to the collection proxy object will result in a -setValue:forUndefinedKey: message being sent to the original receiver of -mutableArrayValueForKey:. The default implementation of -setValue:forUndefinedKey: raises an NSUndefinedKeyException, but you can override it in your application.
Performance note: the repetitive -set<Key>: messages implied by step 2's description are a potential performance problem. For better performance implement insertion and removal methods that fulfill the requirements for step 1 in your KVC-compliant class. For best performance implement a replacement method too. */ - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
2020-12-2623:36:46.702341+0800 Runtime-KVC[51145:19283102] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Father 0x6000004dd260> valueForUndefinedKey:]: this class is not key value coding-compliant for the key children.'
- (void)mutableArrayValueForKey { self.father = [Father new]; [self.father.children addObject:@"son1"]; NSLog(@"befor ---- children = %p children class %@",self.father.children,[self.father.children class]); [[self.father mutableArrayValueForKey:@"children"] addObject:@"farmer"]; NSLog(@"after ---- children = %p children class %@",self.father.children,[self.father.children class]); [[self.father mutableArrayValueForKey:@"children"] addObject:@"air"]; NSLog(@"after ---- children = %p children class %@",self.father.children,[self.father.children class]); }
控制台打印:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
2020-12-2700:11:15.866438+0800 Runtime-KVC[51719:19314394] befor ---- children = 0x600003330360 children class __NSArrayM 2020-12-2700:11:15.866768+0800 Runtime-KVC[51719:19314394] { indexes = "<_NSCachedIndexSet: 0x600003d57c40>[number of indexes: 1 (in 1 ranges), indexes: (1)]"; kind = 2; new = ( farmer ); } 2020-12-2700:11:15.866870+0800 Runtime-KVC[51719:19314394] after ---- children = 0x600003330900 children class __NSArrayM 2020-12-2700:11:15.867033+0800 Runtime-KVC[51719:19314394] { indexes = "<_NSCachedIndexSet: 0x600003d57c60>[number of indexes: 1 (in 1 ranges), indexes: (2)]"; kind = 2; new = ( air ); } 2020-12-2700:11:15.867115+0800 Runtime-KVC[51719:19314394] after ---- children = 0x6000033590b0 children class __NSArrayM
- (void)mutableArrayValueForKey { self.father = [Father new]; [self.father.children addObject:@"son1"]; NSLog(@"befor ---- children = %p children class %@",self.father.children,[self.father.children class]); [[self.father mutableArrayValueForKey:@"children"] addObject:@"farmer"]; NSLog(@"after ---- children = %p children class %@",self.father.children,[self.father.children class]); [[self.father mutableArrayValueForKey:@"children"] addObject:@"air"]; NSLog(@"after ---- children = %p children class %@",self.father.children,[self.father.children class]); }
控制台输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
2020-12-2700:30:06.297870+0800 Runtime-KVC[51979:19331763] befor ---- children = 0x6000008b9a40 children class __NSArrayM 2020-12-2700:30:06.298069+0800 Runtime-KVC[51979:19331763] insertObject: farmerinChildrenAtIndex: 1 2020-12-2700:30:06.298360+0800 Runtime-KVC[51979:19331763] { indexes = "<_NSCachedIndexSet: 0x6000006d02e0>[number of indexes: 1 (in 1 ranges), indexes: (1)]"; kind = 2; new = ( farmer ); } 2020-12-2700:30:06.298480+0800 Runtime-KVC[51979:19331763] after ---- children = 0x6000008b9a40 children class __NSArrayM 2020-12-2700:30:06.298595+0800 Runtime-KVC[51979:19331763] insertObject: airinChildrenAtIndex: 2 2020-12-2700:30:06.298754+0800 Runtime-KVC[51979:19331763] { indexes = "<_NSCachedIndexSet: 0x6000006d0300>[number of indexes: 1 (in 1 ranges), indexes: (2)]"; kind = 2; new = ( air ); } 2020-12-2700:30:06.298854+0800 Runtime-KVC[51979:19331763] after ---- children = 0x6000008b9a40 children class __NSArrayM