Ios-runtime knowledge points, Ios-runtime knowledge points

Source: Internet
Author: User

Ios-runtime knowledge points, Ios-runtime knowledge points

Directory of this document

    • 1.Runtime Introduction
    • 2.Runtime Related header Files
    • 3. Technical points and application scenarios
      • 3_1. Get property \ member Variable list
      • 3_2. Exchange Method implementation
      • 3_3. class \ object's associated object, False property
      • 3_4. Dynamically adding methods to intercept non-implemented methods
      • 3_5. Dynamically creating a class
    • 4. Face Questions

-1.runtime Introduction back to Top

1.Runtime Introduction

Because OBJC is a dynamic language, it always tries to postpone some of the decision work from the compile connection to the runtime. This means that only the compiler is not enough, and a runtime system is required to execute the compiled code. This is the meaning of the existence of the OBJECTIVE-C runtime system, which is a cornerstone of the entire OBJC operating framework.

There are actually two versions of runtime: "Modern" and "legacy". The OBJECTIVE-C 2.0 we are using now is the current (modern) version of the runtime system, which can only be run in 64-bit programs after IOS and OS X 10.5. OS X's older 32-bit programs still use the (early) Legacy version of the Runtime system in Objective-c 1. The biggest difference between the two versions is that when you change the layout of an instance variable of a class, you need to recompile its subclasses in earlier versions, and the current version is not required.

Runtime is basically written in C and sink, which can be seen in Apple's efforts to make dynamic systems efficient. You can go down here to Apple to maintain the open source code. Apple and GNU each maintain an open-source runtime version, and the two versions are struggling to keep up with each other.

-2.runtime related header files back to the top

2.Runtime Related header Files

There are several files under the USR/INCLUDE/OBJC folder in the iOS SDK

List.hnsobjcruntime.hnsobject.hobject.hprotocol.ha.txthashtable.hhashtable2.hmessage.hmodule.mapobjc-api.hobjc-auto.hobjc -class.hobjc-exception.hobjc-load.hobjc-runtime.hobjc-sync.hobjc.hruntime.h

are header files associated with the runtime, where the main functions are defined in the two files of Message.h and runtime.h. In Message.h, there are a number of functions that send messages to an object, which is the underlying implementation of the OC object method invocation. Runtime.h is the most important file at run time, which contains methods for manipulating the runtime. Mainly include:

1. Definition of type of action object
An opaque type is represents a method in a class definition. A type that represents a method in the class definition typedef struct OBJC_METHOD *method;///an opaque type the represents an instance variable. The variable that represents the instance (object) Ty pedef struct Objc_ivar *ivar;///a opaque type that represents a category. Represents a taxonomy typedef struct OBJC_CATEGORY *category;// /An opaque type this represents an Objective-c declared property. Attributes that represent the OC declaration typedef struct OBJC_PROPERTY *objc_property_t;/ /class represents a class, which is defined in objc.h as a typedef struct OBJC_CLASS *class;struct Objc_class {class Isa objc_isa_availability; #if    !__objc2__ Class Super_class objc2_unavailable;    const char *name objc2_unavailable;    Long version objc2_unavailable;    Long info objc2_unavailable;    Long Instance_size objc2_unavailable;         struct Objc_ivar_list *ivars                    objc2_unavailable;    struct Objc_method_list **methodlists objc2_unavailable;    struct Objc_cache *cache objc2_unavailable; struct objc_protocol_list *protocols objc2_unavailable; #endif} objc2_unavailable;

These types of definitions, a class is completely decomposed, the class definition or each part of the object is abstracted into a type, the operation of a class properties and methods is very convenient. The attributes of the OBJC2_UNAVAILABLE tag are not supported by Ojective-c 2.0, but they can actually be obtained with the response function, for example: If you want to get the name property of the class, you can get it as follows:

Class Classperson = person.class;//printf ("%s\n", classperson->name); The name is not available in this way because Objc2_unavailableconst char *cname  = Class_getname (Classperson);p rintf ("%s", CNAME);//output: Person
2. Definition of functions

The method of manipulating an object is usually object_ preceded by a

A method that operates on a class is typically preceded by a class_

A method that operates on a method of a class or object is typically preceded by a method_

The methods for manipulating member variables are generally ivar_ preceded by

The method of manipulating a property is usually property_开头 preceded by a

The method of manipulating the protocol is usually protocol_ preceded by a

Depending on the prefix of the function above, you can get a general understanding of hierarchical relationships. For objc_ the beginning of the method, the runtime is the ultimate housekeeper, you can get the load information of the class in memory, the list of classes, associated objects and associated properties and other operations.

For example: Use Runtime to print a class that is loaded in the current app, and don't be surprised.

-(void) Touchesbegan: (Nsset *) touches withevent: (Uievent *) event {    unsigned int count = 0;    Class *classes = objc_copyclasslist (&count);    for (int i = 0; i < count; i++) {        const char *cname = Class_getname (Classes[i]);        printf ("%s\n", CNAME);}    }

-3. Technical points and application scenarios back to the top

3. Technical points and application scenarios

In the following code, the person class is used, and the person class knowledge simply defines a member variable and two attributes

@interface person:nsobject{    @private    float _height;} @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) int age; @end
3_1. Get property \ member Variable list

Back to the top for a list of member variables, you can use a function, and class_copyIvarList if you want to get a list of properties you can use class_copyPropertyList a function, use the following example:

-(void) Touchesbegan: (Nsset *) touches withevent: (Uievent *) event {        Class Classperson = nsclassfromstring (@ "person") ); The same as the following sentence, you can not import the header file//    Class clazz = person.class;    unsigned int count = 0;    Ivar *ivarlist = class_copyivarlist (Classperson, &count); Gets the member variable array    for (int i = 0; i < count; i++) {        const char *cname = Ivar_getname (Ivarlist[i]);//Gets the name of the member variable        N sstring *name = [NSString stringwithutf8string:cname];        NSLog (@ "%@", name);    }    NSLog (@ "-------------------Split line------------------");    objc_property_t *propertylist = class_copypropertylist (Classperson, &count); Gets an array of attributes for    (int i = 0; i < count; i++) {        const char *cname = Property_getname (Propertylist[i]);        NSString *name = [NSString stringwithutf8string:cname];        NSLog (@ "%@", name);}    }

The output of the above code is:

2015-06-05 22:28:16.194 Runtime Ultimate [4192:195757] _height2015-06-05 22:28:16.195 runtime Ultimate [4192:195757] _age2015-06-05 22:28:16.195 runtime Ultimate [4192:195757] _name2015-06-05 22:28:16.195 runtime Ultimate [4192:195757]------------------- Split Line------------------2015-06-05 22:28:16.195 Runtime Ultimate [4192:195757] name2015-06-05 22:28:16.195 Runtime Ultimate [ 4192:195757] Age

Why there is the above output, because @property will do three jobs:
1. Generate an underlined member variable
2. Generate the Get method for this member variable
3. Generate the Set method for this member variable
As a result, three member variables _height, _age, and _name are exported. It is important to note that the attribute name is not underlined, as defined by the name. So it can be said that ivarlist can get the attributes defined by the @property keyword, and propertylist cannot get to the member variable. That is, using ivarlist, you can get all the member variables and attributes.

This happens when the property is ReadOnly and the getter is overridden, such as when a property is a computed property and depends on the value of the other property to be computed. The resulting underlined member variable is not available, and the property cannot be obtained by ivarlist. So when you have this value, you can't get all of the properties or variables, either using Ivarlist or using PropertyList.

Before you go on to the next topic: you need to figure out another question: is Didset+set good for a readonly property, or is it better to rewrite the getter?

Most of the readonly properties are computed and still depend on other properties, so you can use Didset+set, which is set in the set method of other properties. But Didset+set is sometimes completely unnecessary, does not conform to lazy loading rules, wastes computational power, and uses the method of rewriting getter better. So rewriting a getter is always a little better.

Return to the point: when KVC, want to get all the member variables and properties, how to do?

The first step is to understand setValue: forKeyPath: the underlying implementation of the method: take the name attribute as an example
1. First go to the class method list to find the wood has setName:, if there is, directly call [person Setname:value]
2. Find out if there is no underlined member variable _name, if there is _name = value;
3. Find the member variable name if there is a name = value;
4. If none are found, the error will be directly.
Therefore, for ReadOnly and rewrite the Getter properties: If the properties of the propertylist are used once KVC, will be an error, so in order to ensure that the code is normal, you can not use the PropertyList properties KVC;
In addition: This property is a computed type, why it is also assigned to it, so it is not unreasonable to kvc it.
This property is not available directly when using ivalist, so it is the best solution for KVC. Furthermore, you cannot get a member variable (_height) using PropertyList, and you cannot assign a value to a member variable. The use of ivalist is a member variable that can be assigned to that value.

These are the arguments for KVC using Ivar or using the property.

Outside the topic: Many classes some member variables are not exposed to external calls getter and no setter, just declared with @private: Why??
The guess is that it is the intermediate variable that is used when the method is called, because it is followed by an object, is not suitable for static statics, and because it is not used externally, there is no need to provide an external interface, but there may be several methods that require this amount and are not suitable for local variables, so this is defined.

In this case, if you want to not assign a value to this member variable, it can be improved in KVC, by Ivarlist, remove the member variables not in the propertylist, so that the above member variables are filtered out.

3_1_1. Applying 1:KVC dictionary to model

Back to Top

Get property \ member list an important application is to take the attribute \ member variable in the model one at a time, get the key in the dictionary according to its name, and then remove the value of this key in the dictionary and use the setValue: forKeyPath: method to set the values. Why this is not the way to use it setValuesForKeysWithDictionary: . Because setValuesForKeysWithDictionary: such a procedure is executed inside the method
Iterate through all the keys in the dictionary, one by one, and the following procedure for each key.
1. Remove the key,
2. Remove the value of key, or Dict[key], and assign the property \ member variable of the model directly
3. How to assign values to the properties of the model, using methods setValue:value forKeyPath:key to assign values, this method of the implementation of the process is mentioned earlier.

As a result, the key in the dictionary that is often encountered in development is more than a few times in the model, which this class is not key-value compliant for ‘xxx’ is usually explained by the fact that the key in the dictionary is more than the attribute \ member variable in the model. So when the properties in the model are longer than the dictionary, setValuesForKeysWithDictionary: will there be no bugs in use? Tested: When the extra attribute is an object data type, NULL, when the property is the base data type, there is a system default value (such as int 0).

Therefore, KVC is performed using a method that assigns a value to a property individually:

Class clazz = person.class;unsigned int count = 0; Person *person = [[Person alloc]init]; Nsdictionary *dict = @{@ "name": @ "Zhangsan", @ "age": @19, @ "height": @1.75};ivar *ivars = Class_copyivarlist (Clazz, & count);//NSLog (@ "%tu", count); 3for (int i = 0; i < count; i++) {    const char *cname = Ivar_getname (Ivars[i]);    NSString *name = [NSString stringwithutf8string:cname];    NSString *key = [name substringfromindex:1]; Remove ' _ '    [person Setvalue:dict[key] forkey:key];} NSLog (@ "%@", person); The description method has been rewritten

The output is:

<person, 0x7ff15b80f230>{name = Zhangsan, height = 1.750000, age = 19}

Using this method for KVC, even if there are many keys in the dictionary will not have a bug, but the new problem arises, if the model is more than the number of keys in the dictionary will be a bug and: If more than the object type is not a bug, the value of the property is null, if it is a basic data type error could not set nil as the value for the key ‘xxx’. For example, change the dictionary above to read:

Nsdictionary *dict = @{@ "Age": @19, @ "height": @1.75}; Removed the name NSString type

After modification, the output is:

<person, 0x7f996263fbd0>{name = (NULL), height = 1.750000, age = 19}

If you change the dictionary to:

Nsdictionary *dict = @{@ "name": @ "Zhangsan", @ "age": @19}; The height float type is removed

Program crashes directly.

How to fix the above bug: You can setValue:value forKeyPath:key do the following before the method call: Take out the type of the property, and if the type is the base data type, value is replaced with the default value (such as int corresponds to the default value of 0).

The function provided by runtime ivar_getTypeEncoding can get the type of the property, and the return value represents the following meaning:


The height is the float type corresponding to the TypeCode is "F" so can be filtered, code changes as follows:

Class clazz = person.class;unsigned int count = 0; Person *person = [[Person alloc]init]; Nsdictionary *dict = @{@ "name": @ "Zhangsan", @ "age": @19, @ "height": @1.75};ivar *ivars = Class_copyivarlist (Clazz, & count); for (int i = 0; i < count; i++) {    const char *cname = Ivar_getname (Ivars[i]);    NSString *name = [NSString stringwithutf8string:cname];    NSString *key = [name substringfromindex:1];        const char *coding = ivar_gettypeencoding (Ivars[i]); Get type    nsstring *strcode = [NSString stringwithutf8string:coding];    ID value = Dict[key];    if ([Strcode isequaltostring:@ "F"]) {//Determines whether the type is float        value = @ (0.0);    }        [Person Setvalue:value Forkey:key];} NSLog (@ "%@", person);

This will perform normally and the output is:

<person, 0x7fc75d004a00>{name = Zhangsan, height = 0.000000, age = 19}
3_1_2. Apply 2:nscoding Archive and file

Back to top Get properties \ member list Another important application is to archive and reconcile files, the principle of which is basically the same as the above KVC, here just show some code:

-(void) Encodewithcoder: (Nscoder *) acoder {unsigned int count = 0;    Ivar *ivars = class_copyivarlist (Self.class, &count);        for (int i = 0; i < count; i++) {const char *cname = Ivar_getname (Ivars[i]);        NSString *name = [NSString stringwithutf8string:cname];                NSString *key = [name substringfromindex:1]; ID value = [self valueforkey:key]; Remove the value corresponding to the key [Acoder Encodeobject:value Forkey:key];        Encoding}}-(ID) Initwithcoder: (Nscoder *) Adecoder {if (self = [super init]) {unsigned int count = 0;        Ivar *ivars = class_copyivarlist (Self.class, &count);            for (int i = 0; i < count; i++) {const char *cname = Ivar_getname (Ivars[i]);            NSString *name = [NSString stringwithutf8string:cname];                        NSString *key = [name substringfromindex:1]; ID value = [adecoder Decodeobjectforkey:key]; decode [self setvalue:value forkey:key];  Set key corresponding to value}  } return self; }
3_2. Exchange Method implementation

Back to Top

The requirement scenario for an Exchange method implementation: You create a functional method that is referenced multiple times in a project, and when the requirements of the project change, another function is used instead of the old project (that is, the implementation of the original method is not changed).

You can write a new method (that meets the new requirements) in the class classification, and then swap the implementations of the two methods. This completes the project improvement without changing the code of the project, but only adding the new code.

The implementation of the exchange two methods is generally written in the load method of the class, because the load method is loaded once before the program runs, and the Initialize method is called when the class or subclass is first used, and is called multiple times when there is a classification.

When the program is run, call + (void) load{//If it is a class method, using Class_getclassmethod, if the object method is using Class_getinstancemethod    MethodOne = Class_getinstancemethod (self, @selector (methodone:));    Method methodtwo = Class_getinstancemethod (self, @selector (methodtwo:));    Exchange two methods of implementation    Method_exchangeimplementations (MethodOne, methodtwo);}

Note that the
1. The parameters of the two methods that can be exchanged must match, and the parameters are of the same type.
2. If you want to call method one inside method one, you should call it in the inside of the method, and actually call both, otherwise it will cause a dead loop.
For example:

Pre-swap-(NSString *) MethodOne: (NSString *) Str{nslog (@ "%@", [self methodtwo:str]); return "suc";} After swapping in the implementation of the method, be aware that the place where you will call both, and replace it with your own name-(NSString *) MethodOne: (NSString *) Str{nslog (@ "%@", [self methodone:str]); return " Suc ";}

Either method has two important properties: The SEL is the number of the method, IMP is the implementation of the method, and the method's invocation process actually goes to the SEL to find the Imp.
In this example, it is assumed that the SEL is methodone before swapping: The method pointing to Imp1,sel as Methodtwo points to IMP2.
The switching implementation is actually the point at which the method number is exchanged at the bottom, that is, MethodOne: pointing to Imp2,methodtwo pointing to IMP1.

Application scenario 3_3. class \ object's associated object

Back to the top the associated object is not adding a property or member variable for a class \ object (because it cannot be obtained by ivarlist or propertylist after setting the association), but instead adds a related object to the class, typically for storing class information, such as an array of property lists for the storage class. Convenient for future dictionary-to-model transfer. For example, the name of the property is stored in an array to set the association

Const char* Propertieskey = "Propertieskey"; unsigned int count = 0;ivar *ivars = class_copyivarlist ([Person class], &c Ount); Nsmutablearray *arraym = [Nsmutablearray arraywithcapacity:count];for (unsigned int i = 0; i < count; ++i) {    Ivar P ty = ivars[i];    const char *cname = Ivar_getname (Ivars[i]);    NSString *name = [NSString stringwithutf8string:cname];    NSString *key = [name substringfromindex:1]; Remove _    [Arraym Addobject:key];} Free (ivars); Objc_setassociatedobject (self, propertieskey, Arraym, objc_association_copy_nonatomic); NSLog (@ "%@", Arraym);

Output is

(age    ,    height,    name)

objc_setAssociatedObjectParameter interpretation of the method:

First parameter ID object, current object
The second parameter is the const void *key, the associated key, which is the C string
The third parameter ID value, the object being associated
The fourth parameter objc_associationpolicy the rules that the Policy association refers to, with the following values:

enum {   objc_association_assign = 0,   objc_association_retain_nonatomic = 1,   objc_association_copy_ Nonatomic = 3,   objc_association_retain = 01401,   objc_association_copy = 01403};

If you want to get an already associated object, you can get it by key

Nsarray *plist = objc_getassociatedobject (person, propertieskey);

You can encapsulate the above two operations, add a properties class method to the person class, and encapsulate the above operation for easy access to the class's property list.

Const char* Propertieskey = "Propertieskey"; @implementation person+ (Nsarray *) Properties {    //If it is already associated, Takes the associated object out of the key and returns    Nsarray *plist = Objc_getassociatedobject (self, propertieskey);    if (pList! = nil) {        return pList;    }    If there is no association, the associated object is set and the object is returned    unsigned int count = 0;    Ivar *ivars = Class_copyivarlist ([self class], &count);        Nsmutablearray *arraym = [Nsmutablearray arraywithcapacity:count];        for (unsigned int i = 0; i < count; ++i) {        Ivar pty = ivars[i];        const char *cname = Ivar_getname (Ivars[i]);        NSString *name = [NSString stringwithutf8string:cname];        NSString *key = [name substringfromindex:1];        [Arraym Addobject:key];    }    Free (ivars);    Objc_setassociatedobject (self, propertieskey, Arraym, objc_association_copy_nonatomic);    return arraym.copy;} @end
3_4. Dynamically adding methods to intercept non-implemented methods

Back to the top every class has a two-class method (from NSObject)
+ (BOOL)resolveClassMethod:(SEL)sel 
+ (BOOL)resolveInstanceMethod:(SEL)sel
The above two one is used for the class method, one for the object method. When you call a method that is not implemented in code, the method that the SEL identifies does not implement will now call one of these two methods (if the class method calls the first, if the object method calls the second) intercept. It is common practice to specify the SEL corresponding IMP within the resolve to complete the dynamic creation and invocation of two procedures for the method, or to return directly without specifying the IMP print error message.

If the method is not-sayhi in the person class, if the object P uses [P Performselector: @selector (Sayhi) Withobject:nil], then it will have to go through the method of the person class resolveInstanceMethod:(SEL)sel , The implementation is specified here for-sayhi.

VOID ABC (ID self, SEL _cmd) {    NSLog (@ "%@ said hello", [self name]);} @implementation person//Dynamic Add Method: Add the appropriate method in the resolve, notice whether the class method or the object method. + (BOOL) Resolveinstancemethod: (SEL) sel{    if ([Nsstringfromselector (SEL) isequaltostring:@ "Sayhi"]) {        Class_ Addmethod (self, SEL, ABC, "[Email protected]:"); Specifies the implementation for the SEL as ABC    }    return YES; @end

Description of the first two parameters of the implementation (ABC)

Each method has a default of two parameters, called an implicit parameter.
ID type self (representing class or object) and Sel type _cmd (method number)

class_addMethodThe meaning of the function parameter:

First parameter class CLS, type
Second parameter sel name, parsed method
The third parameter, IMP IMP, specifies the implementation
The fourth parameter is const char *types, the type of the method, the CodeType of the specific reference type, but one thing to note: Since the function must takes at least two arguments-self and _cmd, The second and third characters must be ' @: ' (the first character is the return type).: Because the function must have at least two arguments of self and _cmd, the second and third characters Must be "@:". If you want to add more parameters, you can start with the third parameter of the implementation and see the example below.
Return value: YES If the method is found and added to the receiver, otherwise NO.

Adding parameters to the implementation of the-sayhi method

When called:

Person *p = [[Person alloc] init];    P.name = @ "Zhangsan";p. Age = 10; [P Performselector: @selector (sayhi:) withobject:@ "World"];//Add one parameter, more colons

Modified the code in the Person class

VOID ABC (ID self, SEL _cmd, NSString *content) {//added a parameter to content    NSLog (@ "%@ said hello%@", [self name], content);} @implementation person//Dynamic Add Method: Add the appropriate method in the resolve, notice whether the class method or the object method. + (BOOL) Resolveinstancemethod: (SEL) sel{    if ([Nsstringfromselector (SEL) isequaltostring:@ "Sayhi:"]) {        class _addmethod (self, SEL, ABC, "[Email protected]:@"); Added an object type parameter added @    }    return YES; @end

The output is:

Zhangsan said HelloWorld.
3_5. Dynamically creating a class

Go back to the top dynamically create a class, add member variables and methods to the class, and create objects of this type:

#import "ViewController.h" #import <objc/runtime.h> #import <objc/message.h> #import "Person.h" static void Printschool (id self, SEL _cmd) {NSLog (@ "My school is%@", [Self valueforkey:@ "schoolname"]);} @implementation viewcontroller-(void) Touchesbegan: (Nsset *) touches withevent: (Uievent *) Event {Class classstudent = O        Bjc_allocateclasspair (Person.class, "Student", 0);        Add a NSString variable, the fourth parameter is the way, the fifth parameter is the parameter type if (Class_addivar (classstudent, "Schoolname", sizeof (NSString *), 0, "@")) {    NSLog (@ "Add Member variable schoolname success"); }//Add a method to the student class "[email protected]:" This is shown in the parameter type connection if (Class_addmethod (Classstudent, @selector (Printschool)    , (IMP) Printschool, "[email protected]:") {NSLog (@ "Add method Printschool: Success"); }//Register this class into the runtime system and you can use him. Objc_registerclasspair (classstudent);    return void//use created class id student = [[Classstudent alloc] init];    NSString *schoolname = @ "Tsinghua University"; Assign a value to the variable you just added//object_setinstancevariable (Student, "Schoolname", (void *) &str); [Student Setvalue:schoolname forkey:@ "Schoolname"] is not allowed under arc; Call the Printschool method, that is, to send student this recipient Printschool: This message//Objc_msgsend (student, "Printschool"); I tried to invoke this method but did not succeed [student Performselector: @selector (Printschool) Withobject:nil]; Dynamic invocation of a method that is not explicitly declared in a class} @end

The result of the output is:

Add member variable Schoolname successfully added method Printschool My school is Tsinghua University




Http://www.bkjia.com/IOSjc/1012702.html

Ios-runtime knowledge points, Ios-runtime knowledge points

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.