ValueForKey: and setValue:forKey: are used to using values forKey: and setValue:forKey:, but there are some details we haven’t gone into, so let’s take a look at KVC through official documentation. ⛽ ️ ⛽ ️
About Key-Value Coding
Key-value coding is a mechanism enabled by the NSKeyValueCoding informal protocol for objects to provide indirect access to their attributes/member variables. When an object conforms to a key-value encoding, its attributes/member variables can be addressed by string arguments via a concise, uniform messaging interface (setValue:forKey:). This indirect access mechanism complements the direct access provided by instance variables (automatically generated _ attribute names) and their associated accessor methods (getter methods).
Accessor methods are typically used to access properties of objects. The GET accessor (or getter) returns the value of the property. The set accessor (or setter) sets the value of the property. In Objective-C, you also have direct access to the underlying instance variables of a property (the instance variables generated by the compiler that correspond to the property with a concatenation of underscores and property names). Accessing object properties in either of these ways is simple, but requires calling property-specific methods or variable names. As the list of properties grows or changes, so must the code that accesses them. Instead, a key-value encoding compatible object provides a simple messaging interface that is consistent across all of its properties.
Key-value coding is the basis for many other Cocoa technologies, such as key-value observing, Cocoa Bindings, Core Data, and applescript-ability. In some cases, key-value coding can also help simplify code.
Key-value coding is a mechanism enabled by NSKeyValueCoding, which is an informal protocol. That is, it is different from common protocols such as NSCopying and NSCoding. It is directly defined by @protocol, and then <NSCopying, NSCoding> is added after the class declaration or class extension to indicate that the class complies with this protocol. The NSKeyValueCoding mechanism is realized by classification. Under the Foundation framework, there is an nsKeyValuecoding. h interface file, which internally defines multiple groups of classification interfaces, including: @interface NSObject(NSKeyValueCoding), @Interface NSArray(NSKeyValueCoding), @Interface NSDictionary<KeyType, The ObjectType > (NSKeyValueCoding), @ interface NSMutableDictionary < KeyType, ObjectType>(NSKeyValueCoding), @interface NSOrderedSet(NSKeyValueCoding), @Interface NSSet(NSKeyValueCoding), Where the NSObject base class already implements all the interfaces of NSKeyValueCoding, Subclasses NSArray, NSDictionary, NSMutableDictionary, NSOrderedSet, and NSSet override setValue:forKey: and valueForKey:. For example, when the setValue:forKey: function is called on an NSArray object, it internally calls the setValue:forKey: function on each element in the array. When valueForKey: is called on an NSArray object, it returns an array containing the result of calling valueForKey: on each element of the array. The returned array will contain the NSNull element, which refers to the case where valueForKey: returns nil for some element in the array.
NSKeyValueCoding Guide NSKeyValueCoding Guide NSKeyValueCoding Guide NSKeyValueCoding Guide
NSKeyValueCoding
NSKeyValueCoding is a mechanism for indirectly accessing object attributes/member variables by name or key.
The basic way to read and set the value of an object (attribute value/member variable value) is:
- SetValue :forKey: Sets the value of the property identified by the specified key.
- ValueForKey: It returns the value of the property identified by the specified key.
Therefore, all properties/member variables of an object can be read and set in a consistent manner.
The default implementation of accessing property values relies on accessor (getter) methods that are typically implemented by objects (or direct access to instance variables (_ property names) if needed).
valueForKey:
Returns the value of the property identified by the given key.
- (id)valueForKey:(NSString *)key;
Copy the code
Key is the name of one of the receiver properties. The return value is the value of the property identified by key. ValueForKey: a Search pattern used to find the correct Value to return is described in Accessor Search Patterns in key-value Coding Programming Guide.
valueForKeyPath:
Returns the value of the derived property identified by the given key path.
- (id)valueForKeyPath:(NSString *)keyPath;
Copy the code
KeyPath said the path of the property relationship (relationship between one or more), such as “department. The name” or “department. The manager. The lastName”. The return value is the value of the derived property of the keyPath identifier.
The default implementation uses valueForKey: to get the target object for each relationship and returns the result of the valueForKey: message to the final object.
If you’re still confused, let’s take a look at an example:
The Person class has an attribute named name
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
The Student class has an attribute named person
@interface Student : NSObject
@property (nonatomic, strong) Person *person;
@end
Person *person = [[Person alloc] init];
person.name = @"CHM";
Student *student = [[Student alloc] init];
student.person = person;
// Read the name of the person property under student using valueForKeyPath: @"person.name"
NSLog(@"📪 📪 % @", [student valueForKeyPath:@"person.name"]);
Copy the code
When we read the Person name property value via valueForKeyPath:, we read it via @”person.name”.
dictionaryWithValuesForKeys:
Returns a dictionary containing property values identified by each key in a given array.
- (NSDictionary<NSString *,id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
Copy the code
Keys contains an NSString array that identifies the receiver property. The return value is a dictionary containing the property names in keys as keys, and the corresponding values are the corresponding property values.
The default implementation is to call valueForKey: for each key in keys and replace the NSNull value in the dictionary with the returned nil value (that is, when a key returns nil, NSNull is replaced in the return value dictionary). Keys cannot contain keys of the form @”person.name”. If the receiver’s valueForUndefinedKey: Crash if not implemented)
valueForUndefinedKey:
Called by valueForKey: when no property corresponding to the given key can be found.
- (id)valueForUndefinedKey:(NSString *)key;
Copy the code
Key is a string that is not equal to the name of any of the Receiver properties.
Subclasses can override this method to return an alternate value for an undefined key. The default implementation raises NSUndefinedKeyException.
mutableArrayValueForKey:
Returns a mutable array proxy that provides read and write access to the ordered one-to-many relationship specified by the given key (that is, access to the array properties of the object through the key).
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
Copy the code
Key is the name of a one-to-many relationship. The return value is a mutable array proxy that provides read and write access to the ordered one-to-many relationship specified by key.
Objects added to the mutable array are relevant to the receiver, and objects removed from the mutable array are no longer relevant. The default implementation recognizes the same simple accessor methods and array accessor methods as valueForKey: and follows the same direct instance variable access strategy, but always returns a mutable collection proxy object rather than the immutable collection that valueForKey: will return.
The Accessor Search Patterns in key-value Coding Programming Guide describe mutableArrayValueForKey: the Search pattern used.
Add @property (nonatomic, strong) NSMutableArray *array; Property can be read in the following three ways:
NSLog(@"📢 📢 % @", [self.student mutableArrayValueForKey:@"array"]);
NSLog(@"📢 📢 % @", [self.student valueForKey:@"array"]);
NSLog(@"📢 📢 % @", [self.student valueForKeyPath:@"array"]);
Copy the code
When using the mutableArrayValueForKey: function to read non-array type properties such as @Property (nonatomic, strong) Person * Person; , using NSLog(@”📢📢 %@”, [self.student mutableArrayValueForKey:@”person”]); Read directly crash, the console output: Terminating app due to uncaught exception ‘NSInvalidArgumentException, reason:’ – [Person count] : Unrecognized selector sent to instance 0x600002b4D780 ‘.
And a series of: mutableArrayValueForKeyPath:/mutableSetValueForKey:/mutableSetValueForKeyPath:/mutableOrderedSetValueForKey:/mutableOrde RedSetValueForKeyPath: functions are similar to mutableArrayValueForKey: functions.
Here we take a closer look at the use of mutableArrayValueForKey:, which allows us to modify mutable/immutable collection types more quickly. @property (nonatomic, strong) NSArray
self.student = [[Student alloc] init];
Person *person2 = [[Person alloc] init];
NSMutableArray *arr = [@[person2, person2, person2] mutableCopy];
self.student.personArray = arr;
NSLog(@"🛂 print % @", [self.student mutableArrayValueForKey:@"personArray"]);
[[self.student mutableArrayValueForKey:@"personArray"] removeLastObject];
NSLog(@"🛂🛂 after removal %@", [self.student mutableArrayValueForKey:@"personArray"]);
[[self.student mutableArrayValueForKey:@"personArray"] addObject:person2];
NSLog(@"🛂🛂🛂 added after %@", [self.student mutableArrayValueForKey:@"personArray"]);
[((NSMutableArray *)[self.student valueForKey:@"personArray"]) removeLastObject];
NSLog(@"🛂🛂🛂🛂 valueForKey removed %@", self.student.personArray);
[((NSMutableArray *)[self.student valueForKey:@"personArray"]) addObject:person2];
NSLog(@"🛂🛂🛂🛂 port valueForKey added after %@", self.student.personArray);
// Console print:🛂 print ("<Person: 0x600001fdd0e0>"."<Person: 0x600001fdd0e0>"."<Person: 0x600001fdd0e0>") 🛂🛂"<Person: 0x600001fdd0e0>"."<Person: 0x600001fdd0e0>") 🛂🛂🛂 added ("<Person: 0x600001fdd0e0>"."<Person: 0x600001fdd0e0>"."<Person: 0x600001fdd0e0>"🛂🛂🛂🛂 valueForKey"<Person: 0x600001fdd0e0>"."<Person: 0x600001fdd0e0>"🛂🛂🛂🛂 port valueForKey"<Person: 0x600001fdd0e0>"."<Person: 0x600001fdd0e0>"."<Person: 0x600001fdd0e0>"
)
Copy the code
You can see that mutableArrayValueForKey: We can directly add and remove the contents of the personArray immutable array. If we use self.student. PersonArray, we read and assign back and forth.
setValue:forKey:
Sets the properties of the receiver specified by the given key to the given value.
- (void)setValue:(id)value forKey:(NSString *)key;
Copy the code
Key is the name of one of the receiver properties. Value is the value of an attribute identified by a key.
If key identifies a one-to-one relationship, the object specified by value is associated with the receiver and, if present, the previously related object is disassociated.
Given a collection object (value) and a key identifying the many-to-many relationship, the objects contained in the collection are associated with the receiver, and if there are previously associated objects, the association is cancelled.
The Search pattern used by setValue:forKey is described in Accessor Search Patterns in key-value Coding Programming Guide.
In an environment where reference counting is used, if instance variables are accessed directly, the values are retained.
setValue:forKeyPath:
Sets the value of the property identified by the given key path to the given value.
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
Copy the code
Value is the value of the keyPath attribute. KeyPath said the path of the property relationship (relationship between one or more), such as “department. The name” or “department. The manager. The lastName”. (with valueForKeyPath:)
The default implementation of this method uses valueForKey: to get the target object for each relationship and sends a setValue:forKey: message to the final object. (read the final object and then assign it a value)
If this method is used and the target object does not implement an accessor for the value, the default behavior is for the object to retain the value rather than copy or assign the value.
setNilValueForKey:
Called by setValue:forKey: when assigning a nil value to a scale value (such as int or float, when assigning a value to a property that is not an object type using setValue:forKey:).
- (void)setNilValueForKey:(NSString *)key;
Copy the code
Subclasses can override this method to handle the request in other ways, such as replacing nil with 0 or sentinel value and calling setValue:forKey: again, or setting the variable directly. The default implementation is NSInvalidArgumentException.
Here are properties for non-object types such as int/float. @ Property (nonatomic, assign) int mark; , then we use [self.student setValue:nil forKey:@”mark”]; We can override Student’s setNilValueForKey: function to handle this or prevent crashes. And if the property is an object type, value passes nil and it doesn’t crash.
setValuesForKeysWithDictionary:
Sets the attributes of the receiver using the value in the given dictionary, and identifies the attributes with the key corresponding to the value.
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *,id> *)keyedValues;
Copy the code
KeyedValues A dictionary whose key identifies attributes in the receiver. The property value in receiver is set to the value corresponding to the key in the dictionary.
The default implementation is to call setValue:forKey: for each key-value pair in keyedValues, replacing the NSNull value in keyedValues with nil.
setValue:forUndefinedKey:
Called by setValue:forKey: when it can’t find the property for the given key.
- (void)setValue:(id)value forUndefinedKey:(NSString *)key;
Copy the code
Key A string that is not equal to the name of any of the receiver properties.
Subclasses can override this method to handle requests in other ways. The default implementation raises NSUndefinedKeyException. (with valueForUndefinedKey:)
accessInstanceVariablesDirectly
(read-only) Returns a Boolean value indicating whether the key-value coding method should directly access the corresponding instance variable if the accessor method for the attribute is not found.
@property(class, readonly) BOOL accessInstanceVariablesDirectly;
Copy the code
YES if the key-value encoding method should access the corresponding instance variable directly if the accessor method for the property is not found, otherwise NO.
By default, YES is returned. A subclass can override this to return NO, in which case the key-value coding method will not be able to access the instance variable.
validateValue:forKey:error:
Returns a Boolean value indicating whether the value specified by the given pointer is valid for the property identified by the given key.
- (BOOL)validateValue:(inout id _Nullable *)ioValue forKey:(NSString *)inKey error:(out NSError * _Nullable *)outError;
Copy the code
IoValue A pointer to the new value of the property identified by inKey. This method can modify or replace the value to make it valid. Name of one of the inKey Receiver properties. InKey must specify an attribute or one-to-one relationship. OutError If validation is required and the ioValue is not converted to a valid value, the return contains an NSError object that describes why the ioValue is not valid.
If the ioValue points to a value that is valid for an attribute identified by inKey, or if the method can modify the ioValue to make it valid, then the Boolean value is set to YES; Otherwise, set the Boolean value to NO.
validateValue:forKeyPath:error:
Returns a Boolean value indicating whether the value specified by the given pointer is valid for the given key path relative to the receiver.
- (BOOL)validateValue:(inout id _Nullable *)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError * _Nullable *)outError;
Copy the code
The default implementation of this method using valueForKey: for each relationship of the target object, and returns to the property call validateValue: forKey: error: the results of the method.
NSKeyValueCoding NSKeyValueCoding NSKeyValueCoding NSKeyValueCoding NSKeyValueCoding NSKeyValueCoding NSKeyValueCoding NSKeyValueCoding
Compliant Objects Using key-value Coding
Objects usually also have key-value coding capability when they inherit NSObject (directly or indirectly), Because NSObject uses the NSKeyValueCoding protocol and provides a default implementation for the basic methods (it is actually the NSObject + NSKeyValueCoding classification that implements the basic methods used for key-value Coding by default). This class of objects allows other objects to perform the following operations through a compact messaging interface:
- Access object properties. The protocol specifies methods, such as the generic getter function: valueForKey: and the generic setter function: setValue:forKey:, to access object properties by name or key (parameterized as a string). The default implementations of these and related methods use keys to locate and interact with the underlying data, as described in Accessing Object Properties.
- Manipulate collection properties. The default implementation of the access method is used with the collection properties of the object, such as the NSArray object, just like any other property. In addition, if an object defines collection accessor methods for properties, it enables key-value access to collection contents. This is usually more efficient than direct access and allows you to work with custom Collection objects through standardized interfaces, as described in Accessing Collection Properties.
- Invokes the collection operator on the collection object. Collection Operators can be inserted into key strings when accessing Collection properties in key-value coding compatible objects, as described in Using Collection Operators. The collection operator instructs the default NSKeyValueCoding getter implementation to perform operations on the collection and then return either a new, filtered version of the collection or a single value (mean, sum, and so on) that represents some feature of the collection.
- Access non-object properties. The default implementation of the protocol detects non-object properties, including scalars, int/float, and structures, and automatically wraps and expands them as objects for use on the protocol interface, as described in Representing non-object Values. In addition, the protocol declares a method (setNilValueForKey 🙂 that allows compatible objects to provide appropriate operations when setting nil values for non-object properties through a key-value encoding interface.
- Properties are accessed through key paths. When you have a hierarchy of key-value coding-compatible objects, you can use keypath-based method calls to drill down into the hierarchy (up to the final destination) with a single call to get or set values. (i.e. all the way down @” XXX.xx.x “)
Adopting key-value Coding for an Object
To make your own objects conform to key-value coding requirements, make sure they use the NSKeyValueCoding informal protocol and implement methods such as valueForKey: as a generic getter and setValue:forKey: as a generic setter. Fortunately, as mentioned above, NSObject adopts this protocol and provides a default implementation for these and other necessary methods. So if you derive objects from NSObject (or any of its many subclasses), a lot of work is already done for you. (Implementation interfaces are in the NSObject + NSKeyValueCoding category)
In order for the default methods to do their job, you need to ensure that the object’s accessor methods and instance variables follow some well-defined (unambiguous) pattern. This allows the default implementation to look up properties of an object based on key-value encoded messages. You can then choose to extend and customize the key-value encoding by providing validation methods and ways to handle certain special cases
Key-Value Coding with Swift
By default, properties of Swift objects inherited from NSObject or its subclasses conform to key-value encoding. In Objective-C, property accessors and instance variables must follow a specific pattern, which is automatically guaranteed by standard property declarations in Swift. On the other hand, many features of the protocol are either irrelevant or are better handled using native Swift constructs or techniques that do not exist in Objective-C. For example, since all Swift attributes are objects, you would never use the default implementation for special handling of non-object attributes.
Thus, this guide focuses on Objective-C when the key-value encoding protocol methods are converted directly to Swift, where you have more work to do to ensure compliance and key-value encoding is often the most useful. Situations that require significantly different approaches in Swift are explained throughout the guide.
For more information about Using Swift with Cocoa technology, read Using Swift with Cocoa and Objective-C (Swift 3). For a complete description of Swift, read The Swift Programming Language (Swift 3).
Other Cocoa Technologies Rely on key-value Coding (Cocoa Technologies Rely on key-value Coding)
Objects that conform to key-value encoding can participate in a variety of Cocoa techniques that depend on this access, including:
- Key-value observing. This mechanism enables an object to register asynchronous notifications driven by property changes of another object, as described in the Key-value Observing Programming Guide.
- Cocoa Bindings. This family of technologies fully implements the Model-View-Controller paradigm, where the Model encapsulates application data, the View displays and edits that data, and the Controller mediates between the two. Read Cocoa Bindings Programming Topics to learn more about Cocoa Bindings.
- Core Data.
- AppleScript. This scripting language provides direct control over scriptable applications and many parts of macOS. Cocoa scripting supports key-value encoding to get and set information in scriptable objects. Methods in the NSScriptKeyValueCoding informal protocol provide additional capabilities for handling key-value encoding, including fetching and setting key values in multi-valued keys by index, and forcing (or converting) key values to appropriate data types. AppleScript Overview provides a high-level Overview of AppleScript and its related technologies.
The basic function of the Accessing Object Properties
Objects (class definitions) typically specify properties in their interface declarations that fall into one of the following categories:
- Attributes. These are simple values, such as scalars (int/float, etc.), strings, or Booleans. Value objects (such as NSNumber) and other immutable types (such as NSColor) are also considered attributes.
- To-one relationships. They are mutable objects that have properties of their own. The properties of an object can change, but the object itself does not. For example, a bank account object might have an owner property that is an instance of the Person object, which itself has an Address property. The owner’s address can be changed without having to change the Owner reference held by the bank account. The owner of the bank account has not changed, only their address has changed.
- To-many relationships. These are collection objects. Although custom collection classes can also be used, instances of NSArray or NSSet are typically used to hold such collections.
The BankAccount object (class) declared in Listing 2-1 illustrates the properties of each type.
Listing 2-1Properties of the BankAccount object Listing 2-1Properties of the BankAccount object
@interface BankAccount : NSObject
@property (nonatomic) NSNumber* currentBalance; // An attribute
@property (nonatomic) Person* owner; // A to-one relation
@property (nonatomic) NSArray< Transaction* >* transactions; // A to-many relation
@end
Copy the code
To maintain encapsulation, objects typically provide accessor methods for properties on their interfaces. Object authors can either write these methods explicitly or rely on the compiler to synthesize them automatically. Either way, the author of code that uses one of these accessors must write the property name into the code before compiling it. The name of the accessor method becomes a static part of the code that uses it. For example, given the bank Account object declared in Listing 2-1, the compiler synthesizes a setter that you can call for the myAccount instance:
[myAccount setCurrentBalance:@(100.0)];
Copy the code
This is straightforward, but inflexible. Key-value encoding-compatible objects, on the other hand, provide a more general mechanism for accessing objects’ properties using string identifiers.
Identifying an Object’s Properties with Keys and Key Paths
A key is a string that identifies a particular property. Typically, by convention, the key that represents the property is the name of the property itself displayed in the code. Keys must be ASCII encoded, cannot contain Spaces, and usually begin with a lowercase letter (although there are exceptions, such as the URL attribute found in many classes).
Because the BankAccount class in Listing 2-1 is key-value coded, it recognizes keys such as Owner, currentBalance, and Transactions as the names of its properties. Instead of calling setCurrentBalance:, you can set the value by its key:
[myAccount setValue:@(100.0) forKey:@"currentBalance"];
Copy the code
In fact, you can set all the properties of the myAccount object in the same way, using different key arguments. Because the parameter is a string, it can be a variable to operate on at run time.
The key path is a set of points (.) A separator key that specifies the sequence of object properties to iterate over. The property of the first key in the sequence is evaluated relative to the receiver, and each subsequent key is evaluated relative to the value of the previous property. Critical paths are useful for drilling down into the hierarchy of objects through a single method call.
For example, assuming that the Person and Address classes also conform to key-value encodings, the key path owner.address.street applied to the bank account instance refers to the value of the street string stored in the bank account owner’s Address.
NOTE: In Swift, you can use the #keyPath expression instead of using strings to indicate keys or key paths. This provides the advantage of compile-time checking, as described in the “Keys and Key paths” section of the Using Swift with Cocoa and Objective-C (Swift 3) Guide.
Getting Attribute Values Using Keys
When an object uses the NSKeyValueCoding protocol, it conforms to the key-value encoding requirements. An object inherited from NSObject provides a default implementation of the protocol base method, which automatically adopts a protocol with some default behavior (methods implemented in the NSObject + NSKeyValueCoding classification). Such objects implement at least the following basic key-based getters:
valueForKey:
– Returns the value of the property named by the key parameter. If an attribute named by a key is not found according to the rules described in Accessor Search Patterns, the object sends one to itselfvalueForUndefinedKey:
The message.valueForUndefinedKey:
NSUndefinedKeyException is raised by the default implementation of NSUndefinedKeyException, but subclasses can override this behavior and handle the situation more gracefully.valueForKeyPath:
– Returns the value of the specified key path relative to receiver. Any object in a key-path sequence that is incompatible with key-value encoding for a particular key, i.evalueForKey:
The default implementation of the accessor method cannot find the accessor methodvalueForUndefinedKey:
The message.dictionaryWithValuesForKeys:
– Returns the value of the key array relative to receiver. This method is called for each key in the arrayvalueForKey:
. The returned NSDictionary contains the values of all the keys in the array.
NOTE: Collection objects such as NSArray, NSSet, and NSDictionary cannot contain nil as a value. Instead, you can use NSNull objects to represent nil values. NSNull provides a single instance of a nil value representing an object property. DictionaryWithValuesForKeys: and related setValuesForKeysWithDictionary: the default implementation of automatically in the NSNull () in the dictionary parameters and nil () in the storage properties of conversion between.
When using the key path addressing property, if any key in the key path other than the last key is a one-to-many relationship (that is, it refers to a collection), the value returned is a collection containing all the values of the keys to the right of a pair of multiple keys. For example, the transactions.payee request for the key path returns an array containing all payee objects for all transactions. This also applies to multiple arrays in key paths. Key path accounts. The transactions. Payee, returns an array containing all the accounts of all the transactions of all payee object. (for example in the above example we add @property (nonatomic, strong) NSArray
Setting Attribute Values Using Keys
Like getters, key-value coding-compatible objects also provide a small set of generic setters, These setters are based on an implementation of the NSKeyValueCoding protocol in NSObject (methods implemented in NSObject + NSKeyValueCoding classification) and have default behavior:
setValue:forKey:
– Sets the value of the specified key relative to the object receiving the message to the given value.setValue:forKey:
The default implementation of “automatically unpacks NSNumber and NSValue objects representing scalars (int/float, etc.) and structures (structs) and assigns their values to attributes. For details on packaging and expansion semantics, see Representing non-object Values. For example, our Student class above has a property of type int@property (nonatomic, assign) int mark;
, so when we use setValue:forKey: we need to package value as an NSNumber because value only accepts id, such as:[self.student setValue:@(99) forKey:@"mark"];
, so we can assign the mark attribute directly, key-value coding is done automatically. If the specified key corresponds to a property that the object receiving the setter message does not have, the object sends one to itselfsetValue:forUndefinedKey:
Messages andsetValue:forUndefinedKey:
The default implementation of “is to raise NSUndefinedKeyException. However, subclasses can override this method to handle requests in a custom way to prevent crashes.setValue:forKeyPath:
– Sets the given value at the specified key path relative to the receiver. Any object in the key-path sequence that does not match the key-value encoding for a particular key is receivedsetValue:forUndefinedKey:
The message.setValuesForKeysWithDictionary:
– Sets the properties of receiver with values in the specified dictionary, identifying the properties with dictionary keys. Default implementation callsetValue:forKey:
For each key-value pair, replace the NSNull object with nil as needed.
In the default implementation, key-value codec compatible objects send themselves setNilValueForKey: messages when you try to set non-object properties to nil values. NSInvalidArgumentException setNilValueForKey: the default implementation, but the object may rewrite setNilValueForKey: Replace the default or marked value, as described in Handling Non-object Values.
Using Keys to Simplify Object Access
To see how key-based getters and setters simplify code, refer to the following example. In macOS, NSTableView and NSOutlineView objects associate identifier strings with each of their columns. If the Model object supporting the table does not match the key-value encoding, the data source methods of the table are forced to examine each column identifier in turn to find the correct property to return, as shown in Listing 2-2. In addition, when you add another property (in this case, the Person object) to the Model in the future, you must revisit the data source method to add another condition to test the new property and return the associated value.
Listing 2-2 Implementation of data source method without key-value coding Listing 2-2 Implementation of data source method without key-value coding Listing 2-2 Implementation of data source method without key-value coding
- (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row {
id result = nil;
Person *person = [self.people objectAtIndex:row];
if ([[column identifier] isEqualToString:@"name"]) {
result = [person name];
} else if ([[column identifier] isEqualToString:@"age"]) {
result = @([person age]); // Wrap age, a scalar, as an NSNumber
} else if ([[column identifier] isEqualToString:@"favoriteColor"]) {
result = [person favoriteColor];
} // And so on...
return result;
}
Copy the code
On the other hand, Listing 2-3 shows a more compact implementation of the same data source method that leverages a key-value encoding compatible Person object. Using only valueForKey: getter, the data source method returns the appropriate value using the column identifier as the key. In addition to being shorter, it is also more general because it can continue to work unchanged when new columns are added later, as long as the column identifier always matches the attribute name of the model object.
Listing 2-3 Implementation of data source method with key-value coding Listing 2-3 Implementation of data source method with key-value coding
- (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row {
return [[self.people objectAtIndex:row] valueForKey:[column identifier]];
}
Copy the code
The basic function of the Accessing Collection Properties
An object compatible with key-value encoding exposes its multiple attributes in the same way that other attributes are exposed. You can get or set collection objects just like you would any other object using valueForKey: and setValue:forKey: (or their equivalent key paths). However, when you want to manipulate the contents of these collections, it is usually most effective to use the protocol-defined variable proxy method.
The protocol defines three different proxy methods for accessing collection objects, each with a key and a key path (keyPath) variant:
mutableArrayValueForKey:
和mutableArrayValueForKeyPath:
They return proxy objects that behave like NSMutableArray objects.mutableSetValueForKey:
和mutableSetValueForKeyPath:
They return proxy objects that behave like NSMutableSet objects.mutableOrderedSetValueForKey:
和mutableOrderedSetValueForKeyPath:
They return proxy objects that behave like NSMutableOrderedSet objects.
When you operate on, add, remove, or replace a proxy object, the default implementation of the protocol modifies the underlying properties accordingly. This is much more convenient and flexible than using valueForKey: to get an immutable collection object, creating a modified collection object with changed contents, and then using setValue:forKey: to assign values to specified properties. In many cases, these methods provide the added benefit of maintaining a see key-value Observing Programming Guide for Details for the objects saved in the collection object.
Using Collection Operators
When you send a valueForKeyPath: message to a key-value encoding compatible object, you can embed a collection operator in the key-value path. The collection operator is one of a small string of keywords preceded by the AT symbol (@) that specifies what the getter should do to process the data in some way before returning it. NSObject provides valueForKeyPath: implements this behavior by default.
When a key path contains a collection operator, any portion of the key path preceding that operator (called the left key path) represents the collection to operate on relative to the receiver of the message. If a message is sent directly to a collection object, such as an NSArray instance, the left key path may be ignored. The key path section (called the right Key path) following the operator specifies the attributes of the elements in the collection that the operator should process. All set operators except @count require the correct key path. Figure 4-1 shows the format of operator key path. (The following code examples will help us understand what these three parts mean.)
Figure 4-1 Operator key path format
Set operators exhibit three basic types of behavior:
- Aggregation Operators merge the objects of the collection in some way and return a single object that usually matches the data type of the property named in the correct key path. One exception is the @count operator, which does not use any right-click paths and always returns an NSNumber instance.
- Array Operators returns an instance of NSArray that contains some subset of the objects held in the named collection.
- Nesting Operators process sets that contain other sets and, depending on the Operators, return NSArray or NSSet instances that combine nested set objects in a certain way.
Sample Data
The following descriptions include code snippets that demonstrate how to invoke each operator and the results of doing so. These rely on the BankAccount class shown in Listing 2-1, which contains an array of Transaction objects. Each represents a simple CheckBook entry, as Listing 4-1 shows. (Here are two simple class definitions.)
Listing 2-1Properties of the BankAccount object Listing 2-1Properties of the BankAccount object
@interface BankAccount : NSObject
@property (nonatomic) NSNumber* currentBalance; // An attribute
@property (nonatomic) Person* owner; // A to-one relation
@property (nonatomic) NSArray< Transaction* >* transactions; // A to-many relation
@end
Copy the code
Listing 4-1 Interface declaration for the Transaction object Listing 4-1 Interface declaration for the Transaction object
@interface Transaction : NSObject
@property (nonatomic) NSString* payee; // To whom
@property (nonatomic) NSNumber* amount; // How much
@property (nonatomic) NSDate* date; // When
@end
Copy the code
For the sake of discussion, suppose your BankAccount instance has a Transactions array populated with the data shown in Listing 4-1, and you make sample calls from inside the BankAccount object. (Self represents a BankAccount object.)
Table 4-1 Example data for the Transactions Objects
payee values | amount values formatted as currency | date values formatted as month day, year |
---|---|---|
Green Power | The $120.00 | Dec 1, 2015 |
Green Power | The $150.00 | Jan 1, 2016 |
Green Power | The $170.00 | Feb 1, 2016 |
Car Loan | The $250.00 | Jan 15, 2016 |
Car Loan | The $250.00 | Feb 15, 2016 |
Car Loan | The $250.00 | Mar 15, 2016 |
General Cable | The $120.00 | Dec 1, 2015 |
General Cable | The $155.00 | Jan 1, 2016 |
General Cable | The $120.00 | Feb 1, 2016 |
Mortgage | $1250.00 | Jan 15, 2016 |
Mortgage | $1250.00 | Feb 15, 2016 |
Mortgage | $1250.00 | Mar 15, 2016 |
Animal Hospital | The $600.00 | Jul 15, 2016 |
Aggregation Operators
Aggregate operators process an array or set of attributes to generate a single value that reflects some aspect of the collection.
@avg (Average)
When specifying the @avg operator, valueForKeyPath: reads the properties specified by the right-click path of each element of the collection, converts them to double (0 instead of nil value), computs the arithmetic mean of those values, and returns the result stored in the NSNumber instance.
To obtain the average transaction amount in the sample data in Table 4-1:
// transactionAverage is the average of the amount property of each Transaction object in the self.transactions array.
NSNumber *transactionAverage = [self.transactions valueForKeyPath:@"@avg.amount"];
Copy the code
TransactionAverage formatting results in $456.54.
@count
When the @count operator is specified, valueForKeyPath: returns an NSNumber instance representing the number of objects in the collection. The correct key path (if present) is ignored.
To get the number of counterparties in a transaction:
// numberOfTransactions is the number of elements in the self.transactions array.
NSNumber *numberOfTransactions = [self.transactions valueForKeyPath:@"@count"];
Copy the code
The value of numberOfTransactions is 13.
@max
When specifying the @max operator, valueForKeyPath: searches for and returns the largest entry in the collection entries named by the right-click path. Searches are compared using the compare: method, which is defined by many Foundation classes, such as the NSNumber class. Therefore, the property indicated by the right-click path must contain an object that responds meaningfully to the message (that is, the elements in the collection must implement compare:). The search will ignore nil collection entries.
To obtain the maximum date values (that is, the date of the last transaction) in the transaction listed in Table 4-1, do the following:
NSDate *latestDate = [self.transactions valueForKeyPath:@"@max.date"];
Copy the code
The formatted latestDate value is Jul 15, 2016.
@min
When specifying the @min operator, valueForKeyPath: searches through the collection items named by the right-click path and returns the smallest item. Searches are compared using the compare: method, which is defined by many base classes, such as the NSNumber class. Therefore, the property indicated by the right-click path must contain objects that make a meaningful response to the message. The search will ignore nil collection entries.
To obtain the minimum date value for the earliest transaction listed in Table 4-1, do the following:
NSDate *earliestDate = [self.transactions valueForKeyPath:@"@min.date"];
Copy the code
Written date value is Dec 1, 2015.
@sum
When specifying the @sum operator, valueForKeyPath: reads the properties specified by the right-click path for each element of the collection, converts them to double (0 instead of nil value), and evaluates the sum of those values. It then returns the result stored in the NSNumber instance.
NSNumber *amountSum = [self.transactions valueForKeyPath:@"@sum.amount"];
Copy the code
The amountSum formatting result is $5,935.00.
Array Operators
The array operator makes valueForKeyPath: return an array of objects corresponding to a specific set of objects indicated by the right-click path.
IMPORTANT: The valueForKeyPath: method will raise an exception if any leaf objects are nil when using the array operator.
@distinctUnionOfObjects
When specifying the @DISTINCtunionofObjects operator, valueForKeyPath: Creates and returns an array of different objects that correspond to the collection of properties specified by the right-click path.
To get a collection of payee attribute values for transactions in transactions that omit duplicate values, do the following:
// distinctPayees is an array of strings with the values of payee for each Transaction object in the self.transactions array (duplicate payee is ignored).
NSArray *distinctPayees = [self.transactions valueForKeyPath:@"@distinctUnionOfObjects.payee"];
Copy the code
The generated distinctPayees array contains an instance of each of the following strings: Car Loan, General Cable, Animal Hospital, Green Power, Mortgage.
NOTE: The @unionofObjects operator provides similar behavior, but does not remove duplicate objects.
@unionOfObjects
When specifying the @unionofobjects operator, valueForKeyPath: creates and returns an array containing all the objects of the collection corresponding to the property specified by the right-click path. Unlike @distinctunionofObjects, duplicate objects are not deleted.
To get a collection of payee attribute values for transactions in transactions:
// Payees are strings containing the values of payee for each Transaction object in the self.transactions array (duplicate payees are not ignored).
NSArray *payees = [self.transactions valueForKeyPath:@"@unionOfObjects.payee"];
Copy the code
The resulting Payees array contains the following strings: Green Power, Green Power, Green Power, Car Loan, Car Loan, Car Loan, General Cable, General Cable, General Cable, Mortgage, Mortgage, Mortgage, Animal Hospital. (Note duplicates)
NOTE: Provides similar behavior to the @distinctunionofArrays operator, but removes duplicate objects.
Nesting Operators
Nesting operators operate on nested collections, where each entry in the collection itself contains a collection.
IMPORTANT: The valueForKeyPath: method will raise an exception if any leaf objects are nil when using nested operators.
For the following description, consider a second Data array, called moreTransactions, that populates the Data in Table 4-2 and is collected into a nested array along with the original transaction array (from the Sample Data section) :
NSArray* moreTransactions = @[<# transaction data #>]; // Data in Table 4-2
NSArray* arrayOfArrays = @[self.transactions, moreTransactions];
Copy the code
Table 4-2 Hypothetical Transaction data in the moreTransactions Array Table 4-2 Hypothetical Transaction data in the moreTransactions Array
payee values | amount values formatted as currency | date values formatted as month day, year |
---|---|---|
General Cable – Cottage | The $120.00 | Dec 18, 2015 |
General Cable – Cottage | The $150.00 | Jan 9, 2016 |
General Cable – Cottage | The $120.00 | Dec 1, 2016 |
Second Mortgage | $1250.00 | Nov 15, 2016 |
Second Mortgage | $1250.00 | Sep 20, 2016 |
Second Mortgage | $1250.00 | Jun 14, 2016 |
Hobby Shop | The $600.00 | Jun 14, 2016 |
@distinctUnionOfArrays
When specifying the @distinctunionofArrays operator, valueForKeyPath: Creates and returns an array of different objects containing combinations of all collections corresponding to the properties specified by the right-click path.
To get different values of the Payee property across all arrays in arrayOfArrays:
NSArray *collectedDistinctPayees = [arrayOfArrays valueForKeyPath:@"@distinctUnionOfArrays.payee"];
Copy the code
The resulting collectedDistinctPayees array contains the following values: Hobby Shop, Mortgage, Animal Hospital, Second Mortgage, Car Loan, General Cable-Cottage, General Cable, Green Power.
NOTE: Provides similar behavior to the @unionofArrays operator, but does not delete duplicate objects.
@unionOfArrays
When specifying the @unionofarrays operator, valueForKeyPath: creates and returns an array containing all objects that are combinations of all collections corresponding to the properties specified by the right-click path, without removing duplicates.
To get the payee property values of all arrays in arrayOfArrays:
NSArray *collectedPayees = [arrayOfArrays valueForKeyPath:@"@unionOfArrays.payee"];
Copy the code
The resulting collectedPayees array contains the following values: Green Power, Green Power, Green Power, Car Loan, Car Loan, Car Loan, General Cable, General Cable, General Cable, Mortgage, Mortgage, Mortgage, Animal Hospital, General Cable-Cottage, General Cable-Cottage, General Cable – Cottage, Second Mortgage, Second Mortgage, Second Mortgage, Hobby Shop.
NOTE: Provides similar behavior to the @distinctunionofArrays operator, but removes duplicate objects.
@distinctUnionOfSets
When you specify the @DISTINCtUnionofSets operator, valueForKeyPath: Creates and returns an NSSet object that contains different objects that are combinations of all collections corresponding to the properties specified by the right-click path.
This operator behaves like @distinctunionofArrays, except that it requires an NSSet instance that contains the NSSet instance of the object, not an NSArray instance of the NSArray instance. In addition, it returns an NSSet instance. Assuming that the sample data is stored in a collection rather than an array, the sample call and result are the same as the result of @distinctunionofArrays.
Our current non-object Values are Representing non-object Values.
The default implementation of the key-value encoding protocol method provided by NSObject handles both object and non-object properties. The default implementation automatically converts between object parameters or return values and non-object properties. This allows the signature of key-based getters and setters to be consistent, even if the stored property is a scalar or structure.
NOTE: Since all attributes in Swift are objects, this section covers objective-C attributes only.
When you call a getter for the protocol (such as valueForKey :), the default implementation determines the specific Accessor method or instance variable that provides the value for the specified key, according to the rules described in Accessor Search Patterns. If the return value is not an object, the getter uses that value to initialize an NSNumber object (for scalar int/float, etc.) or an NSValue object (for structural struct) and returns that value.
Similarly, by default, setter setValue:forKey: Determines the data type required for an accessor or instance variable of a property given a particular key. If the data type is not an object, the setter first sends an appropriate Value message to the incoming Value object to extract the underlying data and store it.
NOTE: Setters have no obvious general procedure to take when you call one of the key-value encoding protocol setters with nil values for non-object properties. Therefore, it sends a setNilValueForKey: message to the object that receives the setter call. The default implementation of this method will lead to NSInvalidArgumentException is unusual, but subclasses may override this behavior, such as processing of object values, such as setting the tag values or provide meaningful default values.
Wrapping and Unwrapping Scalar Types Int /float etc.
Table 5-1 lists the scalar types that the default key-value encoding implementation wraps with NSNumber instances. For each data type, the table shows the creation method used to initialize the NSNumber from the underlying property value to provide the getter return value. It then shows the accessor methods used to extract values from setter input parameters during the set operation.
Table 5-1 Scalar types as wrapped in NSNumber objects
Data type | Creation method | Accessor method |
---|---|---|
BOOL | numberWithBool: | boolValue (in iOS) charValue (in macOS)* |
char | numberWithChar: | charValue |
double | numberWithDouble: | doubleValue |
float | numberWithFloat: | floatValue |
int | numberWithInt: | intValue |
long | numberWithLong: | longValue |
long long | numberWithLongLong: | longLongValue |
short | numberWithShort: | shortValue |
unsigned char | numberWithUnsignedChar: | unsignedChar |
unsigned int | numberWithUnsignedInt: | unsignedInt |
unsigned long | numberWithUnsignedLong: | unsignedLong |
unsigned long long | numberWithUnsignedLongLong: | unsignedLongLong |
unsigned short | numberWithUnsignedShort: | unsignedShort |
NOTE: * In macOS, BOOL is defined as signed char for historical reasons, while KVC does not distinguish between the two types. Therefore, string values such as @”true” or @”YES” should not be passed to setValue:forKey: when the key is a Boolean type. KVC will try to call charValue (because BOOL itself is char), but NSString does not implement this method, which will cause a runtime error. Instead, just pass an NSNumber object, such as @(1) or @(YES), as the argument value setValue:forKey: when the key is of Boolean type. This restriction does not apply to iOS, where the type of BOOL is defined as a native Boolean BOOL, while KVC calls boolValue, which applies to NSNumber objects or well-formed NSString objects.
Wrapping and Unwrapping Structures (Wrapping and Unwrapping Structures)
Table 5-2 shows the create and accessor methods that the default accessor uses to wrap and unpack common NSPoint, NSRange, NSRect, and NSSize structures.
struct CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct _NSRange {
NSUInteger location;
NSUInteger length;
} NSRange;
struct CGRect {
CGPoint origin;
CGSize size;
};
struct CGSize {
CGFloat width;
CGFloat height;
};
Copy the code
Table 5-2 Common struct types as wrapped using NSValue
Data type | Creation method | Accessor method |
---|---|---|
NSPoint | valueWithPoint: | pointValue |
NSRange | valueWithRange: | rangeValue |
NSRect | valueWithRect:(macOS only) | rectValue |
NSSize | valueWithSize: | sizeValue |
Automatic wrapping and expansion are not limited to NSPoint, NSRange, NSRect, and NSSize. Structural types (that is, types of Objective-C encoded strings that begin with {) can be wrapped in NSValue objects. For example, consider the structure and class interface declared in Listing 5-1.
Listing 5-1 A sample class using A custom structure Listing 5-1 A sample class using A custom structure
typedef struct {
float x, y, z;
} ThreeFloats;
@interface MyClass
@property (nonatomic) ThreeFloats threeFloats;
@end
Copy the code
Using an instance of a class named myClass, we can get the threeFloats value via key-value encoding:
NSValue* result = [myClass valueForKey:@"threeFloats"];
Copy the code
The default implementation of valueForKey: calls the threeFloats getter and returns the result wrapped in the NSValue object.
Also, you can set threeFloats using key encoding:
ThreeFloats floats = {1..2..3.};
NSValue* value = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
[myClass setValue:value forKey:@"threeFloats"];
Copy the code
The default implementation uses getValue: message to unpack the value and then calls setThreeFloats: using the result structure.
Validating Properties
The key-value encoding protocol defines methods to support attribute validation. Just as key-based accessors are used to read and write properties of key-value encoded compatible objects, keystroke (or key-path) validation of properties is also possible. When you call validateValue: forKey: error: (or validateValue: forKeyPath: error:) method, the default implementation of the agreement will be on the receiving validation message objects (or key path at the end of the object) in the search for a method, Its name matches the pattern validate:error:. If the object does not have such a method, the validation succeeds by default and the default implementation returns YES. When there is a property-specific validation method, the default implementation returns the result of calling that method.
NOTE: Usually only the validation described in Objective-C is used. In Swift, property validation is more used to relying on compiler support for optionals and strong type checking, Use both the built-in willSet and didSet attribute observer to test any runtime API contracts, As described in Property Observers in The Swift Programming Language (Swift 3).
Because attribute-specific validation methods refer to received values and error parameters, validation has three possible outcomes:
- The validation method considers the value object valid and returns YES without changing the value or error.
- The validation method considers the value object invalid, but chooses not to change it. In this case, the method returns NO and sets the error reference (provided by the caller, if any) to an NSError object indicating the cause of the failure.
- The validation method considers the value object invalid, but creates a new valid object as a replacement. In this case, the method returns YES while leaving the Error object unchanged. Before returning, the method modifies the value reference to point to the new value object. When a method is modified, it always creates a new object rather than modifying the old object, even if the value object is mutable.
Listing 6-1 shows an example of how to invoke name string validation.
Listing 6-1 Validation of the name property
Person* person = [[Person alloc] init];
NSError* error;
NSString* name = @"John";
if(! [person validateValue:&name forKey:@"name" error:&error]) {
NSLog(@"% @",error);
}
Copy the code
Automatic Validation
In general, neither the key-value encoding protocol nor its default implementation defines any mechanism to perform validation automatically. Instead, you can use validation methods when appropriate for your application.
Some other Cocoa technologies perform validation automatically in some cases. For example, Core Data automatically performs validation when the context of a managed object is saved (see Core Data Programming Guide). In addition, Cocoa Bindings in macOS allows you to specify automatic validation (see Cocoa Bindings Programming Topics for more information).
Accessor Search Patterns (visitor Search Patterns)
The default implementation of the NSKeyValueCoding protocol provided by NSObject uses a well-defined set of rules to map key-based accessor calls to the underlying properties of the object. These protocol methods use a key parameter to search for their own object instances to find accessors, instance variables, and related methods that follow some naming convention. Although this default search is rarely modified, it’s helpful to know how it works, both to track the behavior of key-encoded objects and to make your own objects compatible.
NOTE: The description in this section uses or acts as a placeholder for a key when it appears as a parameter in one of the key-value encoding protocol methods. This method is then used as part of an auxiliary method call or a variable name lookup. The attribute name of the map follows the case of the placeholder. For example, for getters and is, the property named hidden will map to hidden and isHidden.
Search Pattern for the Basic Getter
Given a key argument as input, the default implementation of valueForKey: performs the following procedure. (Operates inside the class instance that receives the valueForKey: call)
-
Search the instance for the first accessor method named GET, is, or _. If it is found, call it and proceed to Step 5 with the result returned. Otherwise proceed to the next step. (get, is, or _)
-
If you can’t find a simple accessor method, search the instance for names like countOf, objectInAtIndex (corresponding to the original method defined by the NSArray class), and AtIndexes (corresponding to objectsAtIndexes of NSArray: Method) method of pattern matching.
If the first of these and at least one of the other two are found, a Collection Proxy object that responds to all of the NSArray methods is created and returned. Otherwise, go to Step 3. The proxy object then converts any NSArray messages it receives into a combination of countOf, objectInAtIndex:, and AtIndexes: messages and into the key-value encoding compatible object that created it. If the original object also implements an optional method called get:range: and so on, the proxy object will also use that method when appropriate. In effect, the proxy object works with key-value encoding compatible objects, allowing the underlying property to behave just like NSArray, even if it isn’t.
- If you can’t find a simple accessor method or array access method group, look for the triple methods named countOf, enumeratorOf, and memberOf:. (Corresponding to the original method defined by the NSSet class)
If all three methods are found, create a collection proxy object that responds to all NSSet methods and return that object. Otherwise, go to Step 4. The proxy object then converts any NSSet messages it receives to some combination of countOf, enumeratorOf, and memberOf: messages to create its object. In effect, a proxy object works with a key-value encoding compatible object, making the underlying property behave like an NSSet, even if it is not.
-
If you cannot find a simple accessor methods or collection access method group, and if the receiver class methods accessInstanceVariablesDirectly returns YES, search in the order named _, _is, or is the instance variables. If found, simply get the value of the instance variable and continue with Step 5. Otherwise, go to Step 6. (_, _is, or is)
-
If the retrieved property value is an object pointer, only the result is returned.
If the value is a scalar type supported by NSNumber, it is stored in an NSNumber instance and returned. If the result is a scalar type not supported by NSNumber, it is converted to an NSValue object and returned.
- If all methods fail, valueForUndefinedKey: is called. By default, this throws an exception, but subclasses of NSObject can provide key-specific behavior (subclasses override valueForUndefinedKey: function).
Search Pattern for the Basic Setter
SetValue: forKey: The default implementation of, given key and value parameters as input, tries to set a property named key to value, using the following procedure inside the object that receives the call: (For a property that is not an Object, it’s an expanded version of value, as described in our Representing non-object Values.)
-
Find the first accessor named set: or _set in this order. If it is found, it is called with value (or unpacked value’s value as needed) and done. (set:, _set)
-
, and if you do not find a simple accessor methods accessInstanceVariablesDirectly returns YES, in the order lookup name similar to _, _is, or is the instance variables. If found, set the instance variable directly with value (or unpack the value of value as needed) and complete the operation. (_, _is, or is)
-
When can’t find the accessor or instance variables, called setValue: forUndefinedKey:. This raises an exception by default, but subclasses of NSObject may provide key-specific behavior. (by subclasses override setValue: forUndefinedKey:)
Search Pattern for Mutable Arrays
The default implementation of mutableArrayValueForKey: returns a mutable array of proxies for the property named key in the object called by the receiving accessor, given a key argument as input, using the following procedure:
- Search for a pair of names such as insertObject:inAtIndex: and removeObjectFromAtIndex: (corresponding to NSMutableArray’s original methods, insertObject:atIndex: And removeObjectAtIndex:), or methods with names like INSERT :atIndexes: and removeAtIndexes: (insertobIndexes: And removeObjectsAtIndexes: Method).
If the object has at least one insertion method and at least one removal method, insertion :inAtIndex, removeObjectFromAtIndex, insert:atIndexes, A combination of messages that returns a proxy object that responds to the NSMutableArray message and sends the removeAtIndexes: message to the original recipient of mutableArrayValueForKey:. When receive the mutableArrayValueForKey: message objects also implements an optional replace object method, its name is similar to replaceObjectInAtIndex: withObject: Or replaceAtIndexes:with:, the proxy objects also leverage these objects when appropriate for optimal performance.
- If the object has no mutable array methods, look for accessor methods whose names match set:. In this case, the proxy object that responds to the NSMutableArray message is returned by sending a set: message to the original receiver of mutableArrayValueForKey:.
NOTE: The mechanism described in this step is much less efficient than the mechanism in the previous step, as it may involve repeatedly creating new collection objects rather than modifying existing ones. Therefore, you should generally avoid using it when designing your own key-value encoding compatible objects.
- If neither find variable array method, also found no accessor, and accessInstanceVariablesDirectly returns YES if the receiver class methods, is in the order search called _ or instance variables.
If such an instance variable is found, a proxy object is returned that forwards each received NSMutableArray message to the value of the instance variable, which is typically an instance of NSMutableArray or one of its subclasses.
- If all other operating failure, send mutableArrayValueForKey: messages of the original recipients as long as it receives the NSMutableArray emit setValue: forUndefinedKey: message.
NSUndefinedKeyException setValue: forUndefinedKey:, but subclasses can override this method.
Achieving Basic key-value Coding Compliance
When using key-value encoding for objects, you rely on the default implementation of the NSKeyValueCoding protocol by having your object inherit from NSObject or one of its many subclasses. The default implementation, in turn, relies on you defining the object’s instance variables (or IVAR) and accessor methods according to some well-defined pattern so that you can associate key strings with properties when receiving key-encoded messages. For example, valueForKey: and setValue:forKey:.
You usually follow the standard pattern in Objective-C by simply declaring properties using the @property statement and allowing the compiler to synthesize ivar and accessors automatically. By default, the compiler follows the expected pattern.
NOTE: In Swift, simply declare the properties in the usual way to automatically generate the appropriate accessors, and you never interact directly with ivars. For more information about Properties in Swift, read Properties in The Swift Programming Language (Swift 3). For specific information about interacting with objective-C attributes in Swift, see the Accessing Properties in Using Swift with Cocoa and Objective-C (Swift 3).
If you do need to implement accessors or IVAR manually in Objective-C, follow the guidelines in this section to maintain basic compliance. To provide additional functionality that enhances interaction with object Collection properties in any language, implement the approach described in Defining Collection Methods. To further enhance the object with key-value Validation, implement the methods described in Adding Validation.
NOTE: The default implementation of key-value encoding is available with a wider range of ivars and accessors than described here. If you have older code that uses other variables or Accessor conventions, check the Search Patterns in Accessor Search Patterns to see if the default implementation can find the properties of your object.
Basic Getters
To implement getters that return property values, and possibly perform other custom work, use methods called properties, such as the title string property:
- (NSString*)title {
/ / Extra getter logic...
return _title;
}
Copy the code
For properties with Boolean types, you can also use methods prefixed with is, such as a Boolean property named hidden:
- (BOOL)isHidden {
/ / Extra getter logic...
return _hidden;
}
Copy the code
When the property is a scalar or structure, the default implementation of key-value encoding wraps the value into an Object for use on the interface to protocol methods, as described in Representing non-object Values. You don’t have to do anything special to support this behavior.
Basic Setters
To implement setters for storing property values, use methods with the uppercase first letter of the property’s name beginning with the word set. For the Hidden property:
- (void)setHidden:(BOOL)hidden {
/ / Extra setter logic...
_hidden = hidden;
}
Copy the code
WARNING: Do not call validation methods as described in Validating Properties from inside the set: method.
When the property is not an object type (such as the hidden property of Boolean type), the default implementation of the protocol detects the underlying data type and unlocks data from setValue:forKey: In this case, an NSNumber instance, and then apply that to setters, as described in our Representing non-object Values. You don’t have to deal with that in the setter itself. However, if it is possible to write nil Values to non-object properties, you can override setNilValueForKey: to handle this situation, as described in Handling Non-Object Values. The appropriate behavior for the hidden property might simply be to interpret nil as NO:
- (void)setNilValueForKey:(NSString *)key {
if ([key isEqualToString:@"hidden"]) {
[self setValue:@(NO) forKey:@"hidden"];
} else{ [super setNilValueForKey:key]; }}Copy the code
Even if the compiler is allowed to synthesize setters, the above method overrides can be provided (if applicable).
Instance Variables
When a key coding accessor methods of the default implementation can’t find the property accessor, it will query the class directly accessInstanceVariablesDirectly method, check to see if the class allow direct use of instance variables. By default, this method returns YES, but you can override it to return NO.
If you do allow ivars, be sure to name it in the normal way and use property names prefixed with an underscore (_). Normally, the compiler will do this for you when it automatically synthesizes a property, but if you use the explicit @synthesize directive, you can force the naming yourself:
@synthesize title = _title;
Copy the code
In some cases, instead of using the @synthesis directive or allowing the compiler to automatically synthesize properties, the @dynamic directive is used to inform the compiler that getters and setters will be provided at run time. Doing so avoids automatic synthesis of getters and provides Collection accessors, as described in Defining Collection Methods. In this case, you declare ivAR yourself as part of the interface declaration:
@interface MyObject : NSObject {
NSString* _title;
}
@property (nonatomic) NSString* title;
@end
Copy the code
Defining Collection Methods
When you create getters and IVArs using standard naming conventions (as described in Achieving Basic Key-value Coding Compliance), the default implementation of the key-value Coding protocol can find them based on key-value Coding messages. This is just as true for properties of collection types that represent one-to-many relationships as for any other one-to-one relationship. However, if you replace or implement the collection accessor methods on top of the collection properties, you can:
- Model one-to-many relationships with classes other than NSArray or NSSet. When a collection method is implemented in an object, the default implementation of a key-valued getter returns a proxy object that calls these methods in response to subsequent NSArray or NSSet messages it receives. The underlying property object doesn’t have to be NSArray or NSSet itself, because the proxy object uses your collection methods to provide the expected behavior.
- Achieve increased performance when mutating the contents of a to-many relationship. The default implementation of the protocol uses your collection methods to change the underlying properties, rather than using basic setters to repeatedly create new collection objects in response to each change. (That is, using the mutableArrayValueForKey: function mentioned above to modify the personArray property array directly)
- Provide key-value observing compliant access to the contents of your object’s collection properties Compliance access to observing to access the contents of the collection properties of the object). For more information on critical Value Observing, read the Key-value Observing Programming Guide.
You can implement one of two sets of accessors, depending on whether you want the relationship to behave like an ordered set of indexes (such as NSArray) or an unordered unique set (such as NSSet). In both cases, you need to implement at least one set of methods to support read access to the properties, and then add another set of methods to enable variability in the content of the collection.
NOTE: Key-value encoding protocols do not declare the methods described in this section. Instead, the default implementation of the protocol provided by NSObject looks for these methods in objects that match the key-value encoding, as described in Accessor Search Patterns, and uses them to process key-value encoded messages as part of the protocol.
For the Student class that we defined above, when we type in at sign implementation Student the first few letters of this function Xcode will prompt us to generate that function. For the Student class that we defined above, when we type in at sign implementation Student the first few letters of this function Xcode will prompt us to generate that function.
The basic function of Accessing Indexed Collections
You can add indexed Accessor methods to provide a mechanism for counting, retrieving, adding, and replacing objects in ordered relationships. The underlying object is usually an instance of NSArray or NSMutableArray, but if you provide collection Accessors, you can manipulate any object property that implements these methods as an array.
Indexed Collection Getters
For collection type properties that do not have a default getter, the default implementation of the protocol will respond to the valueForKey: message by providing the following index collection getter methods, returning a proxy object that behaves like NSArray but calls the following collection methods to do its job.
NOTE: In modern Objective-C, the compiler synthesizes a getter for each property by default, so the default implementation does not create a read-only proxy that uses the methods in this section (NOTE the accessor search order for basic getters). You can get around this by not declaring the property (relying only on IVAR) or by declaring the property @dynamic (indicating that you plan to provide accessor behavior at run time). Either way, the compiler does not provide a default getter, and the default implementation uses the following methods.
- CountOf this method returns the number of objects in a one-to-many relationship in the form of NSUInteger, just as the NSArray base method count does. In fact, you can use this method to provide results when the underlying attribute is NSArray. For example, for a one-to-many relationship (called transactions) that represents a list of bank transactions and is supported by NSArray:
- (NSUInteger)countOfTransactions {
return [self.transactions count];
}
Copy the code
- ObjectInAtIndex: or AtIndexes: The first returns the object at the index specified in the one-to-many relationship, while the second returns an array of objects at the index specified in the NSIndexSet argument. They correspond to the NSArray methods objectAtIndex: and objectsAtIndexes:, respectively. You only need to implement one of them. The corresponding method for the Transactions array is:
- (id)objectInTransactionsAtIndex:(NSUInteger)index {
return [self.transactions objectAtIndex:index];
}
- (NSArray *)transactionsAtIndexes:(NSIndexSet *)indexes {
return [self.transactions objectsAtIndexes:indexes];
}
Copy the code
- Get :range: This method is optional, but improves performance. It returns objects in the specified range from the collection and corresponds to NSArray’s getObjects:range: method. The transactions array implementation is
- (void)getTransactions:(Transaction * __unsafe_unretained *)buffer range:(NSRange)inRange {
[self.transactions getObjects:buffer range:inRange];
}
Copy the code
Indexed Collection Mutators
Supporting variable one-to-many relationships with index accessors requires implementing a different set of methods. When you provide these setter methods, the default implementation of the response mutableArrayValueForKey: message will return a proxy object that behaves like the NSMutableArray object, but uses the object’s methods to do its job. This is usually more efficient than simply returning the NSMutableArray object. It also makes the content of one-to-many relationships compliant with key-value Observing (see key-value Observing Programming Guide).
To make your object key-value encoding conform to a variable ordered one-to-many relationship, implement the following methods:
- InsertObject :inAtIndex: or INSERT :atIndexes: The first to receive the object to be inserted and an integer that specifies the index to be inserted into that object. The second method inserts an array of objects into the collection at the index specified by the passed NSIndexSet. These methods are similar to insertObject:atIndex: and insertObjects:atIndexes: for NSMutableArray. You only need one of these methods. For transactions declared as NSMutableArray:
- (void)insertObject:(Transaction *)transaction inTransactionsAtIndex:(NSUInteger)index {
[self.transactions insertObject:transaction atIndex:index];
}
- (void)insertTransactions:(NSArray *)transactionArray atIndexes:(NSIndexSet *)indexes {
[self.transactions insertObjects:transactionArray atIndexes:indexes];
}
Copy the code
- RemoveObjectFromAtIndex: or removeAtIndexes: The first to receive an NSUInteger value that specifies the index of the object to be removed from the Relationship. The second receives an NSIndexSet object that specifies the index of the object to be removed from the Relationship. These methods correspond to removeObjectAtIndex: and removeObjectsAtIndexes: of NSMutableArray, respectively. You only need one of these methods. For transactions objects:
- (void)removeObjectFromTransactionsAtIndex:(NSUInteger)index {
[self.transactions removeObjectAtIndex:index];
}
- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {
[self.transactions removeObjectsAtIndexes:indexes];
}
Copy the code
- ReplaceObjectInAtIndex: withObject: or replaceAtIndexes: with: these replacement accessor for the proxy object provides a direct replacement in the collection method of an object, rather than having to delete an object in turn and insert another object. They correspond to the NSMutableArray replaceObjectAtIndex: withObject: and replaceObjectsAtIndexes: withObjects:. You can choose to provide these methods when profiling your application reveals performance issues. For transactions objects:
- (void)replaceObjectInTransactionsAtIndex:(NSUInteger)index withObject:(id)anObject {
[self.transactions replaceObjectAtIndex:index withObject:anObject];
}
- (void)replaceTransactionsAtIndexes:(NSIndexSet *)indexes withTransactions:(NSArray *)transactionArray {
[self.transactions replaceObjectsAtIndexes:indexes withObjects:transactionArray];
}
Copy the code
Accessing Unordered Collections
You can add an unordered collection accessor method to provide a mechanism for accessing and changing objects in an unordered relationship. Typically, this relationship is an instance of an NSSet or NSMutableSet object. However, when implementing these accessors, any class can be enabled to model the relationship and operate with key-value encoding as if it were an instance of an NSSet.
Unordered Collection Getters
When you provide the following collection getter methods to return the number of objects in the collection, iterate over the collection objects, and test for the existence of objects in the collection, the default implementation of the protocol will respond to the valueForKey: message, returning a proxy object that behaves like AN NSSet, but calling the following collection methods to do its job.
NOTE: In modern Objective-C, the compiler synthesizes a getter for each property by default, so the default implementation does not create a read-only proxy that uses the methods in this section (NOTE the Search Pattern for the Basic getter). You can solve this problem by not declaring attributes (relying only on IVAR) or by declaring attributes as @dynamic, indicating that you plan to provide accessor behavior at run time. Either way, the compiler does not provide a default getter, and the default implementation uses the following methods.
- CountOf This required method returns the number of items in relationship corresponding to the NSSet method count. This method can be called directly when the underlying object is NSSet. For example, for an NSSet object named Employee, which contains the Employee object:
- (NSUInteger)countOfEmployees {
return [self.employees count];
}
Copy the code
- EnumeratorOf This required method returns an instance of NSEnumerator that is used to iterate through the items in relationship. For more information about enumerators, see Enumeration: Traversing a Collection’s Elements in Collections Programming Topics. This method corresponds to the objectEnumerator in the NSSet class. For the employees set:
- (NSEnumerator *)enumeratorOfEmployees {
return [self.employees objectEnumerator];
}
Copy the code
- MemberOf: This method compares the object passed as an argument to the object in the collection and returns the matching object as the result, or nil if no match is found. If you implement the comparison method manually, you usually use isEqual: to compare objects. When the base object is an NSSet object, the equivalent member: method can be used:
- (Employee *)memberOfEmployees:(Employee *)anObject {
return [self.employees member:anObject];
}
Copy the code
Unordered Collection Mutators
To support mutable one-to-many relationships with unordered accessors, additional methods need to be implemented. Implement a variable unordered accessor that allows your object to provide an unordered collection proxy object in response to the mutableSetValueForKey: method. Implementing these accessors is much more efficient than relying on accessors to return mutable objects directly to make changes to the data in the relationship. This also makes the objects in your class’s collection compliant with key-value observing (see the Key-Value Observing Programming Guide).
In order to carry out key-value coding for variable disordered one-to-many relationship, please implement the following method:
- AddObject: or Add: These methods add a single item or group of items to the relationship. When adding a set of items to a relationship, make sure there is no equivalent in the relationship. These methods are similar to NSMutableSet’s addObject: and unionSet:. You only need one of these methods. For the employees collection:
- (void)addEmployeesObject:(Employee *)anObject {
[self.employees addObject:anObject];
}
- (void)addEmployees:(NSSet *)manyObjects {
[self.employees unionSet:manyObjects];
}
Copy the code
- RemoveObject: or remove: These methods remove a single item or group of items from the relationship. They are similar to NSMutableSet’s removeObject: and minusSet:. You only need one of these methods. Such as:
- (void)removeEmployeesObject:(Employee *)anObject {
[self.employees removeObject:anObject];
}
- (void)removeEmployees:(NSSet *)manyObjects {
[self.employees minusSet:manyObjects];
}
Copy the code
- Intersect: This method that receives an NSSet parameter removes all objects from the relationship that are not generic to either the input set or the set set. This is equivalent to intersectSet of NSMutableSet:. You can choose to implement this approach when the analysis indicates performance issues related to collection content updates. Such as:
- (void)intersectEmployees:(NSSet *)otherObjects {
return [self.employees intersectSet:otherObjects];
}
Copy the code
Handling non-object Values
Typically, objects that are compatible with key-value encoding rely on the default implementation of key-value encoding to automatically wrap and unpack non-object properties, as described in our Representing non-object Values. However, you can override the default behavior. The most common reason for doing this is to handle attempts to store nil values on non-object properties.
NOTE: Since all attributes in Swift are objects, this section applies only to Objective-C attributes.
If a key-value encoding compatible object receives a message that setValue:forKey: nil is a value that is not an object property, the default implementation has no generic operation procedure in place. So, it sends itself a setNilValueForKey: message, which you can override. Abnormal NSInvalidArgumentException setNilValueForKey: the default implementation, but you can provide the proper, implementation specific behavior.
For example, the code in Listing 10-1 responds to an attempt to set a person’s age to nil, internally setting age to 0, which is more appropriate for floating point values. Note that the override method calls its superclass’s setNilValueForKey: function for any key it does not explicitly handle. ([super setNilValueForKey: key])
Listing 10-1 Listing 10-1 Example implementation of setNilValueForKey:
- (void)setNilValueForKey:(NSString *)key {
if ([key isEqualToString:@"age"]) {
[self setValue:@(0) forKey:@"age"];
} else{ [super setNilValueForKey:key]; }}Copy the code
NOTE: For backward compatibility, setValue:forKey: calls the unableToSetNilForKey: method instead of setNilValueForKey: when an object overwrites the unableToSetNilForKey: method, which is not recommended.
Adding Validation
The key-value encoding protocol defines the means by which keys or key-paths verify properties. The default implementation of these methods in turn depends on the methods you define in a naming scheme similar to the accessor methods. Specifically, you can provide the validate:error: method for any property whose name is key that you want to validate. The default implementation will search the value, in response to key coding validateValue: forKey: error: message.
If no validation method is provided for an attribute, the default implementation of the protocol assumes that the attribute was successfully validated, regardless of its value. This means that you choose to validate attribute by attribute.
NOTE: You usually only use validation as described in Objective-C. In Swift, attribute validation is more used to relying on compiler support for options and strong type checking, while using built-in willSet and didSet attribute observers to test any runtime API contracts, As described in The Property Observators section of The Swift Programming Language (Swift 3).
Implementing a Validation Method
When you provide a validation method for an attribute, the method takes two arguments via a reference (the address of the pointer (ID *, NSError **) in order to modify the pointing of the original pointer inside the function) : the address of the pointer to the value object to validate and the address of the NSError object pointer used to return an error message. Therefore, your validation method can do one of three things:
- When value is valid, return YES without changing value or error.
- If value is invalid and you cannot or do not want to provide a valid alternative, set the error argument to an NSError object indicating the cause of the failure and return NO.
IMPORTANT: Check whether the NSError ** error argument is not nil before attempting to set the error reference.
- If value is invalid, but you know a valid alternative, create a valid object, point value to the created valid object, and return YES without modifying error. If another value is provided, the new object is always returned, rather than modifying the object being validated, even if the original object is mutable. The value entry is a temporary variable of its own. Not the property of the object being validated.
Listing 11-1 demonstrates a validation method for the Name String attribute that ensures that the value object is not nil and that the name is minimum length. Listing 11-1 shows a validation method for the name String attribute that ensures that the value object is not nil and that the name is minimum length. This method does not replace other values if validation fails.
Listing 11-1 Validation method for the name property
- (BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError{
if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 2)) {
if(outError ! =NULL) {
*outError = [NSError errorWithDomain:PersonErrorDomain
code:PersonInvalidNameCode
userInfo:@{ NSLocalizedDescriptionKey
: @"Name too short" }];
}
return NO;
}
return YES;
}
Copy the code
Validation of Scalar Values
The validation method expects the value parameter to be an Object type, so Values that are not Object attributes will be wrapped in an NSValue or NSNumber Object, as described in our Representing non-object Values. The example in Listing 11-2 demonstrates validation of the scalar property age. In this case, a potentially invalid condition, the nil age value, is handled by creating a valid value set to zero and returning YES. You can also handle this particular condition in setNilValueForKey: overload, because the user of the class might set nil to age without calling the validation method.
Listing 11-2 Validation method for a Scalar property
- (BOOL)validateAge:(id *)ioValue error:(NSError * __autoreleasing *)outError {
if (*ioValue == nil) {
// Value is nil: Might also handle in setNilValueForKey
// Value is nil, possibly handled in the setNilValueForKey function
*ioValue = @(0);
} else if ([*ioValue floatValue] < 0.0) {
if(outError ! =NULL) {
*outError = [NSError errorWithDomain:PersonErrorDomain
code:PersonInvalidAgeCode
userInfo:@{ NSLocalizedDescriptionKey
: @"Age cannot be negative" }];
}
return NO;
}
return YES;
}
Copy the code
Describing Property Relationships
Class Descriptions provide a way to describe one-to-one and one-to-many attributes in a Class. By defining these relationships between class attributes, you can use key-value encoding to manipulate these attributes more intelligently and flexibly.
Class Descriptions
NSClassDescription is a base class that provides an interface for retrieving class metadata. Class description objects record the available properties of objects of a particular class and the relationships (one-to-one, one-to-many, and reverse) between objects of that class and other objects. For example, the attributeKeys method returns a list of all the attributes defined for the class; The ToAnyRelationshipKeys and ToOnRelationshipKeys methods return an array of keys defining a many-to-one relationship; InverseRelationshipKey: Returns the name of the relationship that points to the receiver from the target of the relationship of the supplied key.
NSClassDescription does not define the methods used to define relationships. Concrete subclasses must define these methods. Once created, you can use NSClassDescription registerClassDescription: forClass: class method to register a class description.
NSScriptClassDescription is the only concrete subclass of NSClassDescription provided in Cocoa. It encapsulates the script information of the application.
Designing for Performance
Key-value coding is efficient, especially if you rely on the default implementation to do most of the work, but it does add a level of indirection that is slightly slower than direct method calls. Use key-value encoding only if you can benefit from the flexibility it provides or allow your objects to participate in Cocoa technologies that depend on it.
Ancient Key-value Coding Methods (TPO)
In general, you can make an object conform to key-value encoding by ensuring that it inherits from NSObject and then providing the property-specific accessors and associated methods described in this document. There is little need to override the default implementation of key-value encoded accessors, such as valueForKey: and setValue:forKey:, or key-based validation methods, such as validateValue:forKey:. Because these implementations cache information about the runtime environment for efficiency, if you override them to introduce custom logic, be sure to call the default implementation in the superclass before returning.
Optimizing one-to-many Relationships
When implementing one-to-many relationships, in many cases, especially for mutable collections, the index form of the accessor can significantly improve performance. For more information, see Accessing Collection Properties and Defining Collection Methods.
Compliance Checklist (KVC coding requirements)
Follow the steps outlined in this section to ensure that your object conforms to the key-value encoding. See the previous section for more information.
Attribute and to-one Relationship Compliance
For each attribute as an attribute or one-to-one relationship:
☑️ implements a method named or is, or creates an instance variable or _. The compiler usually does this for you when it automatically synthesizes properties. (That is, when we add a property to a class definition, the compiler usually automatically generates the setter getter for that property and a member variable whose name is _.)
NOTE: Although attribute names usually start with a lowercase letter, the default implementation of the protocol can also use names that start with an uppercase letter, such as a URL.
☑️ If the property is mutable, implement the set: method. When you allow the compiler to automatically synthesize a property, the compiler will usually do this for you.
IMPORTANT: If you override the default setter, be sure not to call any validation methods of the protocol.
☑️ If the property is a scalar, override the setNilValueForKey: method to gracefully handle assigning nil values to scalar properties.
Indexed To-Many Relationship Compliance
For each property that is a one-to-many relationship (such as the NSArray property) :
☑️ implements a method named, which returns an array, or has an array instance variable named or _. The compiler will usually do this for you when the attribute is automatically synthesized.
☑️ or, implement one or both of the countOf and objectInAtIndex: and AtIndexes: methods.
☑️ (Optional) Implement get:range: to improve performance.
Also, if the property is mutable:
☑️ implements one or both methods of insertObject:inAtIndex: and INSERT :atIndexes:.
☑️ implements one or both of removeObjectFromAtIndex: and removeAtIndexes:.
☑ ️ (optional) implementation replaceObjectInAtIndex: withObject: or replaceAtIndexes: with: to improve the performance.
Unordered To-Many Relationship Compliance
For each property that is an unordered one-to-many relationship (such as an NSSet property) :
☑️ implements a method named, which returns either a collection, or an instance variable with an NSSet type named or _. The compiler will usually do this for you when the attribute is automatically synthesized.
☑️ or, implement methods countOf, enumeratorOf, and memberOf:.
Also, if the property is mutable:
☑️ implements one or both of the methods addObject: and add:.
☑️ implements one or both of removeObject: and remove:.
☑️ (Optional) Implement INTERSECT: to improve performance.
Validation
Select whether validation is required:
Implement the validate:error: method that returns a Boolean value indicating the validity of the value and, where appropriate, a reference to an NSError object describing the cause of the error.
Refer to the link
Reference link :🔗
- Key-Value Coding Programming Guide