Third days of discharge of Objective-C of nerve hospital Runtime — how to use Runtime correctly

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

Preface

Today, finally to the hospital, to sum up the hospital a few days of harvest, talk about Runtime in the end what can bring benefits to our development. Of course, it is also a double-edged sword, improper use, it will become a big hole in the development of the road.

Catalog

  • The advantages of 1.Runtime (1) to achieve multiple inheritance (2) Method Multiple Inheritance Swizzling Aspect Oriented (3) Programming (4) Isa Swizzling (5) Associated Object (6) increase the associated object dynamic method (7) NSCoding automatic archiving and automatic solution file (8) dictionary and model conversion
  • Disadvantages of 2.Runtime

A realization of multiple inheritance Multiple Inheritance

Can forwardingTargetForSelector: method mentioned in an article on the inside of the know, a class can inherit from more than one class effect, only in this step, the message is forwarded to the correct class object can simulate multiple inheritance effect.

This example is recorded in the official document.

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

In the OC program can use the message forwarding mechanism to achieve the function of multiple inheritance. In the figure above, an object responds to a message, similar to the way the other object is borrowed or inherited. In the figure, the warrior instance forwarded a negotiate message to the Diplomat instance, implementation of the negotiate method in Diplomat, the result looks like negotiate warrior instance a Diplomat instance and the same, in fact, execution or Diplomat instance.

This allows different inheritance system branch of the two class methods can be “inherited” to each other, so that a class can inherit their response branches inside, but also can response to other unrelated classes send messages. In the above figure, Warrior and Diplomat have no inheritance relationship, but the Warrior will be forwarded to the negotiate Diplomat message, as if the Diplomat is the same as the Warrior class.

Message forwarding offers a number of features similar to multiple inheritance, but there is a big difference between them:

Multiple inheritance: combining different behavioral characteristics in a single object, will get a heavyweight multi level object.

Message forwarding: each of the functions is dispersed into different objects, and some of the lightweight objects, which are combined by message passing through the message.

It is worth noting that, even if we use forwarding messages to achieve a “false” inheritance, but the NSObject class will be divided between the two areas. Methods such as respondsToSelector: and isKindOfClass: will only consider the inheritance system and will not consider forwarding chain. For example, a Warrior object in the image above is asked if it can respond to the negotiate message:

If ([aWarrior respondsToSelector:@selector (negotiate))

The result is NO, although it can respond to negotiate messages without error, but it is forwarded to the Diplomat class to respond to the message.

If you want to create a false impression, reflecting the “false” inheritance, then you need to re implement respondsToSelector: and isKindOfClass: to join your forwarding algorithm:

- (BOOL) respondsToSelector: (SEL) aSelector ([super respondsToSelector:aSelector]) {if return YES; else test whether {/ * Here, the aSelector message can forwarded to another object * * be and whether that can respond to it. * * object Return YES if it can. return NO * /};}

In addition to respondsToSelector: and isKindOfClass:, instancesRespondToSelector: should also write a forwarding algorithm. If an agreement is used, conformsToProtocol: also needs to be rewritten. Similarly, if an object forwards any remote message it receives, it has to give a methodSignatureForSelector: to return an accurate description of the method. For example, an object can forward a message to its surrogate object, which needs to be implemented as follows: methodSignatureForSelector:

- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector signature methodSignatureForSelector:selector] {NSMethodSignature* = [super; if (signature! = [surrogate) {signature methodSignatureForSelector:selector]}; return signature;}

Note: This is an advanced technique, suitable only for situations where no other solution is possible. It is not intended as a replacement for inheritance. If you must make use of this technique, make sure you fully understand the behavior of the class doing the forwarding and the class you re forwarding to. “

Note that the need to pay attention to the implementation of the methodSignatureForSelector method is an advanced technique that applies only to situations where there are no other solutions. It is not an alternative to inheritance. If you must use this technique, make sure you fully understand the behavior of the class to do the forwarding and the behavior of the class you forwarded. Do not abuse!

Two.Method Swizzling

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

Referred to in the Runtime Objective-C, most people first thought is probably black magic Method Swizzling. After all, this is a part of Runtime which is very powerful, it can change any method implemented by Runtime API, could theoretically at runtime by class / method name hook to any OC method, alternative implementation of any class and add any class.

The most cited example should be buried in the example of statistical user information.

Suppose we need to count the user information in different places on the page, there are two common ways:

  1. Fool in all the statistics required to add the code page. This is simple, but the code is too repetitive.
  2. Writes a statistical code to a base class, such as BaseViewController. Although this code only need to write once, but UITableViewController, UICollectionViewcontroller need to write again, so repeated code is still a lot of.

Based on these two points, we choose this time to solve the most elegant Method Swizzling.

1 Method Swizzling principle

Method Swizzing is in operation, mainly at run time for two Method exchange, we can use the Method Swizzling code written in any place, but only in the Method Swilzzling code is executed after the exchange act. And Method Swizzling is also a AOP (face cut programming), a way to achieve, we can use this feature to achieve apple AOP programming iOS.

Method Swizzling is essentially the exchange of IMP and SEL.

2.Method Swizzling use

Generally, we use a new classification, the classification of the exchange of Method Swizzling method. The code template for exchange is as follows:

#import < objc/runtime.h> @implementation; UIViewController (Swizzling) + (void) load dispatch_once_t onceToken {static; dispatch_once (& onceToken, ^{Class class = [self class]; swizzling a class / / When method, use the following: / / Class class = object_getClass (self (ID)); SEL originalSelector = @selector (viewWillAppear:); SEL swizzledSelector = @selector (xxx_viewWillAppear:); Method originalMethod = class_getInstanceMethod (class, originalSelector); Method swizzledMethod = class_getInstanceMethod (class, swizzledSelector); BOOL didAddMethod = class_addMethod (class, originalSelector, Method_getImplementation (swizzledMethod), method_getTypeEncoding (swizzledMethod)); if (didAddMethod) {class_replaceMethod (class, swizzledSelector, method_getImplementation (originalMethod), method_getTypeEncoding (originalMethod));} else {method_exchangeImplementations (originalMethod, swizzledMethod);}}}); #pragma mark - Method Swizzling - (void) xxx_viewWillAppear: (BOOL) {[self animated xxx_viewWillAppear:animated]; NSLog (@ viewWillAppear:% @ ", self);} @end

Method Swizzling can be modified at runtime by modifying the list of methods in the selector corresponding to the function or set the exchange method to achieve dynamic modification. You can override a method without inheritance, and you can call the original implementation. So it is often used to add a method in category.

3.Method Swizzling attention points
Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

1.Swizzling should always be executed in +load

Objective-C automatically calls the two methods of +load and +initialize at runtime. +load will call in the initial loading, the +initialize method is called lazy loading way, if the program has not given a class or its subclasses to send a message, then the +initialize method of this class is never called. So if Swizzling is written in the +initialize method, it is possible that it will never be executed.

Compared with +initialize, +load can be loaded in the initialization of the class.

Comparison of +load and +initialize can refer to this article “Objective-C +load vs +initialize”

2.Swizzling should always be executed in dispatch_once

Swizzling will change the global state, so take some precautions at run time, using dispatch_once to ensure that the code no matter how many threads are executed only once. This will be the best practice for Method Swizzling.

There is a mistake that is easy to make, and that is to use Swizzling. If you do not write dispatch_once will lead to Swizzling failure!

For example, at the same time, both the NSArray and NSMutableArray methods in Swizzling are Swizzling, which may cause the failure of the NSArray in the objectAtIndex.

But why is this?
reason is that we do not use dispatch_once control Swizzling only once. If this Swizzling is executed many times, after several times of switching between IMP and SEL, the result may be the state before the swap.

For example, the parent class A B method and subclass C D method for exchange, exchange once, the parent class A holds the D method of IMP, subclass C holds the B method of IMP, but once again, and then restore the. Parent class A or B method to hold IMP, subclass C or D method to hold the IMP, which is equivalent to the exchange. It can be seen that, if you do not write dispatch_once, even after the exchange, equivalent to no exchange, Swizzling failure!

When 3.Swizzling is executed in +load, do not call [super load]

The reason for the same point of attention two, if it is more inheritance, and the same method are carried out on the Swizzling, then call [super load], the parent class of the Swizzling is invalid.

4 no errors in the above template

Some people suspect that the template I gave above may be wrong. Need to explain here.

In Swizzling, we need to use class_addMethod to determine whether the original class to replace the method to achieve.

If class_addMethod returns NO, indicating that the current class to replace the method to achieve, so you can directly replace, call method_exchangeImplementations to achieve Swizzling.

If class_addMethod returns YES, the implementation of the current class does not have an alternative method, and we need to find it in the parent class. This time you need to use method_getImplementation to get inside the class_getInstanceMethod method to achieve. Then proceed to class_replaceMethod to achieve Swizzling.

This is what Swizzling needs to judge.

It is also important to note that in the xxx_viewWillAppear: (void) animated, we replaced the [self (BOOL) xxx_viewWillAppear:animated], which is not a dead loop?

In fact, this is not a dead cycle.
because we carried out the Swizzling, so in fact the original (void) viewWillAppear: (BOOL) animated method, called the – (void) xxx_viewWillAppear: (BOOL) implementation of the method. So it won’t cause a dead loop. On the contrary, if the [self xxx_viewWillAppear:animated] is changed to [self viewWillAppear:animated], it will cause a dead cycle. Because the outside call [self viewWillAppear:animated]; it will go to the [self xxx_viewWillAppear:animated] exchange method; the method of implementation, and here again to call the [self viewWillAppear:animated], it will cause the cycle of death.

So in accordance with the above Swizzling template to write, you will not encounter these 4 points need to pay attention to the problem.

4.Method Swizzling usage scenarios

Method Swizzling actually has too many scenes, in some special development needs timely use of black magic, can practice the effect of an inspired passage. Here are 3 common scenarios.

1 achieve AOP

AOP examples in the article cited an example, in the next chapter also intends to analyze its principle in detail, here with a pen.

2 achieve buried point statistics

If the app has buried point requirements, and to achieve their own set of buried logic, then use the Swizzling is a very suitable choice. Advantages in the beginning has been analyzed, and no longer repeat here. See a very exciting analysis of the buried point of the article, we recommend reading.
iOS dynamic (two) reusable and highly decoupled user statistical embedding

3 achieve abnormal protection

Daily development we often encounter the situation of NSArray array out of bounds, Apple’s API is not exceptional protection, so we need to pay more attention to the development of our developers. There are a lot of ways on Index, objectAtIndex, removeObjectAtIndex, replaceObjectAtIndex, exchangeObjectAtIndex, etc., these designs to the Index are required to determine whether the cross-border.

The common practice is to increase NSArray, NSMutableArray classification, these methods increase abnormal protection, but if the original project has written a large number of AtIndex series, to replace the new method of classification, the efficiency will be relatively low. Here you can consider using Swizzling to do.

"#import NSArray+ Swizzling.h" #import "objc/runtime.h" @implementation NSArray (Swizzling) + load (void) {Method fromMethod = class_getInstanceMethod (objc_getClass ("__NSArrayI"), @selector (objectAtIndex:)); Method toMethod = class_getInstanceMethod (objc_getClass ("__NSArrayI"), @selector (swizzling_objectAtIndex:)); method_exchangeImplementations (fromMethod, toMethod);} - (ID) swizzling_objectAtIndex: (NSUInteger) {if (self.count-1 < index; index) {/ / @try [self swizzling_objectAtIndex:index] exception handling {return}; @catch (NSException *exception) {/ / NSLog (@ "print crash information --%s Crash Because Method%s ----------/n class_getName" (self.cla SS), __func__ (NSLog); @ "% @", [exception callStackSymbols]); return nil;} @finally {}} else {return}} @end [self swizzling_objectAtIndex:index];

Note that this is called the objc_getClass method, to know the real name corresponding to the NSArray, in fact in the Runtime __NSArrayI, NSMutableArray NSDictionary corresponding to __NSArrayM, corresponding to __NSDictionaryI, NSMutableDictionary corresponding to __NSDictionaryM.

Three. Aspect Oriented Programming

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

Wikipedia is introduced to AOP:

An aspect can alter the behavior of the base code by applying advice (additional behavior) at various join points (points in a program specified in a quantification or query) called a pointcut (that detects whether a given join point matches).

Like logging, authentication, caching affairs is very trivial, has nothing to do with the business logic, a lot of places, it is difficult to abstract a module, the program design, the industry to give them a name (Cross-cutting concern) horizontal focus, AOP is horizontal separation concerns (Cross-cutting concern) to improve the reuse of modules, it can add some additional actions in the existing code (log, authentication, caching) without modifying code.

The next analysis and analysis of the working principle of AOP.

In the article we analyzed, in the process of objc_msgSend search function in IMP, if the parent class did not find the corresponding IMP, it will be the beginning of the implementation of _class_resolveMethod method, if not the implementation of the _class_resolveInstanceMethod class, if class _class_resolveClassMethod. In this method, developers are allowed to dynamically add methods to implement. This phase is usually to provide dynamic methods for the @dynamic attribute variables.

If the _class_resolveMethod cannot handle, will choose to backup the recipient to accept the message, forwardingTargetForSelector this time. If the method returns a non nil object, the object is used as the new message recipient.

- (ID) forwardingTargetForSelector: (SEL aSelector) {if (aSelector = = @selector (Method:)) {return} otherObject; return [super forwardingTargetForSelector:aSelector];}

You can also replace class methods

(ID) + forwardingTargetForSelector: (SEL aSelector) {if (aSelector = = @selector (xxx)) {return NSClassFromString (@ "Class name");} return [super forwardingTargetForSelector:aSelector];}

Replace class method return value is a class object.

ForwardingTargetForSelector this method is a simple forwarding, can not handle the parameters and return values of the message.

Finally to the full forwarding phase.

The Runtime system sends an methodSignatureForSelector: message to the object and takes the returned method signature to generate the NSInvocation object. Generates a NSMethodSignature object for the next full message forwarding. NSMethodSignature objects will be packaged as NSInvocation objects, the forwardInvocation: method can be processed in the NSInvocation.

/ / to the target object is invoked in the method returns a NSMethodSignature instance of the #warning runtime system requirements in the implementation of standards when forwarding the implementation of this method - (NSMethodSignature) methodSignatureForSelector: (SEL) sel{return [self.proxyTarget methodSignatureForSelector:sel];}

Object needs to create a NSInvocation object, the message to call all the details of the package, including selector, target, arguments and other parameters, but also to return the results of processing.

Most of the AOP operations are done in forwardInvocation. Generally will be divided into 2 stages, one is the Intercepter registration phase, one is the Intercepter implementation phase.

1 Intercepter registration
Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

First of all, the class will be a section of the IMP method to add to the Aspect, the class method, if there is a forwardingTargetForSelector: IMP, but also to join the Aspect.

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

Then the class of slicing methods and forwardingTargetForSelector: IMP replacement. The corresponding IMP of both is replaced by the objc_msgForward () method and hook over forwardingTargetForSelector. So the main Intercepter registration is completed.

2 Intercepter execution
Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

When performing func () method, it will look for the IMP, now it has been replaced by IMP objc_msgForward (US), and they began to find redundant forwarding object.

Find backup receiver calls to the forwardingTargetForSelector: method, because here is our hook, so IMP to forwardingTargetForSelector: method is hook. Here we will return to the Aspect target, which selects Aspect as backup receiver.

After a backup receiver will re objc_msgSend, starting from the first stage message.

Objc_msgSend can not find the specified IMP, and then _class_resolveMethod, also found here, forwardingTargetForSelector: do not do here, then it will be methodSignatureForSelector. Create a NSInvocation object in the methodSignatureForSelector method and pass it to the final forwardInvocation method.

Aspect inside the forwardInvocation method will do all the things. Forwarding logic here is completely defined by us. Intercepter registration, we also joined the original method of method () and forwardingTargetForSelector: method of IMP, where we can in the forwardInvocation method to carry out these IMP. Before and after the implementation of these IMP can be arbitrary insert any IMP in order to achieve the purpose of section.

Above is the principle of AOP.

Four. Isa Swizzling

The first second points talked about the black magic Method Swizzling, in essence, is the exchange of IMP and SEL. In fact, the next thing to say Isa Swizzling, and it is similar, in essence, is to exchange, but the exchange is Isa.

In Apple’s official library there is a very famous technology to use the Isa Swizzling, that is KVO – Key-Value Observing.

Official documents on the definition of KVO is this:

Automatic key-value observing is implemented using a technique called isa-swizzling.
The isa pointer as the, name suggests, points to the object’s class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.
When an observer is registered for an attribute of an object the ISA pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the ISA pointer does not necessarily reflect the actual class of the instance.
You should never rely on the ISA pointer to determine class membership. Instead, you should use the class method to determine the class Of an object instance.

So much to the official, the specific realization is not very clear. That’s all we can do for ourselves.

KVO is to monitor whether an attribute value of an object changes. When the property value changes, the setter method will be called. So the essence of KVO is to monitor the object has not been called by the corresponding setter method. Specific implementation should be rewritten its setter method can be.

How is the official implementation of the elegant implementation of the listener class setter method? Experimental code is as follows:

Student *stu = alloc]init] addObserver:self; [stu forKeyPath:@ "name" options:NSKeyValueObservingOptionNew [[Student context:nil];

We can print to observe the direction of the ISA pointer

Printing description of Student; isa: Printing description of stu-> isa: NSKVONotifying_Student; stu->

By printing, we can see clearly that the ISA of the observed object has changed and become the NSKVONotifying_Student class.

In @interface NSObject (NSKeyValueObserverRegistration) this classification, apple defines the KVO method.

- (void) addObserver: (NSObject * observer) forKeyPath: (NSString *) keyPath options: (NSKeyValueObservingOptions) options context: (nullable void * context); - (void) removeObserver: (NSObject * observer) forKeyPath: (NSString *) keyPath context: (nullable void * context) NS_AVAILABLE (10_7, 5_0) - (void) removeObserver:; (NSObject * observer) forKeyPath: (NSString * keyPath);

KVO calls the addObserver method, Apple’s practice is to complete the addObserver: forKeyPath: options: context: method, the ISA pointing to another class.

In this new class rewrite the observed object four methods. Class, setter, dealloc, _isKVOA.

1 rewrite the class method

Rewriting the class method is the same as when we call it to return to the same class as the overriding inheritance class.

Static NSArray * ClassMethodNames (Class C) {NSMutableArray * array = [NSMutableArray array]; unsigned int methodCount = 0; methodList = Method * class_copyMethodList (C, & methodCount); unsigned int i; for (I = 0; I < methodCount; i++) {[array addObject: NSStringFromSelector (method_getName (methodList[i]))];} free (methodList); return array;} int main (int argc, char * argv[]) {Student *stu = [[Student alloc]init]; NSLog (@ self-> isa:%@, object_getClass (stu)); NSLog ("self class:%@", [stu @ class]); NSLog ("ClassMethodNames =% @, @ ClassMethodNames (object_getClass) (stu)); [stu addObserver:self forKeyPath:@" name "options: (NSLog NSKeyValueObservingOptionNew context:nil]; @ "self-> isa:%@" object_getClass (stu)); NSLog ("self class:%@", [stu @ class]); NSLog (@ ClassMethodNames =% @ ", ClassMethodNames (object_getClass (stu)));}

Print result

Self-> isa:Student self class:Student = ClassMethodNames (".Cxx_destruct", "setName: name," self-> isa:NSKVONotifying_Student self class:Student); ClassMethodNames = (setName:, class, dealloc, _isKVOA)

It can also be seen that this is the difference between the object_getClass method and the class method.

2 rewrite the setter method

In the new class, the corresponding set method is overridden to add another two methods to the set method:

- (void) willChangeValueForKey: (NSString *) key - (void) didChangeValueForKey: (NSString *) key

Call again in didChangeValueForKey: method

- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (ID) change: object (NSDictionary *) change context: (void *) context

Here are a few things to explain:

1) if you use the KVC
if you have accessor methods, the runtime calls the will/didChangeValueForKey: method in the setter method;

If you do not use accessor methods, the runtime calls the will/didChangeValueForKey: method in the setValue:forKey method.

So in this case, KVO is working.

2) the accessor method
runtime overrides the accessor method called will/didChangeValueForKey: method.
, therefore, the KVO can also listen to the property when the accessor method is invoked directly.

3) call the will/didChangeValueForKey: method directly.

To sum up, as long as the setter rewrite will/didChangeValueForKey: method can use the KVO.

3 rewrite the dealloc method

Destroy the newly generated NSKVONotifying_ class.

4 rewrite the _isKVOA method

This private method estimate may be used to indicate that the class is a class declared by the KVO mechanism.

Foundation in the end what we provide for the KVO auxiliary function. Open terminal, use the nm -a command to view the information in Foundation:

Nm -a /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation

Which contains the following functions may be used in KVO:

00000000000233e7 t __NSSetDoubleValueAndNotify 00000000000f32ba t __NSSetFloatValueAndNotify __NSSetIntValueAndNotify 000000000007fbb5 t __NSSetLongLongValueAndNotify 0000000000025025 t 00000000000f33e8 t __NSSetLongValueAndNotify 000000000002d36c t __NSSetObjectValueAndNotify 0000000000024dc5 t __NSSetPointValueAndNotify 00000000000f39ba t __NSSetRangeValueAndNotify 00000000000f3aeb t __NSSetRectValueAndNotify 00000000000f3512 t __NSSetShortValueAndNotify 00000000000f3c2f t __NSSetSizeValueAndNotify 00000000000f363b t __NSSetUnsignedCharValueAndNotify 000000000006e91f t __NSSetUnsignedIntValueAndNotify 0000000000034b5b t __NSSetUnsignedLongLongValueAndNotify 00000000000f3766 t __NSSetUnsignedLongValueAndNotify 00000000000f3890 t __NSSetUn SignedShortValueAndNotify 00000000000f3060 __NSSetValueAndNotifyForKeyInIvar 00000000000f30d7 t t __NSSetValueAndNotifyForUndefinedKey

Foundation provides the most basic data types of the auxiliary function (Objective C Boolean unsigned char is typedef, it includes, but not in C++ bool), also includes some common structures such as Point, Range, Rect, Size, which indicates that this structure can also be used for automatic key observation. But it should be noted that in addition to the body structure cannot be used for automatic KVO. For all Objective C objects corresponding to the __NSSetObjectValueAndNotify method.

KVO even realize the official apple, is defective, here is an article with the analysis of the defects in KVO, the main problem in the callback mechanism of KVO, not a selector or block as a callback, but must be a series of problems caused by overriding the -addObserver:forKeyPath:options:context: method. And only listen to the one or two attribute values fortunately, if the listener more than the property, or listening to the properties of multiple objects, it is a little trouble, need to write a lot of if-else in the method of judgment.

Finally, the official documentation for the final implementation of KVO is given, we need to pay attention to the point, never use isa to determine a class inheritance relationship, but should use the class method to determine the class instance.

Five. Associated Object associated object

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

Associated Objects is one of the characteristics of Objective-C 2 in Runtime. As we all know, in Category, we can not add @property, because the addition of the @property will not automatically help us generate instance variables and access methods. So, we can now through the associated object to achieve the function of adding attributes in the Category.

1 usage

Borrow this classic article Associated Objects example to illustrate the usage.

/ / NSObject+AssociatedObject.h @interface NSObject (AssociatedObject) @property (nonatomic, strong) ID associatedObject @end; / / NSObject+AssociatedObject.m @implementation NSObject (AssociatedObject) @dynamic associatedObject; (void) - setAssociatedObject: (ID object) {objc_setAssociatedObject (self, @selector (associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);} - {(ID) associatedObject return objc_getAssociatedObject (self, @selector (associatedObject));}

Here are 3 functions:

OBJC_EXPORT void objc_setAssociatedObject (ID object, const void *key, ID value, objc_AssociationPolicy Policy) __OSX_AVAILABLE_STARTING (__MAC_10_6, __IPHONE_3_1); OBJC_EXPORT ID objc_getAssociatedObject (ID object, const void *key) __OSX_AVAILABLE_STARTING (__MAC_10_6, __IPHONE_3_1); OBJC_EXPORT void objc_removeAssociatedObjects (ID object) __OSX_AVAILABLE_STARTING (__MAC_10_6, __IPHONE_3_1);

To illustrate the significance of these parameters:

1.id object sets the instance object of the associated object

2.const void *key distinguishes between different association objects of key. There are 3 ways to write.

Use & AssociatedObjectKey as key value

Static char AssociatedObjectKey = "AssociatedKey";

Use AssociatedKey as key value

Static const *AssociatedKey = "AssociatedKey void";

Use @selector

@selector (associatedKey)

3 methods can be, but recommended the use of more concise in third ways.

Objects associated with the 3.id value

4.objc_AssociationPolicy policy associated object storage strategy, which is an enumeration, and the corresponding property attribute.

Behavior @property Equivalent Description
OBJC_ASSOCIATION_ASSIGN @property (assign) / @property (unsafe_unretained) Weak reference object
OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (nonatomic, strong) Strong reference to the associated object, and non atomic operation
OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy) Copy the associated object to a non atomic operation
OBJC_ASSOCIATION_RETAIN @property (atomic, strong) Strongly references associated objects, and is an atomic operation
OBJC_ASSOCIATION_COPY @property (atomic, copy) Copy the associated object to the atomic operation

Note here is marked as OBJC_ASSOCIATION_ASSIGN and
@property Association (weak) is not the same, in the table above equivalent definition is written @property (unsafe_unretained), the object is destroyed when the attribute value is still. If the object is used again, it will cause the program to flash back. So we should pay special attention to the use of OBJC_ASSOCIATION_ASSIGN.

According to the Deallocation Timeline described in WWDC 2011, Session 322 (~36:00), associated objects are erased surprisingly late in the object lifecycle (inobject_dispose), which is, invoked by NSObject -dealloc.

One of the things that need to be explained is objc_removeAssociatedObjects. This method is not one of all the associated objects in the source object. Therefore, the method parameters are not passed to the specified key. To delete the specified Association object, use the objc_setAssociatedObject method to set the corresponding key to nil.

Objc_setAssociatedObject (self, associatedKey, nil, OBJC_ASSOCIATION_COPY_NONATOMIC);

3 scenarios for the use of associated objects

1 add a private variable
2 to an existing class by adding a public property
3 for an existing class to create an associated observer for KVO.

2 source analysis
(I) objc_setAssociatedObject method
Void _object_set_associative_reference (ID object, void *key, ID value, uintptr_t Policy) {/ / retain the new value (if any) outside the lock. ObjcAssociation old_association (0, Nil); ID new_value = value? AcquireValue (value, policy): Nil {AssociationsManager manager; AssociationsHashMap; & associations ((manager.associations)); disguised_ptr_t disguised_object = DISGUISE (object); if (new_value) break any existing association. AssociationsHashMap: {/ / iterator: I = associations.find (disguised_object); if (I! = associations.end (table)) {/ / secondary exists ObjectAssociationMap *refs = i-> second; Object AssociationMap: iterator: J = refs-> find (key); if (J! = refs-> end ()) {old_association = j-> second; j-> second = ObjcAssociation (policy, new_value);} else {(*refs) [key] = ObjcAssociation (policy, new_value);}} else {/ / create the new association (first time). ObjectAssociationMap *refs new = ObjectAssociationMap; associations[disguised_object] = refs; [key] = (*refs) ObjcAssociation (policy, new_value); object-> setHasAssociatedObjects (else);}} {/ / setting the association to nil breaks the Asso Ciation. AssociationsHashMap:: iterator I = associations.find (disguised_object); if (I! = associations.end (ObjectAssociationMap)) {*refs = i-> second ObjectAssociationMap:; iterator: J = refs-> find (key); if (J! = refs-> end ()) {old_association = j-> second; refs-> erase (J)};}}} / / release the old value (outside of the lock). If (old_association.hasValue) (ReleaseValue) ((old_association));}

This function is mainly divided into 2 parts, one part is the corresponding if inside the new_value is not nil, and the other part is the corresponding else inside the new_value for the case of nil.

When new_value is not nil, find the time, the process is as follows:

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

First the structure of the AssociationsManager is as follows

Class AssociationsManager static spinlock_t static AssociationsHashMap {_lock; *_map; public: (AssociationsManager) {_lock.lock (~AssociationsManager));} ({_lock.unlock}); AssociationsHashMap (& associations) {if ((_map = = NULL) _map = new (AssociationsHashMap); return *_map;}};

There is a spinlock type spin lock in lock AssociationsManager. Ensure that only one thread at a time for AssociationsManager operation, to ensure the safety of the thread. AssociationsHashMap corresponds to a hash table.

AssociationsHashMap hash table inside key is disguised_ptr_t.

Disguised_ptr_t disguised_object = DISGUISE (object);

By calling the DISGUISE () method to obtain the object address pointer. After getting the disguised_object, through the key value, the AssociationsHashMap hash table to find the corresponding value value. And the value value of the first address of the ObjcAssociationMap table.

In the ObjcAssociationMap table, the key value is passed inside the set parameter const void *key, value value is the ObjcAssociation object.

The ObjcAssociation object stores the last two parameters of the set method, policy and value.

So the 4 parameters passed in the objc_setAssociatedObject method are marked in the figure above.

Now look at the structure and then look at the source code, it is easy. The purpose of the objc_setAssociatedObject method is to store the corresponding key pair in the 2 hash tables.

Initialize a AssociationsManager, get the only save the associated object hash table AssociationsHashMap, and then in the AssociationsHashMap to find the object address of the pointer.

If you find it, you will find second tables ObjectAssociationMap. Continue to look for object’s key in this table.

If (I! = associations.end (table)) {/ / secondary exists ObjectAssociationMap *refs second; ObjectAssociationMap: = i-> iterator: J = refs-> find (key); if (J! = refs-> end ()) {old_association = j-> second; j-> second = ObjcAssociation (policy, new_value);} else {(*refs) [key] = ObjcAssociation (policy, new_value);}}

If the corresponding ObjcAssociation object is found in the second table ObjectAssociationMap, update its value. If not found, create a new ObjcAssociation object and place it in the second table ObjectAssociationMap.

Back to the first table AssociationsHashMap, if the corresponding key is not found

ObjectAssociationMap *refs = new ObjectAssociationMap; associations[disguised_object] = refs; (*refs) [key] = ObjcAssociation (policy, new_value); object-> (setHasAssociatedObjects);

At this point there is no second table ObjectAssociationMap, then you need to create a new second ObjectAssociationMap table to maintain all the new attributes of the object. After the new second ObjectAssociationMap table, you need to instantiate the ObjcAssociation object to add to the Map, call the setHasAssociatedObjects method, indicating that the current object contains the associated object. Here, the setHasAssociatedObjects method changes the value of the second flag bits has_assoc in the isa_t structure. Isa_t structure on the structure, please see the details of the first day of analysis

Release the old (outside value / of the lock). If (old_association.hasValue) (ReleaseValue) ((old_association));

Finally, if the old Association object has a value, it will release it.

The above is not the case for nil new_value. In fact, as long as you remember the above 2 table structure, the process of this objc_setAssociatedObject is updated / new table in the process of the key pair.

Let’s take a look at new_value for nil

Setting the association to nil breaks / the association. AssociationsHashMap:: iterator I = associations.find (disguised_object); if (I! = associations.end (ObjectAssociationMap)) {*refs = i-> second ObjectAssociationMap:; iterator: J = refs-> find (key); if (J! = refs-> end ()) {old_association = j-> second; refs-> erase (J);}}

When new_value is nil, that’s when we remove the associated object. This time is in the two table to find the corresponding key, and call the erase () method, you can delete the corresponding object.

(two) objc_getAssociatedObject method
ID _object_get_associative_reference (ID object, void *key) {ID value uintptr_t = nil; policy = OBJC_ASSOCIATION_ASSIGN; manager {AssociationsManager; AssociationsHashMap & associations ((manager.associations)); disguised_ptr_t disguised_object = DISGUISE (object); AssociationsHashMap:: iterator I = associations.find (disguised_object); if (I! = associations.end) {ObjectAssociationMap (*refs) second = i-> ObjectAssociationMap:: iterator; J = refs-> find (key); if (J! = refs-> end (ObjcAssociation)) {& entry = j-> value = second; policy = entry.policy; (entry.value) (if (Policy); & (OBJC_ASSOCIATION_GETTER_RETAIN) (ID (*) (ID, SEL)) objc_msgSend) (value, SEL_retain);}}} if (value & & (policy; & OBJC_ASSOCIATION_GETTER_AUTORELEASE) {((ID) (*) (ID, SEL)) objc_msgSend) (value, SEL_autorelease);} return value;}

Objc_getAssociatedObject method is very simple. Nil is through the traversal of the AssociationsHashMap hash table and the ObjcAssociationMap table to find all the key values of the corresponding ObjcAssociation object, found on the return of the ObjcAssociation object, not found on the return.

(three) objc_removeAssociatedObjects method
Void objc_removeAssociatedObjects (ID object) {if (object & & object-> hasAssociatedObjects (_object_remove_assocations)) {}} (object); void _object_remove_assocations (ID object) {vector< ObjcAssociation, ObjcAllocator< ObjcAssociation> > elements; manager AssociationsHashMap {AssociationsManager; & associations (manager.associations) (if (associations.size); (return) = = 0); disguised_ptr_t disguised_object = DISGUISE (object); AssociationsHashMap:: iterator I = associations.find (disguised_object); if (I! = associations.end (all)) {/ / copy of the associations that need to be removed. ObjectAssociationMap *refs = i-> sec Ond; for (ObjectAssociationMap:: iterator = J refs-> begin (end = refs->), (end);; J! = end; ++j) {elements.push_back (j-> second);} / / remove the secondary table. delete refs; associations.erase (I);}} / / the calls to releaseValue (happen outside of) the lock. for_each (elements.begin), elements.end (ReleaseValue), (());}

When remove the associated object object, will be the first to determine the object isa_t has_assoc for a second bit value, when the presence of object and object-> (hasAssociatedObjects) value is 1 when to call the _object_remove_assocations method to.

The purpose of the _object_remove_assocations method is to delete second ObjcAssociationMap tables, that is, delete all the associated objects. Delete second tables, you need to traverse the first AssociationsHashMap table search. Here will be second ObjcAssociationMap table of all the ObjcAssociation objects are stored in an array of elements inside, and then call associations.erase () to delete the table second. Finally traverse the elements array, the ObjcAssociation object in turn release.

The above is the source code of the Associated Object related objects 3 functions.

Six. Dynamic add method

If the IMP is not found in the parent class, the resolveInstanceMethod method will be executed during the message sending phase. In this method, we can dynamically add a class object or an instance of the dynamic object.

(BOOL) + resolveInstanceMethod: (SEL SEL) {NSString *selectorString = NSStringFromSelector (SEL); if ([selectorString isEqualToString:@ "method1"] {class_addMethod (self.class), @selector (method1), (IMP) functionForMethod1, the "@");} return [super resolveInstanceMethod:sel];}

Method of operation on the following functions

ID method_invoke / / call the specified method (ID receiver, Method, m,...); / / void method_invoke_stret method returns a data structure (the ID receiver Method, m,...); / / SEL method_getName acquisition method (Method m); / / IMP method_getImplementation (Method m) return method; get / / description method parameters and return values of type const string char * method_getTypeEncoding (Method m); / / return value string type char * method_copyReturnType acquisition method (Method m); / / char * type string specified location parameter acquisition method of method_copyArgumentType (Method m, unsigned int index); / / return value by reference void method_getReturnType returns the type string method (Method m, char *dst, size_t / dst_len); The method of parameter returns / number of unsigned int method_getNumberOfArguments (Method m); / / return by reference method specifies the location parameters of type void string method_getArgumentType (Method m, unsigned int index, char *dst, size_t dst_len); / / return method specifies the description of the structure struct objc_method_description * method_getDescription (Method m); / / implementation IMP method_setImplementation set method (Method m, IMP IMP); / / void method_exchangeImplementations exchange two methods (Method M1, Method M2);

In fact, these methods usually do not need to be memorized, the use of time as long as the first to play at the beginning of the method, behind there will be complete information, find the corresponding method, a corresponding method can be introduced.

Seven.NSCoding automatic archiving and automatic file

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

Although there is not much time to write the file and file, but the automatic operation is achieved with Runtime.

- (void) encodeWithCoder: (NSCoder *) aCoder{[aCoder encodeObject:self.name forKey:@ "name"];} - (ID) initWithCoder: (NSCoder * aDecoder{) if (self = [super init]) {self.name [aDecoder decodeObjectForKey:@ = "name"]}; return self;}

Manual has a flaw, if the property is much more, to write multiple lines of similar code, although the function is perfect, but it does not look very elegant.

Using runtime to achieve the idea is relatively simple, we turn to find the names of each member variables, and then use KVC to read and assign to complete the encodeWithCoder and initWithCoder.

#import "Student.h" #import < objc/runtime.h> #import; < objc/message.h> @implementation Student (void) encodeWithCoder: (NSCoder *) aCoder{unsigned int outCount Ivar = 0; *vars = class_copyIvarList ([self class], & outCount); for (int i = 0; I < outCount; I {var = Ivar + +) vars[i]; const char *name = ivar_getName (VaR); NSString *key = [NSString stringWithUTF8String:name]; ID value = [self valueForKey:key]; [aCoder encodeObject:value forKey:key];}} - (nullable __kindof) initWithCoder: (NSCoder * aDecoder{) if (self = [super init]) {unsigned int outCount = 0; Ivar = *vars class_copyIvarList ([self class], & outCount for (int); I = 0; I & Lt; outCount; I) {Ivar + var = vars[i]; const = char *name ivar_getName (VaR); NSString *key = [NSString stringWithUTF8String:name]; ID value = [aDecoder decodeObjectForKey:key]; [self setValue:value forKey:key]; return self @end}}};

The class_copyIvarList method is used to obtain all the member variables of the current Model, and the ivar_getName method is used to obtain the names of each member variable.

Eight. Dictionary and model conversion

1 dictionary model

1 call the class_getProperty method to get all the properties of the current Model.
2 calls property_copyAttributeList to get property list.
3 generates setter method based on attribute name.
4 uses objc_msgSend to call the setter method to assign attributes to Model (or KVC)

(ID) + objectWithKeyValues: (NSDictionary *) aDictionary{ID objc = [[self alloc] init]; for (NSString *key in aDictionary.allKeys ID) {value = aDictionary[key]; / * judge whether the current attribute is not Model*/ objc_property_t property = class_getProperty (self, key.UTF8String); unsigned int outCount = 0; objc_property_attribute_t *attributeList = property_copyAttributeList (property, & outCount) objc_property_attribute_t; attribute = attributeList[0]; NSString * typeString = [NSString stringWithUTF8String:attribute.value]; if ([typeString "isEqualToString:@ @/" Student/ "] = [self objectWithKeyValues:value]) {value}; / / the method of generating setter and objc_m. SgSend call NSString *methodName stringWithFormat:@ [NSString = "set%@%@:" [key substringToIndex:1].uppercaseString, [key substringFromIndex:1]]; SEL setter = sel_registerName (methodName.UTF8String); if ([objc respondsToSelector:setter]) {((void (*) (ID, SEL, ID)) objc_msgSend (objc), setter, value);}}; free (attributeList) return objc;}

The code inside a typeString, here is to prevent the judge model nested, for example there is a layer of Student Student, then there is a need to transform again, of course there are several layers requires several transformations.

Several well-known Open-Source Library JSONModel and MJExtension are realized through this way (to obtain the array of attributes using runtime class_copyIvarList all member properties, traversal model object attribute name found in the dictionary according to the key value assignment, but this method only can solve the NSString, NSNumber, NSArray or NSDictionary if it contains, but also for second step conversion, if it is a dictionary, the dictionary needs to traverse the array, using the objectWithDict method to the dictionary into the model, the model into the array, finally put the dictionary before this model to the array assignment)

2 model dictionary

Here is the reverse step of the last part of the dictionary model:

1 call the class_copyPropertyList method to get all the properties of the current Model.
2 calls property_getName to get the property name.
3 generates getter method based on attribute name.
4 uses objc_msgSend to call the getter method to obtain property values (or KVC)

/ / model dictionary - (NSDictionary *) keyValuesWithObject{unsigned int outCount objc_property_t = 0; *propertyList = class_copyPropertyList ([self class], & outCount); NSMutableDictionary *dict = [NSMutableDictionary dictionary]; for (int i = 0; I < outCount; I + +) {objc_property_t property = propertyList[i]; / / the method of generating getter and objc_msgSend. Call the const char *propertyName (property) SEL = property_getName; getter = sel_registerName (propertyName); if ([self respondsToSelector:getter]) {ID (value = (ID (*) (ID, SEL)) objc_msgSend) (self, getter); / * judge whether the current attribute is not Model*/ if ([value isKindOfClass:[self class]] & & value) {value} = [value keyValuesWithObject]; if (value) {NSString *key = [NSString stringWithUTF8String:propertyName]; [dict setObject:value forKey:key];}}} free (propertyList); return dict;}

The middle of the comments there is also to prevent the model nesting, if there is a layer of model model, then model turn the dictionary when you need to convert again, the same, there are several layers on the need to convert several times.

However, the above approach is to assume that the dictionary is no longer included in the two level of the dictionary, if there is an array, the array which contains the dictionary, it also requires multi-level conversion. Here is a dictionary containing an array of demo.

Nine.Runtime defects

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly

After reading the above eight points, is not the feeling of Runtime is very magical, can quickly solve a lot of problems, however, Runtime like a Swiss knife, if used properly, it will effectively solve the problem. But improper use will bring a lot of trouble. Some people have raised such a question on the stackoverflow: What are Dangers Method Swizzling in Objective of, which is mainly reflected in the following aspects of the risk of the C:

  • Method swizzling is not atomic

Method swizzling is not an atomic operation. If you write in the +load method, there is no problem, but if written in the +initialize method will appear some strange questions.

  • Changes behavior of un-owned code

If you override a method in a class and don’t call the super method, you can cause problems. In most cases, the super method is expected to be called (unless otherwise specified). If you use the same idea to Swizzling, may cause a lot of problems. If you do not call the original method to achieve, then you change too much Swizzling, and the whole process becomes unsafe.

  • Possible naming conflicts

Naming conflicts are a common problem in program development. The prefix class name and method name. Unfortunately, naming conflicts are like a plague in our program. Generally we will write Method Swizzling

@interface NSView: NSObject - (void) setFrame: (NSRect) frame; @end @implementation NSView (MyViewAdditions) - (void) my_setFrame: (NSRect) frame do custom work [self {/ / my_setFrame:frame];} + {[self (void) load swizzle:@ selector (setFrame:) with:@selector (my_setFrame:)]}; @end

It doesn’t seem to be a problem. But what if there is another definition of the my_setFrame: method in the whole program? That would create a naming conflict. We should change the above Swizzling to the following:

@implementation NSView (MyViewAdditions) static void MySetFrame (ID self, SEL _cmd, NSRect frame); static void (*SetFrameIMP) (ID self, SEL _cmd, NSRect frame); static void MySetFrame (ID self, SEL _cmd, NSRect frame) {/ / do custom work SetFrameIMP (self, _cmd, frame);} + (void load) {[self swizzle:@selector (setFrame:) with: (IMP) MySetFrame store: (IMP * &); SetFrameIMP] @end;}

Although the above code does not look like OC (because of the use of function pointers), but this approach does not effectively prevent the naming conflict. In principle, in fact, these practices more in line with the standardization of Swizzling. This approach may be different from the way people use it, but it is better. The definition of the Swizzling Method standard should be as follows:

Typedef IMP *IMPPointer; BOOL class_swizzleMethodAndStore (Class class, SEL original, IMP replacement, IMPPointer store) {IMP imp = NULL; Method = method class_getInstanceMethod (class, original); if (method) {const char *type = method_getTypeEncoding (method); imp = class_replaceMethod (class, original, replacement, type (if); imp! = method_getImplementation) {imp}} (method); if (IMP & & store) {*store = imp;} return (IMP! = NULL);} @implementation NSObject (FRRuntimeAdditions) + (BOOL) swizzle: (SEL) original with: (IMP) replacement store: (IMPPointer store) {return class_swizzleMethodAndStore (self, original, replacement, store);} @end
  • Swizzling changes the method’s arguments

This is one of the biggest of these problems. Standard Method Swizzling does not change the method parameters. Using Swizzling, you change the parameters passed to the original function, such as:

[self my_setFrame:frame];

Will be converted into

Objc_msgSend (self, @selector (my_setFrame:), frame);

Objc_msgSend will find the my_setFrame corresponding to the IMP. Once the IMP is found, the same parameters are passed in. The most primitive setFrame: method is found here. But here’s the _cmd parameter is not setFrame:, now is my_setFrame. The original method is called by a parameter that it does not expect. This is not good.

Here is a simple solution, the last one said, with the function pointer to achieve. Parameters will not change.

  • The order of swizzles matters

Call order for Swizzling, it is very important. Assume that the setFrame: method is only defined in the NSView class.

[NSButton swizzle:@selector (setFrame:) with:@selector (my_buttonSetFrame:)]; [NSControl swizzle:@selector (setFrame:) with:@selector (my_controlSetFrame:)]; [NSView swizzle:@selector (setFrame:) with:@selector (my_viewSetFrame:)];

What happens when NSButton is swizzled? Most swizzling should be guaranteed not to replace the setFrame: method. Because once this method is changed, it will affect all of the following View. So it will pull the instance method. NSButton will use existing methods to redefine the setFrame: method. That changed the IMP implementation does not affect all View. The same thing will happen in time, swizzling NSControl also, IMP is defined in the NSView class, NSControl and NSButton on the two line of the Swizzle sequence to replace, the result is the same.

When calling the NSButton method of setFrame:, it will call the swizzled method, and then jump into the setFrame: method defined in the NSView class. NSControl and NSView corresponding to swizzled method will not be called.

NSButton and NSControl each call their respective swizzling methods, will not affect each other.

But we change the order of the call, the NSView on the first call.

[NSView swizzle:@selector (setFrame:) with:@selector (my_viewSetFrame:)]; [NSControl swizzle:@selector (setFrame:) with:@selector (my_controlSetFrame:)]; [NSButton swizzle:@selector (setFrame:) with:@selector (my_buttonSetFrame:)];

Once the NSView has been swizzling, the situation is quite different. NSControl swizzling will pull the NSView replacement method after. Accordingly, NSControl in front of NSButton, NSButton will also be pulled to replace the NSControl method. This is very confusing. But the order is arranged. How can we ensure that this chaos does not occur in our development?

Furthermore, load the Swizzle in the load method. If you just do Swizzle in the class that has been loaded, it is safe to do so. The load method ensures that the parent class loads the corresponding method before any of its subclasses load the method. This guarantees the correctness of our call sequence.

  • Difficult to understand (looks recursive)

Looking at the traditional definition of swizzled method, I think it’s hard to predict what will happen. But compared to the standard swizzling above, it is easy to understand. This has been solved.

  • Difficult to debug

In debugging, there will be strange stack call information, especially the name of the swizzled is very confusing, all the way to call the confusion. In contrast to the standard swizzled method, you will see a clear naming method on the stack. Swizzling there is a bit more difficult to debug, it is difficult to remember that the current exact method which has been swizzling.

In the code inside the document notes, even if you think that this code is only one person you will see. Follow this approach to practice, your code will be no problem. It is also difficult to debug multi-threaded debugging.

Last

After 3 days of practice in the nerve hospital, the understanding of the OC Runtime deeper.

About black magic Method swizzling, I personally think that if used properly, is still very safe. A simple and safe way to do this is to simply go to Swizzle in the load method. Like many things in programming, it can be dangerous and scary when you don’t know it, but once you understand it, it will be very accurate and efficient.

For the development of many people, especially the place where the Runtime has changed, the documentation must be complete. If someone does not know that a method is Swizzling, the problem debugging, very egg pain.

If is the development of SDK, some methods of some Swizzling will change the overall situation, must be clearly marked on the document, or the use of SDK people do not know, all kinds of strange problems, but also by the pit for a long time.

In the case of the use of reasonable and complete documentation, to solve specific problems, the use of Runtime is very simple and safe.

The more common Runtime functions that may be used daily may be the following

Get / / CLS class object of all members of the Ivar structure of Ivar *class_copyIvarList (Class CLS, unsigned int *outCount) / / structure instance method to obtain the CLS object corresponding to the name class_getInstanceMethod (Class CLS Method, SEL name) / / get CLS class object name method corresponding structure Method (Class class_getClassMethod CLS, SEL name) / / get CLS name class object corresponding method of imp IMP class_getMethodImplementation (Class CLS, SEL name) / CLS corresponding to test whether the response method of sel corresponding to BOOL class_respondsToSelector (Class CLS, SEL SEL) / / get CLS (Class *class_copyMethodList Method corresponding to the list of methods of CLS, unsigned int *outCount) / CLS test whether comply with the protocol protocol BOOL class_conformsToProtocol (Class CLS, Protocol, *protocol) / / CLS class object, add a new method BOOL Class_addMethod (Class CLS, SEL name, IMP imp, const char *types) IMP class_replaceMethod / / name replacement method corresponding to the CLS object in (Class CLS, SEL name, IMP imp, const char *types BOOL class_addIvar) / / add new members of CLS (Class CLS, const char *name, size_t size, uint8_t alignment. Const char *types BOOL class_addProperty) / / add new attributes for the CLS (Class CLS, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount m) / / get the corresponding selector SEL method_getName (Method m) / / get the corresponding realization method of M imp IMP method_getImplementation (Method m) pointer access / / m method corresponding const encoding char *method_getTypeEncoding (Method m) / / get m method the number of parameters (M unsigned int method_getNumberOfArguments Ethod m) the //copy method returns the value type char *method_copyReturnType (Method m) / / get m method index index parameters of type char *method_copyArgumentType (Method m, unsigned int index) / / get the m method returns the value type void method_getReturnType (Method m, char *dst, size_t dst_len) / / acquisition method parameter types (Method m, method_getArgumentType void unsigned int index, char *dst, size_t dst_len) / / implementation pointer IMP method_setImplementation set M method (Method m, IMP IMP) / / exchange M1, M2 method corresponding to the concrete realization of the function pointer void method_exchangeImplementations (Method M1, Method m2) to obtain the name of the V const / char *ivar_getName (Ivar V) / / get V the type of encoding const char *ivar_getTypeEncoding (Ivar V) / / object set on the related object like void objc_s EtAssociatedObject (ID object, const void *key, ID value, objc_AssociationPolicy Policy) / / get object related object ID objc_getAssociatedObject (ID object, const void *key) / / remove object objects associated void objc_removeAssociatedObjects (ID object)

The API doesn’t look good, in fact the use of time is not difficult, a method of operation, usually at the beginning of the method, on the class, usually at the beginning of the class, the other is the basic of objc at the beginning, the rest depends on the code completion tips, see the method can find the basic way to. Of course, very familiar, can directly play the specified method does not depend on the code completion.

There is some agreement on the relevant API and some other not commonly used, but may also be used, you need to check the Objective-C official Runtime API document, the official document which details, see the document usually don’t understand.

Finally, please exhibitions.

Ps. this dry a little more, the number of articles to remind the text of the book to the upper limit, and fortunately have finished. Discharged smoothly!

Third days of discharge of Objective-C of nerve hospital Runtime -- how to use Runtime correctly