Summary of several key points of Objective C runtime Technology
We recommend a good website www.joblai.com.
Preface:
Objective C's runtime technology is very powerful. It can obtain and modify various class information at runtime, including obtaining method lists, attribute lists, variable lists, modifying methods, attributes, and adding methods, attribute and so on. This article makes a summary of several key points.
Directory:
(1) Use the class_replaceMethod/class_addMethod function to dynamically Replace the function at runtime or add a new function.
(2) Reload forwardingTargetForSelector and forward unhandled selector to other objects.
(3) resolveInstanceMethod is reloaded to dynamically Add a selector when a selector cannot be processed.
(4) use class_copyPropertyList and property_getName to obtain the attribute list of the class and the name of each attribute.
(5) Use class_copyMethodList to obtain the list of all methods of the class.
(6) Summary
(1) dynamically Replace the function at runtime: class_replaceMethod
This function can be used to dynamically Replace the function implementation of a class at runtime. What is the purpose of this function? At the very least, you can achieve a hook effect similar to windows, that is, to intercept an instance function of the system class, and then add some of your own things, such as making a log or something.
Sample Code:
IMP orginIMP;NSString * MyUppercaseString(id SELF, SEL _cmd){ NSLog(@"begin uppercaseString"); NSString *str = orginIMP (SELF, _cmd);(3) NSLog(@"end uppercaseString"); return str;}-(void)testReplaceMethod{ Class strcls = [NSString class]; SEL oriUppercaseString = @selector(uppercaseString); orginIMP = [NSStringinstanceMethodForSelector:oriUppercaseString]; (1) IMP imp2 = class_replaceMethod(strcls,oriUppercaseString,(IMP)MyUppercaseString,NULL);(2) NSString *s = "hello world"; NSLog(@"%@",[s uppercaseString]];}
The execution result is:
Begin uppercaseString
End uppercaseString
HELLO WORLD
The role of this Code is
(1) obtain the function pointer of the uppercaseString function and store it in the orginIMP variable.
(2) Replace the implementation of the uppercaseString function in the NSString class with the custom MyUppercaseString
(3) In MyUppercaseString, the log code is executed first, and then the system implementation of the previously saved uppercaseString is called, so that you can add your own items before the system function is executed, when you call uppercaseString for NSString, the log is printed.
Similar to class_replaceMethod, class_addMethod can add a function for the class at runtime.
(2) When an object cannot accept a selector, the call to the selector is forwarded to another object.:-(Id) forwardingTargetForSelector :( SEL) aSelector
ForwardingTargetForSelector is a NSObject function. You can overload it in a derived class and forward unhandled selector to another object. Take uppercaseString as an example. If the User-Defined CA Class Object a does not have an instance function such as uppercaseString, directly execute [a performSelector without calling respondSelector: @ selector "uppercaseString"], crash will be executed. At this time, if the CA implements the forwardingTargetForSelector function and returns an NSString object, then the uppercaseString function is executed relative to the NSString object, so no crash will be executed. Of course, the purpose of implementing this function is not only to make the program not as simple as crash, but also to use this function for message forwarding when implementing the modifier mode.
Sample Code:
1 @interface CA : NSObject 3 -(void)f; 4 5 @end 6 7 @implementation CA 8 9 - (id)forwardingTargetForSelector:(SEL)aSelector11 {13 if (aSelector == @selector(uppercaseString))15 {17 return@"hello world";19 }21 }
Test code:
CA *a = [CA new]; NSString * s = [a performSelector:@selector(uppercaseString)];NSLog(@"%@",s);
The output of the test code is hello world.
Ps: there is a problem here. The CA Class Object cannot receive @ selector (uppercaseString). If I use class_addMethod IN THE forwardingTargetForSelector function to add an uppercaseString function to the CA class, and then return self, is it feasible? After testing, this will crash. At this time, the CA class actually already has the uppercaseString function, but I don't know why it cannot be called. If we create a new CA Class Object and return it, yes.
(3) When an object cannot accept a selector, dynamically Add the required selector to the class to which the object belongs.:
+ (BOOL) resolveInstanceMethod :( SEL) aSEL
This function is similar to forwardingTargetForSelector and will be triggered when the object cannot accept a selector. The execution is slightly different. The former aims to give the customer a chance to add the required selector to the object, and the latter aims to allow the user to forward the selector to another object. In addition, the trigger time is not exactly the same. This function is a class function. When the program is started and the interface is not displayed, it will be called.
If the class cannot process a selector, if the class reloads the function and adds the corresponding selector using class_addMethod, and returns YES, then forwardingTargetForSelector will not be called, if no corresponding selector is added to the function, no matter what is returned, the system will continue to call forwardingTargetForSelector. If no object can accept this selector is returned in forwardingTargetForSelector, the resolveInstanceMethod will be triggered again. If selector is not added, the program reports an exception.
1234567891011121314151617181920212223242526 |
Sample Code 1: 1 @implementation CA 3 void dynamicMethodIMP( id self , SEL _cmd) 5 { 7 printf( "SEL %s did not exist\n" ,sel_getName(_cmd)); 9 } 10 11 + ( BOOL ) resolveInstanceMethod:( SEL )aSEL 13 { 15 if (aSEL == @selector (t)) 17 { 19 class_addMethod([selfclass], aSEL, (IMP) dynamicMethodIMP, "v@:" ); 21 return YES ; 23 } 25 return [superresolveInstanceMethod:aSEL]; 27 } 28 29 @end Test code: CA * ca = [CA new ] [ca performSelector: @selector (t)]; |
Execution result
SEL t did not exist
123456789101112131415161718192021222324 |
Sample Code 2: @implementation CA void dynamicMethodIMP( id self , SEL _cmd) { printf( "SEL %s did not exist\n" ,sel_getName(_cmd)); } + ( BOOL ) resolveInstanceMethod:( SEL )aSEL { return YES ; } - ( id )forwardingTargetForSelector:( SEL )aSelector { if (aSelector == @selector (uppercaseString)) { return @ "hello world" ; } } Test code: a = [[CA alloc]init]; NSLog (@ "%@" ,[a performSelector: @selector (uppercaseString)]; |
The output of the test code is hello world.
For this test code, because a does not have the uppercaseString function, the resolveInstanceMethod will be triggered. However, because this function does not add selector, it will be triggered if this function is not found during running.
The forwardingTargetForSelector function returns an NSString "hello world" in the forwardingTargetForSelector function. Therefore, the string executes the uppercaseString function and returns the uppercase hello world.
12345678910111213141516 |
Sample Code 3: @implementation CA + ( BOOL ) resolveInstanceMethod:( SEL )aSEL { return YES ; } - ( id )forwardingTargetForSelector:( SEL )aSelector { return nil ; } Test code: 1 a = [[CA alloc]init]; 2 NSLog (@ "%@" ,[a performSelector: @selector (uppercaseString)]; |
The execution sequence of this Code is:
(1): The resolveInstanceMethod is triggered when the program is just executed and the AppDelegate has not been released,
(2) When the test code is executed, forwardingTargetForSelector is called
(3) because forwardingTargetForSelector returns nil, The uppercaseString selector cannot be found during the runtime. In this case, the resolveInstanceMethod is triggered. Because the selector is not added, crash is triggered.
(4) use class_copyPropertyList and property_getName to obtain the attribute list of the class and the name of each attribute.
u_int count; objc_property_t* properties= class_copyPropertyList([UIView class ], &count); for ( int i = 0; i < count ; i++) { const char * propertyName = property_getName(properties[i]); NSString *strName = [ NSString stringWithCString:propertyName encoding: NSUTF8StringEncoding ]; NSLog (@ "%@" ,strName); } |
The code above Retrieves all the attributes of UIView and prints the attribute name. The output result is:
skipsSubviewEnumeration viewTraversalMark viewDelegate monitorsSubtree backgroundColorSystemColorName gesturesEnabled deliversTouchesForGesturesToSuperview userInteractionEnabled tag layer _boundsWidthVariable _boundsHeightVariable _minXVariable _minYVariable _internalConstraints _dependentConstraints _constraintsExceptingSubviewAutoresizingConstraints _shouldArchiveUIAppearanceTags |
(5) Use class_copyMethodList to obtain the list of all methods of the class.
The obtained data is a Method array. The Method data structure contains information such as the function name, parameter, and return value. The following code obtains the name as an example:
u_int count; Method* methods= class_copyMethodList([UIView class ], &count); for ( int i = 0; i < count ; i++) { SEL name = method_getName(methods[i]); NSString *strName = [ NSString stringWithCString:sel_getName(name) encoding: NSUTF8StringEncoding ]; NSLog (@ "%@" ,strName); } |
After the code is executed, the names of all functions in UIView are output. The specific results are omitted.
Other related functions:
1. SEL method_getName (Method m) Get SEL2.IMP method_getImplementation (Method m) from Method get IMP function pointer 3. const char * method_getTypeEncoding (Method m) obtains type encoding information from Method 4. unsigned int method_getNumberOfArguments (Method m) to obtain the number of parameters 5. char * method_copyReturnType (Method m) Get the return value type name 6.IMP method_setImplementation (Method m, IMP imp) to set a new implementation for this Method
(6) Summary
In short, what can we do with the runtime technology?
You can perform many operations for various types (including System Classes) without inheriting or category at runtime, including:
- Add
Add function: class_addMethod Add instance variable: class_addIvar Add attributes: @dynamic Label or class_addMethod, because the attribute is actually composed of the getter and setter functions. Add Protocol: class_addProtocol (to be honest, I really don't know how to add a protocol dynamically ,-_-!!) |
- Obtain
Obtain the function list and information of each function (function pointer, function name, etc.): class_getClassMethod method_getName... Obtain the attribute list and information of each attribute: class_copyPropertyList property_getName Obtain information about the class, such as the class name: class_getName class_getInstanceSize Get the Variable list and variable information: class_copyIvarList Get the variable value |
- Replace
Replace the instance with another class: object_setClass Replace the function with a function implementation: class_replaceMethod Directly pass char * The format name is used to modify the value of a variable instead of a variable. |