IOS – Runtime detailed analysis and Application

Preface

Operation can be said to be a core part of the Objective-C language, see many blogs, also saw several source repeatedly (Apple open source runtime source code), for this article on blog more, it is necessary to write this article? Questioned! Because technical articles can not be avoided. Colleagues let me think of a word is necessary, to awe, to understand the work for a year with a string of three years are not the same, all decided to write this article, because it really is time to this understanding is different, this article will be recorded at the moment of understanding with the view, if there is any mistake. If you feel welcome, to help you learn, help a wave of attention to love every.

The first Runtime operation

Runtime runtime. It is the system of the operation of some of the mechanisms, the main embodiment of the message mechanism of the object. Read the article way of thinking: after reading the above sentence you should have three key words in your mind when running, OC objects, messages sent. This article is around the three key words of thinking and elaboration.

  • Objective-C is a dynamic language which is based on the C language, which includes the object-oriented characteristics (object oriented through the entire operating system, OC is a heavy object language) and the message forwarding mechanism (message distribution)
  • How does Objective-C have the ability to target? The object in OC is represented by the structure of C language, and the method is realized by C function.
  • Object oriented features and Objective-C message forwarding mechanism dynamically determines its not only need a compiler (on the question of the process of compiling), but also the need for the runtime system to dynamically create classes and objects, and message sending and forwarding.
  • The specific code to feel under the above words meaning, in a VC controller to the custom NSObject inherited from the CCSomeClass class is instantiated, and then call the – (void) sendMessage: (NSString * STR) method:
- (void) viewDidLoad {[super viewDidLoad]; CCSomeClass *someClass = [[CCSomeClass init]; [someClass sendMessage:@ "zerocc"];}

The following code implementation is equivalent to the above implementation

- (void) viewDidLoad viewDidLoad] CCSomeClass {[super; *someClass = objc_msgSend ([CCSomeClass, class], sel_registerName ("alloc"); someClass = objc_msgSend (someClass), sel_registerName ("init")); SEL sel = sel_registerName ("sendMessage:"); objc_msgSend (someClass, SEL, @ "zerocc");}

Classes and objects in Objective-C

Concepts of classes and objects

  • Class is an abstraction of the same kind of thing, which defines the static attributes (properties) and dynamic attributes (Methods) that this class of objects should have.
  • Object is an instance of the class, and the creation of the object is the instantiation of the class.
  • Can be understood as a class is a data type, its variable is the object.

What are the classes in Objective-C?

  • In the Objective-C class, all classes are NSObject (NSProxy aside without considering the sub class;) that NSObject is the root of class Objective-C inherited the other classes inherit from NSObject a basic interface to the Objective-C runtime system. Examples of these classes are inherited from the NSObject and get the most basic characteristics of Objective-C – NSObject characteristics.
  • NSObject.H in the class definition (Interface) file, we can see the following code:
@interface NSObject < NSObject> {Class isa OBJC_ISA_AVAILABILITY;}

Each NSObject class has a Class class as a member variable, and the root class only such a isa property;

  • Class isa what is it? Into the objc.h file, see the following:
An opaque type that represents / / / an Objective-C class. typedef struct objc_class *Class;

1 Class is a pointer variable
structure objc_class type 2 typedef key role is to point to the objc_class pointer type structure the alias
3 Class ISA is equivalent to struct objc_class *isa, so from here that isa is a pointer is a pointer to the objc_class structure variables.

  • Objc_class type structure is specific to the runtime.h file, see its definition:
Struct objc_class {Class isa OBJC_ISA_AVAILABILITY; #if __OBJC2__ Class super_class! OBJC2_UNAVAILABLE; / / parent class refers to the parent class const char *name OBJC2_UNAVAILABLE long version OBJC2_UNAVAILABLE; / / class name; / / version information category, the default is 0 long info OBJC2_UNAVAILABLE; / / information for runtime use some identification long instance_size OBJC2_UNAVAILABLE; / / this the size of the struct objc_ivar_list *ivars instance variable OBJC2_UNAVAILABLE; / / the member variables of a class list Struct objc_method_list **methodLists OBJC2_UNAVAILABLE; / / objc_cache *cache method definition list struct OBJC2_UNAVAILABLE struct objc_protocol_list *protocols; / / method cache OBJC2_UNAVAILABLE protocol; / / #endif} list OBJC2_UNAVAILABLE; / * Use Class` instead of `struct objc_class *` ` * /

Objc_class structure describes all the information of a class: class, name, information, variable size, variable list, the list of methods, the protocol list and so on; pay special attention to the objc_class structure in the first data is pointing to another Class isa pointer (metaclass analysis surface), all objects are a ISA in memory. Generally speaking, the memory address of the ISA pointer stores the information (class information).

Objc_ivar_list member variable list structure:
Struct objc_ivar_list ivar_count #ifdef __LP64__ {int OBJC2_UNAVAILABLE; int space OBJC2_UNAVAILABLE; #endif variable length structure / struct objc_ivar / ivar_list[1] OBJC2_UNAVAILABLE OBJC2_UNAVAILABLE;};
Objc_method_list method list structure:
Struct objc_method_list objc_method_list *obsolete {struct OBJC2_UNAVAILABLE; int method_count OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif variable length structure struct objc_method / * * / method_list[1] OBJC2_UNAVAILABLE;}

**methodLists pointer to the pointer, you can dynamically modify the value of *methodLists to add the member method, the same can not explain the reasons for adding Category attribute, two pointer

A list of methods in the objc_method list, which stores the method name, method type, and method implementation
Struct objc_method method_name char *method_types {SEL OBJC2_UNAVAILABLE; OBJC2_UNAVAILABLE IMP; method_imp OBJC2_UNAVAILABLE;}

1 method name is SEL, the previously mentioned methods with the same name defined even in different groups, they are also the same method selector.
2 methods method_types type is a char pointer, in fact, a storage method of parameter types and return types.
3 method_imp points to the implementation of the method, is essentially a function pointer will be detailed later

A pointer to the function / / / of a method implementation. #if! OBJC_OLD_DISPATCH_PROTOTYPES typedef void (*IMP) (void / ID, SEL, #else, typedef... * /); ID (*IMP) (ID, SEL, #endif...);

It is a function pointer, which is generated by the compiler. When you start a ObjC message, the code that it will execute is specified by the function pointer. The IMP pointer to the function of this method to achieve. Now that we have an entry to execute an instance of an instance, we can bypass the message passing phase and execute the method directly, which will be mentioned later. You will find the
method IMP points and objc_msgSend function parameters are the same type, including ID and SEL type. Each method name selector corresponds to a SEL type, and SEL method corresponding to each instance object in the realization is the only sure, by a group of ID and SEL parameters can be determined only method to realize the address and vice versa.
each class has a list of methods, mapping kept the name and the method of implementation of selector. IMP is a function pointer, pointing to the specific implementation of Method, the relationship between the SEL and IMP address:
method and call the method to obtain the IMP address to avoid message binding and direct access method. This approach is rarely used, unless it is necessary to continue to repeat a large number of calls to the extreme situation of a method, to avoid sending a flood of messages directly to the method will be more efficient. NSObject class has a methodForSelector: instance method, you can use it to get a method selector corresponding to the IMP, for example:

Void (*setter) (ID, SEL, BOOL); int i; setter = (void (*) (ID, SEL, BOOL) [target) methodForSelector:@selector (setFilled:)]; for (I = 0; I 1000; < i++ setter (targetList[i]), @selector (setFilled:), YES);
Objc_cache method cache
Struct objc_cache unsigned int mask {/ * total = mask + 1 * OBJC2_UNAVAILABLE; unsigned int occupied OBJC2_UNAVAILABLE; Method buckets[1] OBJC2_UNAVAILABLE;};

The performance of Cache method calls for optimization, tongsudejiang, whenever the object receives a message, it will not directly search method traversal method in the ISA class to the list in response to the news, because it is too inefficient, but find the priority in Cache. The Runtime system will take the methods to be called to the Cache (theoretically a method if called, so it is possible in the future will be called method_name) as key, method_imp as value to save the next search when higher efficiency. The principle of the computer composition of the CPU bypass the main memory of the first visit to the Cache of the truth, like -&gt (CACHE) -&gt memory -> virtual disk.

The introduction of the terms involved in the dynamic invocation of Objective-C method

OC only at compile time to determine the message recipient to send message, and receive will be how to respond to this message, it depends on what happens to the operation decision. Dynamic call by sending messages:

[obj doSomething] / / runtime compile time will be converted into objc_msgSend (obj, @selector (doSomething));

That is to say, the objc_msgSend function is equivalent to the entry; the object calls a method that will be converted to the compiler:

ID objc_msgSend (ID self, SEL OP,...); / / objc_msgSend (obj, selector, arg1, arg2,...)

If the message recipient can find the corresponding selector, then the equivalent direct method performs a specific recipient of this object; otherwise, the message is either forwarded or temporary content to the receiver dynamically add this selector corresponding, or crash.
will begin to introduce some terms and their corresponding data structures.

ID

  • The first parameter ID type parameter of the objc_msgSend method, see the objc.h file: Represents an instance of a class. x struct objc_object isa {Class OBJC_ISA_AVAILABILITY;}; / / / A pointer to an instance of a class. typedef struct objc_object *id; the ID is a pointer variable objc_object structure to objc_object structure body on behalf of a class the example contains a pointer to a objc_class structure pointer (have analyzed above Class ISA); summarize the ID as a pointer to an instance of a class, which he can point to any object

SEL

  • The first parameter SEL type parameter of the objc_msgSend method, see the objc.h file: An opaque type that represents / / / a method selector. typedef struct objc_selector *SEL; SEL selector is represented in Objc type, selector is the method selector SEL can be understood as the procedure number, it is mapped to the C string method, objc.h file struct: objc_method {SEL method_name; char *method_types; IMP method_imp;}; Objc compiler can command (@selector) sel_registerName function or Runtime system to obtain a SEL type method selector. The corresponding method of the same name selectors in different classes are the same, even if the same name and different types of variables can lead them with the same selector method, and Objc method named sometimes with parameter type (NSNumber a pile of abstract factory method), there are a lot of long… Cocoa.

Is the argument in the method

Objc_msgSend (receiver, selector, arg1, arg2,...) such as someClass / object to send - (void) sendMessage: (NSString * STR); objc_msgSend (someClass, @selector ("sendMessage:"), @ "zerocc"); / /

Objective-C message sending process

There are two ways to call a method:

  • The [object method is called message], if an object is not according to the normal procedure to accept a message, will start the so-called “message (message forwarding)” mechanism, through this mechanism, we can tell how to deal with the unknown message object. By default, the object receives the unknown message, will cause the program to crash, through the console, we can see the following information: the abnormal abnormal information is actually thrown by doesNotRecognizeSelector NSObject. However, we can take some measures to make our programs execute specific logic, and avoid the collapse of the program.
  • By perform… In the form of a call, it is necessary to wait until the runtime to determine whether the object can receive message messages. If not, the program crashes. Typically, when we can not determine whether an object can receive a message, the first call respondsToSelector: to judge. The following code shows: if ([self respondsToSelector:@selector (method)) {[self performSelector:@selector ())]}

But in the end, it was called objc_msgSend (receiver, selector, arg1, arg2,…), the function of the dynamic binding of all things:

  1. Detection of this selector is not to be ignored. For example, the development of OS X Mac, with a garbage collection will ignore retain, release these functions.
  2. Detection of this target is not a nil object. The feature of ObjC is to allow a nil object to execute any method that will not be Crash because it will be ignored.
  3. All of the above are detected through the start of this class of IMP, from the inside of the cache to find, and then get to jump to the corresponding function to perform.
  4. If the cache is not found, obtaining the structure to the class body through a isa pointer, then published inside the selector method in the search method (method is published: the class method in listing method_list, it will be the method selector and method to realize the link).
  5. If you can’t find a publication, the pointer to the parent class in the objc_msgSend structure finds the parent class, and finds the selector of the method in the distribution table of the parent class. It will always reach the NSObject class along the class’s inheritance system. Once positioned to selector, the function gets the entry point of the implementation, and the corresponding parameters are introduced to implement the method’s implementation, and the method is added to the cache. If you do not locate the last selector, the message forwarding process will go.

The Objective-C runtime will give you a three chance to save the program before the exception is thrown:

  • Dynamic analysis
  • Redirect receiver
  • Complete message forwarding

Dynamic analysis

When an object receives an unknown message, it will first call the +resolveInstanceMethod: method of the class that belongs to it (instance method) or +resolveClassMethod: (class method). In this method, we have the opportunity to add a new “processing method” (or function implementation) to the unknown message, which can be dynamically added to the class by the runtime class_addMethod function.

  • (BOOL) resolveInstanceMethod: (SEL) sel; analytic case method
  • (BOOL) resolveClassMethod: (SEL) sel; analytical method
  • Through the class_addMethod (…) the way the lack of selector dynamic created, the premise is to achieve a good IMP (method_types)
  • If resolveInstanceMethod: the method returns NO, the runtime will proceed to the next step: message forwarding (Message Forwarding).
  • Use: This program is more prepared for the @dynamic attribute, Core Data effective use of this method, the attributes of the NSManagedObjects getter and setter is dynamically added at runtime.

Specific code examples:

/ / CCSomeClass.h / / CCRuntime / / Created / / by zerocc on 2017/4/25. zerocc. All rights / / Copyright 2017 reserved. #import < Foundation/Foundation.h> @interface; CCSomeClass: NSObject - resolveMethod @end (void); / / CCSomeClass.m / / CCRuntime / / Created / / by zerocc on 2017/4/25. zerocc. All rights / / Copyright of 2017 reserved. #import "CCSomeClass.h" #import < objc/runtime.h> @implementation CCSomeClass void addResolveMethod (ID obj, SEL _cmd) {NSLog (@ "resolveMethod was called");} + (BOOL) resolveInstanceMethod: (SEL SEL) {if (SEL = = @selector (resolveMethod)) {class_addMethod ([self class], SEL, addResolveMethod (IMP), "v@"); return YES;} return [super resolveInstanceMetho D:sel];} @end

Redirect receiver

If the previous dynamic method resolution is not processed, runtime will call the following methods

  • (ID) forwardingTargetForSelector: (SEL) aSelector;
  • If the method returns an object that is not nil, then the object is used as a new message recipient
    cannot return self, an infinite loop will appear
  • If you do not know what to return, you should use [super forwardingTargetForSelector:aSelector];
  • This method is a simple forwarding, can not deal with the parameters and return values of the message
  • This step is suitable for us to forward the message to another object that can handle the message. However, this step is unable to process the message, such as the parameters and return values.

Specific code examples:

/ / CCSomeClass.h / / CCRuntime / / Created / / by zerocc on 2017/4/25. zerocc. All rights / / Copyright 2017 reserved. #import < Foundation/Foundation.h> @interface; CCSomeClass: NSObject - (void) forwardMethod_arrayWithString: (NSString * STR); / / CCSomeClass.m / / @end / / CCRuntime / / Created by zerocc on 2017/4/25. zerocc. All rights / / Copyright of 2017 reserved. #import "CCSomeClass.h" #import < objc/runtime.h> #import "CCOtherClass.h" @implementation CCSomeClass pragma mark # - receiver - (ID) forwardingTargetForSelector: redirect (SEL aSelector) {/ / acquisition method NSString *selectorString = NSStringFromSelector (aSelector); / / according to the method name to add the if ([selectorString isEqualToString:@ fo " RwardMethod_arrayWithString: "] {CCOtherClass *otherClass = alloc] init]; return otherClass;} return forwardingTargetForSelector:aSelector] [super;} @end

The spare receiver in this case is a custom CCOtherclass, the specific code:

/ / CCOtherClass.h / / CCRuntime / / Created / / by zerocc on 2017/4/25. zerocc. All rights / / Copyright 2017 reserved. #import < Foundation/Foundation.h> @interface; CCOtherClass: NSObject @end / / CCOtherClass.m / / CCRuntime / / Created / / by zerocc on 2017/4/25. zerocc. All rights / / Copyright of 2017 reserved. #import "CCOtherClass.h" @implementation CCOtherClass / * * * * * for converting a string array @param STR need to convert the string * * @return * / - converted array (NSArray *) forwardMethod_arrayWithString: (NSString * STR) {if (STR & & (STR! = NULL) & & ([str isKindOfClass:[NSNull class]] &!); & str.length > 0) {NSMutableArray *mArr = [NSMutableArray arrayWithCapacit Y:1]; for (NSInteger index = 0; index < str.length; index++) {[mArr addObject:[str substringWithRange:NSMakeRange (index, 1) ";} NSLog (@ array::% @", mArr); return mArr; return nil @end}};

Complete message forwarding

  • The first step of rewriting method for message forwarding: – (NSMethodSignature) methodSignatureForSelector: (SEL) aSelector; for the implementation of the selector method does not provide the appropriate method signature signature method and then return to the aSelector.
  • When the object sends a unrecognized message, will use the information obtained from the above method signature method to create a message of the NSInvocation object, all the details and pending the package in anInvocation, including selector, target (target) and parameters.
  • And then – (void) forwardInvocation: (NSInvocation *) anInvocation; method, in the method and by – (void) invokeWithTarget: (ID) target; method to choose to forward the message to the specified object. The implementation of the forwardInvocation: method has two tasks: locating the objects that can respond to messages encapsulated in anInvocation. This object does not need to handle all unknown messages. Use anInvocation as a parameter to send the message to the selected object. AnInvocation will retain the result of the call, which will be extracted by the runtime system and sent to the original sender of the message.

Specific code example analysis:

/ / CCSomeClass.h / / CCRuntime / / Created / / by zerocc on 2017/4/25. zerocc. All rights / / Copyright 2017 reserved. #import < Foundation/Foundation.h> @interface; CCSomeClass: usualMethod; NSObject - (void) - (void) resolveMethod; (void) - forwardMethod_arrayWithString: (NSString * STR); - (void) signatureMethod_inverseWithString: (NSString * str); @end #import "CCSomeClass.h" #import < objc/runtime.h> #import "CCOtherClass.h" @implementation CCSomeClass #pragma mark - complete message forwarding / must override this method and provide a suitable method signature for a given selector. / / signature method returns aSelector - (NSMethodSignature) methodSignatureForSelector: (SEL) aSelector *signature methodSignatureForSelector:aSelector] {NSMethodSignature = [super; if (! Signature) {if ([CCOtherClass instancesRespondToSelector:aSelector]) {/ / signature = [CCOtherClass instanceMethodSignatureForSelector:aSelector] method to obtain the signature;}}} / / return signature; when the self sends a unrecoginzed message, will create a NSInvocation, and this method is called. Allow in this method, through the [anInvocation invokeWithTarget:otherSelf]; way of message forwarding. - (void) forwardInvocation: (NSInvocation * anInvocation) {//anInvocation choice of forwarding the message to other object if ([CCOtherClass instancesRespondToSelector:anInvocation.selector]) {[anInvocation invokeWithTarget:[[CCOtherClass alloc] init]] @end;}}
/ / CCOtherClass.h / / CCRuntime / / Created / / by zerocc on 2017/4/25. Copyright zerocc. All rights of 2017 / / reserved. / / #import < Foundation/Foundation.h> @interface; CCOtherClass: NSObject @end / / CCOtherClass.m / / CCRuntime / / Created / / by zerocc on 2017/4/25. zerocc. All rights / / Copyright of 2017 reserved. #import "CCOtherClass.h" @implementation CCOtherClass / * * * * * @param STR inverse string to reverse the string replacement string @return * * * / - (NSString *) signatureMethod_inverseWithString: (NSString * STR) {if (STR & & (STR! = NULL) & & ([str isKindOfClass:[NSNull class]] &!); & str.length > 0) {NSMutableString *mStr = [NSMutableString stringWithCapac Ity:1] for (NSInteger; index = str.length; index > 0; index--) {[mStr appendString:[str substringWithRange:NSMakeRange (index - 1, 1 ');} NSLog (@ retureStr::% @ ", mStr); return mStr; return nil @end}};

The class and object operation functions involved in the runtime system

  • Attribute dependent operation function
CCMyClass *myClass = [[CCMyClass alloc] init]; unsigned int outCount = 0; Class = CLS [myClass class] objc_property_t * properties; / / attribute operation = class_copyPropertyList (CLS, & outCount); for (int i = 0; I < outCount; i++) {objc_property_t property = properties[i]; const = char *propertyName property_getName (property); NSLog (@ "property's name:%s", propertyName);} free (properties);
Name / instance member variables obtain class member variable operation function / / the information specified in the Ivar class_getInstanceVariable (Class CLS, const char *name); / / get the class member variable information Ivar class_getClassVariable (Class CLS, const char *name); / / BOOL class_addIvar Class (add member variables CLS, const char *name, size_t size, uint8_t alignment const char, *types); / / this can only add member variables to create / / when runtime class member variables to obtain the entire list of Ivar * class_copyIvarList (Class CLS, unsigned int *outCount (free); / / must use the name of the instance variable information member) Ivar class_getInstanceVariable to release the array / attribute operation function / class access (Class CLS const char, named *name); / / get the class member variable information Ivar class_getClassVariable (Class CLS, const char *name); / / BOOL class_addIvar (Class add member variables CLS, const char *name, size_t size, uint8_t alignment, const char *types); / / get the entire member variable list of Ivar * class_copyIvarList (Class CLS, unsigned int *outCount);
  • Method dependent operation function
Gets a list of methods / / Method *methods = class_copyMethodList (CLS, & outCount); for (int i = 0; I < outCount; i++) {Method method = methods[i]; SEL = method_name method_getName (method); NSLog ("method's signature - method_name: @%s, method_name (free);} methods);
Add member variables and / / / / is different to add a method for dynamic. If you have the same name will return NO, modify it requires the use of method_setImplementation BOOL class_addMethod (Class CLS, SEL name, IMP imp, const char *types); / / implementation to replace the original method (substitution) IMP (Class class_replaceMethod CLS, SEL name, IMP imp, const char *types); / / void method_exchangeImplementations two exchange method (Method M1, Method m2); / / IMP implementation method (Class class_getMethodImplementation returns CLS, SEL name); IMP class_getMethodImplementation_stret (Class CLS, SEL name); / / class instance whether the specified selector BOOL class_respondsToSelector response (Class CLS, SEL sel);
  • Protocol dependent operation function
Add the BOOL class_addProtocol protocol (Class / CLS, Protocol *protocol); / / return class BOOL protocol specified by class_conformsToProtocol (Class CLS, Protocol *protocol); / / Protocol protocol list * returns the class implementation of the class_copyProtocolList (Class CLS, unsigned int *outCount);

The specific application of Runtime black magic

  • Can get all member variables of a class
  • Access to all attributes of a class
  • Get all the methods of a class
  • Exchange method implementation
  • Can dynamically add a member variable
  • Can dynamically add a property
  • Dictionary transfer model
  • Runtime archive / anti archiving

Exchange method

  • Development of the use of the scene: the system comes with the function of the method is not enough to bring the system to extend the function of some of the methods, and maintain the original function.
  • Method one: to inherit the system class, rewrite method
  • Mode two: use runtime, exchange method
@implementation ViewController - (void) viewDidLoad {[super viewDidLoad]; Do any additional setup after loading / the view, typically from a nib. / / demand: provide functionality to imageNamed method, each loading picture judgment whether the image is loaded successfully. The first step: / / make a classification, the definition of a method can load pictures and print + (instancetype) imageWithName: (NSString * name); / / step two: to achieve the exchange of imageNamed and imageWithName, you can call imageWithName, indirect call imageWithName. UIImage *image = [UIImage imageNamed:@ 123];} @end @implementation UIImage (Image) / / when loading classification to the memory of the call + load (void) {/ / / / exchange method to obtain imageWithName method Method imageWithName address = class_getClassMethod (self, @ selector (imageWithName:)); / / Method imageName method to obtain imageWithName address (self = class_getClassMethod @selector, (imageNamed:)); / / exchange address, equivalent exchange implementation of method_exchangeImplementations (imageWithName, imageName);} / / not in the classification of rewriting system imageNamed, because it will make the function of the system to cover, but not in super. / / call classification can add pictures and print + (instancetype) imageWithName: (NSString * name) {/ / this tune With imageWithName, the equivalent of imageName UIImage *image = [self call imageWithName:name]; if (image = = Nil) {NSLog (@ "loading empty picture");} return image;} @end

Dynamic addition method

  • Development of the use of the scene: if a class method is very much, when loading the class to the memory is also more expensive resources, the need to generate a mapping table for each method, you can use a dynamic class, add a solution.
  • Classic interview questions: there is no use of performSelector, in fact, I would like to ask you to have a dynamic method.

Code sample:

@implementation ViewController - (void) viewDidLoad {[super viewDidLoad]; Do any additional setup after loading / the view, typically from a nib. Person *p = [[Person alloc] init]; / / default person does not implement the eat method, can be called by performSelector, but will be in error. / / dynamic method will not add error [p performSelector:@selector (eat)];} @end @implementation Person / void ((*)) / / default methods have two implicit parameters, void eat (ID self, SEL SEL) {NSLog ("% @ @% @", self, NSStringFromSelector (SEL));} "when an object invokes a method not implemented, will call this method, and make a list of the corresponding method. Just came / / can be used to determine the method is not implemented, is not that we want to add dynamic method + (BOOL) resolveInstanceMethod: (SEL SEL) {if (SEL = = @selector (eat)) {/ / eat add dynamic method / / the first parameter to which class adding method of second parameters: / / number / / method adding method third parameters: the function of adding methods to achieve (function address) / / fourth parameters: the type of the function, (return value + v:void @ parameter type): object -> self: SEL-> _cmd; class_addMethod (self, @selector, eat (eat), "v@");} return [super resolveInstanceMethod:sel] @end;}

Add attributes to classification

  • Principle: to declare a class attribute, in fact, is to add to the association of this class, not directly to the value of the memory space to add to the class storage space.
@implementation ViewController - (void) viewDidLoad {[super viewDidLoad]; / / NSObject system to dynamically add name attribute NSObject *objc = [[NSObject alloc] init]; objc.name = "zerocc"; (@ NSLog @ "% @", objc.name);} / / @end key static const char definition of the associated *key = "name"; @implementation NSObject (Property (NSString) - * name) {/ / according to the relevance of key, obtain the associated value. Return objc_getAssociatedObject (self, key);} - (void) setName: (NSString * name) {/ / the first parameter: add Association / / the second parameters to which object: the associated key, third parameters are obtained through the key: / / value / / fourth related parameters: Strategy in relation to objc_setAssociatedObject (self, key name, OBJC_ASSOCIATION_RETAIN_NONATOMIC, @end);}

File reconciliation

  • Principle description: runtime provided by the function of all the properties of the Model itself, and the properties of encode and decode operations.
  • Kernel method: overriding the base class of Model:

If you want to achieve some of the basic data of data persistence (data persistance) or data sharing (data share). We can choose the file reconciliation. If the general method:

- (void) encodeWithCoder: (NSCoder * aCoder) {[aCoder encodeObject:self.name forKey:@ "nameKey"]; [aCoder encodeObject:self.gender forKey:@ [aCoder encodeObject:[NSNumber "genderKey"]; numberWithInteger:self.age] forKey:@ "ageKey"];}

Can also be achieved. However, if the entity class has a lot of member variables, this method is obviously powerless. This time, we can use the runtime to achieve fast archiving, file:

  • Let entity class follow < NSCoding> protocol. And in the.M file import header file < objc/runtime.h>.
  • Implement – (instancetype) initWithCoder: (NSCoder *) aDecoder; and (void) encodeWithCoder: (NSCoder *) aCoder; method.
- (instancetype) initWithCoder: (NSCoder * aDecoder) {self = [super init]; if (self) {/ / unsigned int count = 0; objc_property_t = *properties class_copyPropertyList ([self class], & count); for (int i = 0; I < count; I + +) {objc_property_t property = properties[i]; const char *propertyChar = property_getName (property); NSString *propertyString = [NSString stringWithUTF8String:propertyChar]; ID value = [aDecoder decodeObjectForKey:propertyString]; [self setValue:value forKey:propertyString];} free (properties return self);};} - (void) encodeWithCoder: (NSCoder * aCoder) {unsigned int count = 0; objc_prop Erty_t *properties class_copyPropertyList ([self = class], & count); for (int i = 0; I < count; I + +) {objc_property_t property = properties[i]; const = char *propertyChar property_getName (property); NSString * propertyString = [NSString stringWithUTF8String:propertyChar]; ID object = [self valueForKey:propertyString]; [aCoder encodeObject:object forKey:propertyString];} free (properties});

Or this way:

- (ID) initWithCoder: (NSCoder * aDecoder) {if (self = [super init]) {unsigned int outCount; Ivar * Ivars = class_copyIvarList ([self class], & outCount); for (int i = 0; I < outCount; I + +) {Ivar Ivar = ivars[i]; NSString = [NSString * key stringWithUTF8String:ivar_getName (Ivar)] [self setValue:[aDecoder decodeObjectForKey:key] forKey:key]; return self;}};} - (void) encodeWithCoder: (NSCoder * aCoder) {unsigned int outCount; Ivar * Ivars = class_copyIvarList ([self class], & outCount); for (int i = 0; I < outCount; I {Ivar = Ivar + +) ivars[i]; NSString * key = [NSString stringWithUTF8String:ivar_getName (Ivar)]; [aCoder encodeObject:[self valueForKey:key] forKey:key];}}

Universal jump controller

Application scenarios:

  • Push: according to the server push over the data rules, jump to the corresponding controller
  • List: a similar name, you can jump to different controllers, arbitrary jump
- (void) testRuntime NSDictionary {*userInfo = "class" @{@: @ @ "CCRuntimePushVC", "property", "ID" @{@: @ 81198, @ @ "type": "2"}}; [self push:userInfo];} / / jump - (void) push: (NSDictionary * params) {get / / *className = [NSString stringWithFormat:@ NSString class name "% @ params[@", "class" "; / / convert Class getClass = Class ([NSString stringWithFormat:@ NSClassFromString by the name"% @ ", className]); / / get the existence of this class if (getClass) {/ / create a class object ID creatClass [[getClass = alloc] in It]; NSDictionary *propertys params[@ = "property"]; [propertys enumerateKeysAndObjectsUsingBlock:^ (ID _Nonnull key, ID _Nonnull obj, BOOL * _Nonnull stop) {if ([self checkIsExistPropertyWithInstance:creatClass verifyPropertyName:key]) by KVC [creatClass setValue:obj / / {forKey:key]} assignment;}]; [self.navigationController pushViewController:creatClass animated:YES];}else{NSLog ("not this class can @ not push") whether the inspection object}}; / / the attribute - (BOOL) checkIsExistPropertyWithInstance: (ID) instance verifyPropertyName: (NSString * verifyPropertyName) {unsigned int outCount I; / / get the object attribute list ob Jc_property_t *properties class_copyPropertyList ([instance = class], & outCount); for (I = 0; I < outCount; i++) {objc_property_t property = properties[i]; / / attribute name is converted to a string of NSString *propertyName = [[NSString alloc initWithCString:property_getName encoding:NSUTF8StringEncoding] (property); / / judge whether the property of if ([propertyName isEqualToString:verifyPropertyName]) {free (properties); return YES;}} free (properties); return NO;} - (void) touchesBegan: (NSSet< UITouch *> touches withEvent: (* *) UIEvent event {[self testRuntime]});

Reference links

The Objc object
Objective-C
mental hospital this present life Wikipedia Objective-C runtime series
API
Apple’s official documents explain the official apple Objective-C runtime programming guide document