OC learning --- KVC and KVO operations, oc --- kvckvo
In the previous article, we introduced the most common file operations in OC: commit.
1. KVC operations
The KVC operation in OC is very violent with the variable that uses the reflection mechanism to remove the private permission of the classes in Java. This will damage the encapsulation of classes, the private permission in the class is not intended to be accessed by the outside world, but we will do the opposite in this way, but sometimes we really need to do this, ah. So some things are not just natural, but they are born when they are needed.
Let's take a look at the use of this technology:
Dog. h
//// Dog. h // 42_KVC /// Created by jiangwei on 14-10-14. // Copyright (c) 2014 jiangwei. all rights reserved. // # import <Foundation/Foundation. h> @ interface Dog: NSObject @ end
Dog. m
//// Dog. m // 42_KVC /// Created by jiangwei on 14-10-14. // Copyright (c) 2014 jiangwei. all rights reserved. // # import "Dog. h "@ implementation Dog @ end
The Dog class is defined, but nothing exists. It is only an intermediate class and has no effect. In this demo.
Person. h
/// Person. h // 42_KVC /// Created by jiangwei on 14-10-14. // Copyright (c) 2014 jiangwei. all rights reserved. // # import <Foundation/Foundation. h> # import "Dog. h "@ interface Person: NSObject {@ private NSString * _ name; NSDog * _ dog; NSInteger * age;} @ end
Person. m
/// Person. m // 42_KVC /// Created by jiangwei on 14-10-14. // Copyright (c) 2014 jiangwei. all rights reserved. // # import "Person. h "@ implementation Person-(NSString *) description {NSLog (@" % @ ", _ name); return _ name ;}@ endWe define two attributes in the Person class, but these two attributes are not accessible to the outside, and there is no corresponding get/set method. We also implemented the description method for printing the results.
Let's take a look at the test code.
Main. m
//// Main. m // 42_KVC /// Created by jiangwei on 14-10-14. // Copyright (c) 2014 jiangwei. all rights reserved. // # import <Foundation/Foundation. h> # import "Person. h "# import" Dog. h "// KVC: very violent. in a timely manner, the attributes of a class are private, and there is no get/set method. It can also be read/written. // It is equivalent to reflection in Java, destroys class encapsulation int main (int argc, const char * argv []) {@ autoreleasepool {Person * p = [Person alloc] init]; // set the value // setValue method: the first parameter is value, and the second parameter is key (that is, the attribute name of the class) [p setValue: @ "jiangwei" forKey: @ "name"]; Dog * dog = [[Dog alloc] init]; [p setValue: dog forKey: @ "dog"]; // when KVC sets the value, if the property has a set method, the set method is called first. If not, the set method is directly set. The get method is similar to // The NSString * name = [p valueForKey: @ "name"]; // set the basic data type // here you need to convert the basic data type to NSNumber // when setting the value, the process of automatic unpacking will occur. NSNumber will unpackage and assign it to age [p setValue: @ 22 forKey: @ "age"]; NSLog (@ "% @", p); return 0;} return 0 ;}Here we generate a Person object and start to use KVC technology:
1. Set attribute values
// Set the value // setValue method: the first parameter is value, and the second parameter is key (that is, the attribute name of the class) [p setValue: @ "jiangwei" forKey: @ "name"]; Dog * dog = [[Dog alloc] init]; [p setValue: dog forKey: @ "dog"];
Using the setValue method, you can set the value of the attribute. At the same time, you need to pass the name of this attribute, which is very similar to using the reflection mechanism in Java.
Note: When KVC sets the value, if the property has a set method, the set method is called first. If not, the set method is set directly. The get method is the same.
// Set the basic data type // here you need to convert the basic data type to NSNumber // when setting the value, the process of automatic unpacking will occur. NSNumber will unpackage and assign it to age [p setValue: @ 22 forKey: @ "age"];
Another thing to note: when setting the basic type, we need to convert it to the NSNumber type.
2. Get the attribute value
// Read value NSString * name = [p valueForKey: @ "name"];
The value is simple.
Next let's take a look at the powerful functions in KVC: Key-value path
The key-value path is convenient for attributes of an array object in a class.
Scenario:
One author has multiple books
Author. h
//// Author. h // 43_KeyValuePath /// Created by jiangwei on 14-10-15. // Copyright (c) 2014 jiangwei. all rights reserved. // # import <Foundation/Foundation. h> @ interface Author: NSObject {NSString * _ name; // The book published by the Author. An Author corresponds to multiple book objects NSArray * _ issueBook;} @ end
The author class defines the name and an array of books.
Author. m
//// Author. m // 43_KeyValuePath /// Created by jiangwei on 14-10-15. // Copyright (c) 2014 jiangwei. all rights reserved. // # import "Author. h "@ implementation Author @ end
Book. h
//// Book. h // 43_KeyValuePath /// Created by jiangwei on 14-10-15. // Copyright (c) 2014 jiangwei. all rights reserved. // # import <Foundation/Foundation. h> # import "Author. h "@ interface Book: NSObject {Author * _ author;} @ property NSString * name; @ property float * price; @ endDefines an author attribute, book name, and price
Book. m
//// Book. m // 43_KeyValuePath /// Created by jiangwei on 14-10-15. // Copyright (c) 2014 jiangwei. all rights reserved. // # import "Book. h "@ implementation Book @ end
Let's take a look at the test code.
Main. m
//// Main. m // 43_KeyValuePath /// Created by jiangwei on 14-10-15. // Copyright (c) 2014 jiangwei. all rights reserved. // # import <Foundation/Foundation. h> # import "Book. h "# import" Author. h "int main (int argc, const char * argv []) {@ autoreleasepool {// ---------------- KVC key value path/* Book * book = [[Book alloc] init]; author * author = [[Author alloc] init]; // set the author's [book setValue: author forKey: @ "Author"]; // set the author's name // path: author. name, in the middle, connect to [book setValue: @ "jiangwei" forKeyPath: @ "author. name "]; NSString * name = [author valueForKey: @" name "]; NSLog (@" name is % @ ", name ); * // -------------------- KVC operation Author * author = [[Author alloc] init]; [author setValue: @ "Mo Yan" forKeyPath: @ "name"]; book * book1 = [[Book alloc] init]; book1.name = @ ""; book1.price = 9; Book * book2 = [[Book alloc] init]; book2.name = @ "frog"; book2.price = 10; NSArray * array = [NSArray arrayWithObjects: book1, book2, nil]; [author setValue: array forKeyPath: @ "issueBook"]; // The basic data type is automatically packaged as NSNumber and loaded into the array. // obtain the prices of all books. NSArray * priceArray = [author valueForKeyPath: @ "issueBook. price "]; NSLog (@" % @ ", priceArray); // obtain the array size NSNumber * count = [author valueForKeyPath: @" issueBook. @ count "]; NSLog (@" count = % @ ", count); // obtain the sum of book prices NSNumber * sum = [author valueForKeyPath: @" issueBook.@sum.price "]; NSLog (@ "% @", sum); // obtain the average NSNumber * avg = [author valueForKeyPath: @ "issueBook.@avg.price"]; NSLog (@ "% @", avg); // obtain the maximum and minimum prices of books NSNumber * max = [author valueForKeyPath: @ "issueBook.@max.price"]; NSNumber * min = [author valueForKeyPath: @ "issueBook.@min.price"];} return 0 ;}
1. First, set the author's book array through the KVC mentioned above
// -------------------- KVC operation Author * author = [[Author alloc] init]; [author setValue: @ "Mo Yan" forKeyPath: @ "name"]; book * book1 = [[Book alloc] init]; book1.name = @ ""; book1.price = 9; Book * book2 = [[Book alloc] init]; book2.name = @ "frog"; book2.price = 10; NSArray * array = [NSArray arrayWithObjects: book1, book2, nil]; [author setValue: array forKeyPath: @ "issueBook"];
Added two books
2. The following describes how to use the KVC key value path.
1) Get the price of all books in the array of books in the author class
// The basic data type is automatically packaged as NSNumber and loaded into the array. // obtain the prices of all books. NSArray * priceArray = [author valueForKeyPath: @ "issueBook. price "]; NSLog (@" % @ ", priceArray );
@ "IssueBook. price "this is the use of the key-value path. issueBook is the attribute name of the book array in the author class, and price is the attribute of the book class. It is connected by a dot in the middle, in this way, we can get the price of all books. If we use Java, we need to use a loop operation. But how convenient it is in OC.
2) Get the size of the book array in the author class
// Obtain the array size NSNumber * count = [author valueForKeyPath: @ "issueBook. @ count"]; NSLog (@ "count = % @", count );
Use @ "issueBook. @ count "the size of the book array obtained by the key-value path. issueBook is the attribute name of the book array in the author class, and @ count is a specific method. You can think of it as a method, any link in the middle is connected by a node number
3) Get the total price of the book array in the author class
// Get the total book price NSNumber * sum = [author valueForKeyPath: @ "issueBook.@sum.price"]; NSLog (@ "% @", sum );
Use the @ "issueBook.@sum.price" key value path to get the price sum in the book array, issueBook is the attribute name of the book array in the author class, @ sum is the feature writing, you can think of it as a method, price is the price attribute name of a book. It can be regarded as a parameter of @ sum, and is connected by a dot in the middle.
In java, this requires a loop to calculate the sum, which is very convenient in OC.
4) obtain the average price, minimum value, and maximum value of the book array in the author class.
// Obtain the average NSNumber * avg = [author valueForKeyPath: @ "issueBook.@avg.price"]; NSLog (@ "% @", avg ); // obtain the maximum and minimum price NSNumber * max = [author valueForKeyPath: @ "issueBook.@max.price"]; NSNumber * min = [author valueForKeyPath: @ "issueBook.@min.price"];
The operation is similar to the above, so I will not explain it here
We can see that the data returned above is of the NSNumber type.
Ii. KVO operations
KVO operations are often used in OC, and this mechanism does not exist in java.
It is used to listen for changes in attribute values in the class. The implementation principle is the observer mode. Of course, we can also use the observer mode to implement this mechanism in Java.
Take a look at the specific example: there is now a Child class, which has two attributes: Happy value, hunger value, and then a nurse class to listen to these two attribute values of the Child class.
Chidren. h
/// Children. h // 44_KVO // Created by jiangwei on 14-10-16. // Copyright (c) 2014 jiangwei. all rights reserved. // # import <Foundation/Foundation. h> @ interface Children: NSObject @ property NSInteger * hapyValue; @ property NSInteger * hurryValue; @ end
Children. m
/// Children. m // 44_KVO // Created by jiangwei on 14-10-16. // Copyright (c) 2014 jiangwei. all rights reserved. // # import "Children. h "@ implementation Children-(id) init {self = [super init]; if (self! = Nil) {// start the timer [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @ selector (timerAction) userInfo: nil repeats: YES]; self. hapyValue = 100;} return self;}-(void) timerAction :( NSTimer *) timer {// you can use the set method to modify the attribute value to trigger KVO int value = _ hapyValue; [self setHapyValue: -- value]; int values = _ hurryValue; [self setHurryValue: -- values];} @ endIn the initialization method, we start a timer and modify the Child class value every 1 s.
Nure. h
//// Nure. h // 44_KVO // Created by jiangwei on 14-10-16. // Copyright (c) 2014 jiangwei. all rights reserved. // # import <Foundation/Foundation. h> @ class Children; @ interface Nure: NSObject {Children * _ children;}-(id) initWithChildren :( Children *) children; @ endDefine a child Property
Nure. m
//// Nure. m // 44_KVO // Created by jiangwei on 14-10-16. // Copyright (c) 2014 jiangwei. all rights reserved. // # import "Nure. h "# import" Children. h "@ implementation Nure-(id) initWithChildren :( Children *) children {self = [super init]; if (self! = Nil) {_ children = children; // observe the child's hapyValue // use KVO to add an observer to the _ children object to check whether the value of the hapyValue monitored is modified. [_ children addObserver: self forKeyPath: @ "hapyValue" options: NSKeyValueObservingOptionNew | alert context: @ "context"]; // observe the child's hurryValue [_ children addObserver: self forKeyPath: @ "hurryValue" options: NSKeyValueObservingOptionNew | define context: @ "context"];} return self;} // trigger method-(void) observeValueForKeyPath :( NSString *) keyPath ofObject :( id) object change :( NSDictionary *) change context :( void *) context {NSLog (@ "% @", change); // print change, we can see the corresponding key // judge the observer if ([keyPath isw.tostring: @ "hapyValue"]) of different attributes using keyPath. {// here, the values of old and new in change are used when we call the addObserver method. // NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld; which one is needed? // [change objectForKey: @ "old"] indicates the NSNumber * hapyValue = [change objectForKey: @ "new"]; // NSInteger * value = [hapyValue integerValue]. if (value <90) {// do something ...}} else if ([keyPath isinclutostring: @ "hurryValue"]) {// here, the values of old and new in change are used because we used the // NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld when calling the addObserver Method; which one you want to use? // [change objectForKey: @ "old"] is the NSNumber * hurryValue = [change objectForKey: @ "new"]; // NSInteger * value = [hurryValue integerValue]; if (value <90) {// do something ...}} NSLog (@ "% @", context); // print the context parameter of the addObserver method. // use KVC to modify the attribute value and trigger the event}-(void) dealloc {// remove the observer [_ children removeObserver: self forKeyPath: @ "hapyValue"]; [_ children removeObserver: self forKeyPath: @ "hurryValue"];} @ endNow we can see that the listener is started here.
Next, let's take a look at how to implement the listener.
1. Add a listener object
We use the addObserver method to add a listener object to the child.
The first parameter: listener. Here it is Nure, so self can be passed directly.
The second parameter is the property name of the listener object.
The third parameter: monitors the status of this attribute. Here, you can use | to perform multiple combination operations, including the new and old values of the attribute.
Fourth parameter: Pass the content to the listener Method
// Observe the child's hapyValue // use KVO to add an observer to the _ children object to observe whether the value of the hapyValue monitored is modified [_ children addObserver: self forKeyPath: @ "hapyValue" options: NSKeyValueObservingOptionNew | alert context: @ "context"]; // observe the child's hurryValue [_ children addObserver: self forKeyPath: @ "hurryValue" options: NSKeyValueObservingOptionNew | alert context: @ "context"];
2. Listener Method
// Trigger method-(void) observeValueForKeyPath :( NSString *) keyPath ofObject :( id) object change :( NSDictionary *) change context :( void *) context {NSLog (@ "% @", change); // by printing change, we can see the corresponding key // determine the observer if ([keyPath isEqualToString: @ "hapyValue"]) of different attributes through keyPath. {// here, the values of old and new in change are used when we call the addObserver method. // NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld; which one is needed? // [change objectForKey: @ "old"] indicates the NSNumber * hapyValue = [change objectForKey: @ "new"]; // NSInteger * value = [hapyValue integerValue]. if (value <90) {// do something ...}} else if ([keyPath isinclutostring: @ "hurryValue"]) {// here, the values of old and new in change are used because we used the // NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld when calling the addObserver Method; which one you want to use? // [change objectForKey: @ "old"] is the NSNumber * hurryValue = [change objectForKey: @ "new"]; // NSInteger * value = [hurryValue integerValue]; if (value <90) {// do something ...}} NSLog (@ "% @", context); // print the context parameter of the addObserver method. // use KVC to modify the attribute value and trigger the event}The first parameter we passed above is the listener. This method is also implemented in the listener. When the attribute value changes, this method will be called back.
Parameters of this method:
First parameter: key value path
Second parameter: Listener object
Third parameter: changed Value
Fourth parameter: transmitted content
The Code contains a special parameter: The third parameter: NSDirctionary type
In fact, if we don't know what it is, we can print out the results and take a look at it. It's very simple, and it won't be explained here.
We will find that he has two key-value pairs.
Key: new and old
They are the frontend and backend values representing the changes in this property value, and their results are also related to the third parameter we set when we added the listener object:
NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
Several States are set in that place. Here, there are several key-value pairs in NSDirctionary.
3. Destruction Method
This does not belong to KVO, but it is used here. By the way
-(Void) dealloc {// remove the observer [_ children removeObserver: self forKeyPath: @ "hapyValue"]; [_ children removeObserver: self forKeyPath: @ "hurryValue"];}When creating an object, we call the alloc method. When the object is destroyed, we call the dealloc method. This method is the same as the destructor in C ++. Java has a garbage collector, so there is no such method, but there is a finalize method. In fact, this method is called when the Garbage Collector recycles an object, which is similar to this function, but in Java, we do not advocate this method. This may cause GC collection errors.
We need to remove the listener in the destroy method.
Summary
This article introduces two features of OC: KVC and KVO.
KVC: attackers can perform brute-force get/set class private attributes, and powerful key-value paths can also be used to operate on array-type attributes.
KVO: the property value of the listener Class Changes