Runtime member variable

Source: Internet
Author: User

Runtime member variable
Preface:

Without a lot of preparations, let alone: runtime is a dynamic library written by C and sink, just like a small system that closely associates OC with C. This system mainly does two things.

1. encapsulate the structures and functions of C language to allow developers to create, check, or modify classes, objects, and methods at runtime.
2. Pass the message and find the final Execution Code of the method.

That is to say, the OC code we write will be converted to the runtime code at runtime.

Through the learning of runtime, we can better understand the message sending mechanism of OC, and I also think that the learning of runtime is essential to the deep learning of iOS, for example, you may improve your programming skills by reading some third-party frameworks, and there will be a lot of runtime code in these third-party frameworks. What can we do with runtime?

1. traverse all attributes of an object
2. dynamically Add/modify attributes and dynamically Add/modify/replace Methods
3. dynamically create classes/objects
4. Method interception (add a dynamic implementation to the method, or even rewrite or package the method to lisi)

It sounds like black magic. In fact, runtime is known as black magic! Let's start learning runtime from the member variable.

Body Member variables:

Member variables are an important component in defining a class. They mainly want to describe the attributes and features of the class after it is instantiated. Just like defining a Person class, the Person class has various attributes such as name, age, and gender to describe this class. The following example shows how the member variables are used, but does not indicate the essence of the member variables? In the runtime. h file, the member variable is a struct pointer pointing to the objc_ivar type:

1 /// An opaque type that represents an instance variable.2 typedef struct objc_ivar *Ivar;

The struct of objc_ivar is defined as follows:

Struct objc_ivar {char * ivar_name ready; // member variable name char * ivar_type OBJC2_UNAVAILABLE; // member variable type int ivar_offset OBJC2_UNAVAILABLE; # ifdef _ LP64 _ int space ready; # endif}

You can perform the following operations on member variables:

Ivar * class_copyIvarList (Class cls, unsigned int * outCount) // obtain all member variables const char * ivar_getName (Ivar v) // obtain the name of a member variable const char * ivar_getTypeEncoding (Ivar v) // obtain the type code of a member variable Ivar class_getInstanceVariable (Class cls, const char * name) // obtain the member variable id object_getIvar (id obj, Ivar ivar) of the specified name in a class. // obtain the value void object_setIvar (id obj, Ivar ivar, id value) // set the value of a member variable of an object

The following describes the functions provided in runtime by establishing a Person class. First, we define a Person class and override its description method:

In Person. h:

@ Interface Person: NSObject {NSString * clan; // family name} @ property (nonatomic, copy) NSString * name; @ property (nonatomic, copy) NSString * gender; @ property (nonatomic, strong) NSNumber * age; @ property (nonatomic, assign) NSInteger height; @ property (nonatomic, assign) double weight;

Person. m

-(NSString *) description {unsigned int outCount; Ivar * IvarArray = class_copyIvarList ([Person class], & outCount ); // obtain all member variables in Person for (unsigned int I = 0; I <outCount; I ++) {Ivar * ivar = & IvarArray [I]; NSLog (@ "% d member variable: % s, type: % s", I, ivar_getName (* ivar), ivar_getTypeEncoding (* ivar )); // obtain each member variable in sequence and print the member variable name and type} return nil ;}

Create the Person instance class in the program portal and call the description method to view the print of the printer:


The ivar_getTypeEncoding function obtains the type encoding of member variables. Type encoding is another form that Apple specifies for the Data Type object. For example, "@" indicates an object and ":" indicates a SEL pointer, "v" indicates void. For details, refer to the Apple official documentation on the specific type encoding rules: Stamp me !!!

Assign values to and obtain object values through runtime:

In Person. m, two methods are implemented to assign values to the instantiated person object and set the values respectively:

 1 + (Person *)personWithName:(NSString *)name age:(NSNumber *)age gender:(NSString *)gender clan:(NSString *)clan 2 { 3     Person *p = [Person new]; 4     unsigned int outCount; 5     Ivar *IvarArray = class_copyIvarList([Person class], &outCount); 6     object_setIvar(p, IvarArray[0], clan); 7     object_setIvar(p, IvarArray[1], name); 8     object_setIvar(p, IvarArray[2], gender); 9     object_setIvar(p, IvarArray[3], age);10     return p;11 }12 13 - (void)personGetPersonMessage14 {15     unsigned int outCount;16     Ivar *IvarArray = class_copyIvarList([Person class], &outCount);17     for (NSInteger i = 0; i < 4; i ++) {18         NSLog(@"%s = %@",ivar_getName(IvarArray[i]),object_getIvar(self,IvarArray[i]));19     }20 }

In viewDidLoad:

1 Person * person = [Person personWithName: @ "Zhang San" age: @ 26 gender: @ "man" clan: @ "Han"]; 2 [person personGetPersonMessage];

You can see the print on the printer:

Successfully set the value and value of the Person object.

 

Attribute:

The attributes are defined as follows in runtime:

1 /// An opaque type that represents an Objective-C declared property.2 typedef struct objc_property *objc_property_t;3 /// Defines a property attribute4 typedef struct {5     const char *name;           /**< The name of the attribute */6     const char *value;          /**< The value of the attribute (usually empty) */7 } objc_property_attribute_t;

The essence of an attribute is a struct pointer pointing to objc_property. Like member variables, runtime defines a series of operation functions for attributes:

Objc_property_t * class_copyPropertyList (Class cls, unsigned int * outCount) // obtain the list of all attributes const char * property_getName (objc_property_t property) // obtain the name of an attribute const char * property_getAttributes (objc_property_t property) // obtain the attribute property description objc_property_attribute_t * property_copyAttributeList (objc_property_t property, unsigned int * outCount) // obtain the features of all attributes

Obtain the feature descriptions of all attributes in the person instance object:

In Person. m:

1-(void) getAttributeOfproperty2 {3 unsigned int outCount; 4 objc_property_t * propertyList = class_copyPropertyList ([Person class], & outCount); 5 for (NSInteger I = 0; I <outCount; I ++) {6 NSLog (@ "attribute: % s, its feature description: % s", property_getName (propertyList [I]), property_getAttributes (propertyList [I]); 7} 8}

When you get the property list, only the variables with the property attribute Declaration are obtained. All the variables that are printed on the printer when getAttributeOfproperty is called:

The feature description mainly describes the modifier of this attribute. The specific meanings are as follows:

1 attribute type name value: T value: Change 2 encoding type name value: C (copy) & (strong) W (weak) null (assign) and other values: no 3 non/atomic name value: NULL (atomic) N (Nonatomic) value: None

At runtime, We can get all member variables and private variables of the class. All important applications of runtime areDictionary-to-model, complex Archiving.

Application 1: complex object Archiving

For complex object archiving, we usually need to follow the <NSCoding> protocol and rewrite the encoding and decoding methods in the Protocol. Create an NSKeyarchive object to encode and decode member variables one by one.

Runtime is basically the same operation, but we can use the function provided by runtime to obtain the variable name and the corresponding member variable, enable loop for quick archiving (remember to write data one by one under normal circumstances), and take the Person class as an example;

In Person. m:

-(instancetype)initWithCoder:(NSCoder *)aDecoder{    self = [super init];    if (self) {        unsigned int outCount;        Ivar *ivarList = class_copyIvarList([Person class], &outCount);        for (NSInteger i = 0; i < outCount; i ++) {            Ivar ivar = ivarList[i];            NSString *ivarName = [NSString                                  stringWithUTF8String:ivar_getName(ivar)];            [self setValue:[aDecoder decodeObjectForKey:ivarName] forKey:ivarName];        }    }    return self;}-(void)encodeWithCoder:(NSCoder *)aCoder{    unsigned int outCount;    Ivar *ivarlist = class_copyIvarList([self class], &outCount);    for (NSInteger i = 0; i < outCount; i ++) {        Ivar ivar = ivarlist[i];        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];        [aCoder encodeObject:[self valueForKey:ivarName] forKey:ivarName];    }}
Application 2 dictionary-to-model:

Another important application is to convert the dictionary into a model and assign values to the corresponding attributes in the model. The general idea is to first obtain all the attributes through class_copyPropertyList, then get the name of the variable as the key value through property_getName, and check whether the dictionary contains the corresponding value through the key value, if yes, assign a value to the attribute.

The preceding operations are based on the attributes of an object and get some information about the attributes through runtime, such as the name, attribute value, and attribute feature description. Through runtime, you can also dynamically add variables to an object, that is, add associations. Do you still remember the difference between classification and extension? You can add attributes and methods to a class, but you can only add methods to a class. I have an interview question: How do I add a public variable to the system class without using inheritance? We know that the variables added to the class in the extension are private variables and cannot be accessed by the outside world. If you have knowledge of runtime, you may know that this is to test the runtime knowledge of the candidates.

Three functions are provided in runtime to associate objects:

1 void objc_setAssociatedObject (id object, const void * key, id value, objc_AssociationPolicy policy) // associate an object for a class. 2 id objc_getAssociatedObject (id object, const void * key) // obtain an associated object of a Class 3 void objc_removeAssociatedObjects (id object) // remove the associated object 4 parameter description: 5/** 6 * parameter description: 7 object: object To add member variables 8 key: key value corresponding to member variables 9 value: member variable to be added 10 policy: modifier of the added member variables 11 */

Let's take adding an NSString public variable funnyName to NSDictionary as an example:

In the NSDictionary classification MyDict. h, two new attributes are added, one of which is a string and one block:

1 @property(nonatomic,copy)NSString *funnyName;2 @property(nonatomic,copy)void(^dictAction)(NSString *str);

Generally, if we only declare these variables for external use, an error will be reported, all the set and get methods that require us to manually implement them (do not think that the error is reported because we didn't implement their set and method, @ property: However, the get and set methods are automatically generated for the modified attributes)

How should we implement their set and get methods:

 1 -(void)setFunnyName:(NSString *)funnyName 2 { 3     objc_setAssociatedObject(self, @selector(funnyName), funnyName, OBJC_ASSOCIATION_COPY_NONATOMIC); 4 } 5  6 -(NSString *)funnyName 7 { 8     return objc_getAssociatedObject(self, @selector(funnyName)); 9 }10 11 -(void)setDictAction:(void (^)(NSString *))dictAction12 {13     objc_setAssociatedObject(self, @selector(dictAction), dictAction, OBJC_ASSOCIATION_COPY_NONATOMIC);14 }15 16 -(void (^)(NSString *))dictAction17 {18     return objc_getAssociatedObject(self, @selector(dictAction));19 }

In our program, we can use the two new attributes of the dictionary:

1 NSDictionary * dict = [NSDictionary new]; 2 dict. funnyName = @ "SoFunny"; 3 NSLog (@ "dict. funnyName = % @ ", dict. funnyName); 4 void (^ action) (NSString * str) = ^ (NSString * str) {5 NSLog (@ "printed this string: % @", str ); 6}; 7 // set block 8 dict. dictAction = action; 9 10 // call dict's action11 dict. dictAction (@ "new variable dicAction ");

On the printer, we can see that the print is successfully printed to what we want:

First taste of runtime, if there is any improper expression, please also point out. We will continue to update the learning of runtime in the future.

Stamp me to view code

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.