What happens to classes that use a higher version in a low version?

Source: Internet
Author: User
Tags unsupported

Principle Overview

In simple terms, it's three words-black magic.

There are more and more examples of the use of this kind of black magic, and the earliest I know of this method is a foreigner used in three years to solve Nsuuid.

Our domestic team developed fdstackview is a very good open source Library, has 1500+ stars, I hope that we support our domestic team, in the Fdstackview library also used the same technology, online someone issued an analysis to achieve the principle of the article, but the analysis is very shallow, And did not say in the idea, makes this black magic charm not to be appreciated by everybody, I have done some homework here, elaborated this principle in detail, as well as here the key point is where. If there is anything wrong in the middle process, please correct me, thank you.

The following is a brief idea of implementation.

1. Run time to determine whether the system is already in existence uialertcontroller, if there is, then do nothing, quietly looking at Uialertcontroller, this is iOS8 and above version of the situation.

2. If the system does not have the Uialertcontroller class, we do some "hands and feet" in the runtime, let our Gjalertcontroller in the lower version to complete the problem. This step is the essence, the following analysis of the code back when the detailed description

Detailed analysis

The implementation of the code itself is not really important, the following first thing is the most important, it is the black magic can be achieved by the premise.

Before revealing this important premise, let's briefly talk about memory. There are many kinds of memory, we are most familiar with: stack: the implementation of the function depends on the stack, the function of the simple type of local variables are also open on the stack; heap: We usually use the object is to open up on the heap; Data segment: This is relatively unfamiliar to us, but in fact the static string is the existence of the data segment of

NSString *testStr = @"hello world";NSLog(@"testStr:%p", testStr);testStr:0xb4338 //32位的机器上testStr:0x106326580 //64位的机器上

The memory of the data segment is somewhat special, not that we understand that the pointer on the 32 is the 4BYTE=32BIT,64 bit on the pointer is 8byte=64bit, we have a concept of the data section here, one will use it to explain some phenomena.

The following is a very important part of the premise that this black magic can be implemented. at compile time, each class in the system has a label on the data segment (the form is this:objc_class$_classname), which you can understand as key, whose value is the class name of the class, Example: The data segment will have a key is Objc_class$_uialertcontroller, its corresponding value is Uialertcontroller class name, of course, there will be Objc_class $_uistackview This tag, which identifies the Uistackview class.

The most important point is: in the iOS7, there is no uialertcontroller time, the label Objc_class$_uialertcontroller already exist, but this tag corresponds to the value of nil , because there is no such class, we can think of Apple in the higher version of this kind of position, that is, Apple's position so that we have the privilege of using this black magic. of course, every post-emergence class has a standing position, such as Uistackview.

If this label is Nil or doesnt exist, the class does isn't exist and cannot be allocated/used

This is what I see the foreigner in the use of this kind of black magic to achieve the UUID of one of the words, meaning: if we do not find the label, we can not apply for the memory, it can not be used.

I'm skeptical about the conclusion of this sentence, but it's not possible to do experimental validation, because "tag stands" exist in earlier versions, and it's hard to find "older" versions that don't have tags, because xcode can't support compiling "earlier" versions, which is a bit confusing, Let's look back.

Let's take a look at runtime's methods of dynamically adding classes:

Creates a new class and Metaclass.

@param superclass the class to use as the new class ' s superclass, or \c Nil to create a new root class.

@param name the string to use as the new class ' s name. The string would be copied.

@param extrabytes the number of bytes to allocate for indexed ivars at the end of
The class and Metaclass objects. This should usually is \c 0.

@return the new class, or Nil if the class could not being created (for example, the desired name is already on use).

const char *name, size_t extraBytes) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

Look at the official description of the function can be known: Superclass is the class you want to add the parent class, name is the name of the class you want to add, extrabytes General 0, it will return a new class, if the name is occupied will return nil.

Here are two important conclusions to note:

1. If the objc_class$_classname tag exists, but the corresponding class does not exist (equivalent to having a key, but value is nil) The dynamic addition of the class can be successful at this time.

2. If the objc_class$_classname tag and the corresponding class are present, then dynamically adding the class is unsuccessful and returns nil.

Our idea of black magic is based on these two important conclusions, and we look at the code below.

Code explanation
__asm(    ".section        __DATA,__objc_classrefs,regular,no_dead_strip\n"#if    TARGET_RT_64_BIT    ".align          3\n"    "L_OBJC_CLASS_UIAlertController:\n"    ".quad _OBJC_CLASS_$_UIAlertController\n"#else ".align 2\n" "_OBJC_CLASS_UIAlertController:\n" ".long _OBJC_CLASS_$_UIAlertController\n"#endif ".weak_reference _OBJC_CLASS_$_UIAlertController\n");

This is a piece of assembly code, do not worry about not understand it, I do not understand the Assembly, this does not affect our analysis, I simply explained:

1.__asm is in C, C + + source code in the assembler (OC is a superset of C).

2..align is to align the address of the instruction or data, some CPU architectures require a fixed instruction length, and the storage address is rounded with a power exponent of 2, otherwise it will not work, such as arm. Some do not run this way, that is, the execution is slightly less efficient, such as i386.

The 3.64-bit alignment is 8 bits (the number after the 2^3 (. align)), and the 32-bit alignment is 4 bits (the number behind 2^2 (. Align)). Alignment only works on the statement that is next to it, either L_objc_class_uialertcontroller or _objc_class_uialertcontroller.

4..quad declares a group of 64 bits,. Long declares a group of 32 bits

5..secton is the specified parameter, the general meaning of the above-mentioned assembly is to find the objc_class$_uialertcontroller tag in the data segment (the data segment we mentioned earlier) and take advantage of the. Quad. A long declares a set of numbers to hold it, named: _objc_class_uialertcontroller.

This is a dull and non-focused code, if you are not in a bad mood directly ignore it.

__attribute__ ((constructor))StaticvoidGjalertcontrollerpatchentry(void) {Staticdispatch_once_t Oncetoken; Dispatch_once (&oncetoken, ^{@autoreleasepool {>= iOS8.if (Objc_getclass ("Uialertcontroller")) {Return } Class *alertcontroller =NULL;#If Target_cpu_arm __asm ("MOVW%0,: lower16: (_objc_class_uialertcontroller-(lpc0+4)) \ n""Movt%0,: upper16: (_objc_class_uialertcontroller-(lpc0+4)) \ n""Lpc0:add%0, PC":"=r" (Alertcontroller));#Elif target_cpu_arm64 __asm ("Adrp%0, [email protected]\n""Add%0,%0, [email protected]":"=r" (Alertcontroller));#Elif target_cpu_x86_64 __asm ("Leaq L_objc_class_uialertcontroller (%%rip),%0":"=r" (Alertcontroller));#Elif target_cpu_x86void *pc =NULL; __asm ("Calll l0\n""L0:popl%0\n" "Leal _objc_class_uialertcontroller-l0 (%0),%1":  "=r" (PC), Span class= "hljs-string" > "=r" (Alertcontroller)); #else#error unsupported Cpu#endif if (Alertcontroller &&!*alertcontroller) {Class class = objc_ Allocateclasspair ([Gjalertcontroller class],  " Uialertcontroller ", 0); if (class) {Objc_registerclasspair ( class); *alertcontroller = class;} } });} 

Everybody hold on, this is the last piece of code to analyze.

static void GJAlertControllerPatchEntry(void){}

In general, the above code is a function,
__attribute__ (constructor) is just used to modify the function, what does it do? Here is a black magic about __attribute__, interested people can see my colleague's a special article about __attribute__.

__attribute__ ((constructor)) decorated functions are executed before the main function, which is our best chance to have the runtime environment, but the main function has not been executed and everything is "in time".

if (objc_getClass("UIAlertController")) {    return;}

There are uialertcontroller classes in the system that return directly to the logic that has been mentioned before.

Class *alertcontroller = NULL;#if Target_cpu_arm __asm ("MOVW%0,: lower16: (_objc_class_uialertcontroller-(lpc0+4)) \ n ""Movt%0,: upper16: (_objc_class_uialertcontroller-(lpc0+4)) \ n ""Lpc0:add%0, PC ":"=r" (Alertcontroller));#elif target_cpu_arm64 __asm ("Adrp%0, L_objc_class_uialertcontroller@PAGE \ n "The Add%0%0, L_objc_class_uialertcontroller @PAGEOFF ": " =r "(Alertcontroller));  #elif target_cpu_x86_64 __asm ( "Leaq L_objc_class_ Uialertcontroller (%%rip), %0 ":  "=r" (Alertcontroller));  #elif target_cpu_x86 void *pc = NULL; __asm ( "calll l0\n"  "l0:popl %0\n"  "Leal _OBJC_CLASS_ Uialertcontroller-l0 (%0), %1 ":  "=r" (PC),  "=r" (Alertcontroller));  #else  #error unsupported Cpu# endif                 

This assembly is directly ignored, meaning to put the value of the previous _objc_class_uialertcontroller out into the Alertcontroller , the reason is so troublesome because the CPU of different architectures run different instruction sets, for example , 32 people are going to do this: MOVW 16 bits immediately to the bottom of the register 16 bits, high 16 bits clear 0
The MOVT puts the 16-bit immediate number to the high 16-bit register, and the low 16-bit does not affect it.

if (alertController && !*alertController) {    Class class = objc_allocateClassPair([GJAlertController class], "UIAlertController", 0); if (class) { objc_registerClassPair(class); *alertController = class; }}

If Alertcontroller exists, prove that objc_class$_uialertcontroller tag exists, that is, key exists, *alertcontroller does not exist, proves that there is no such class in the current system, That is, value does not exist. This is exactly what we said earlier, if we print the Alertcontroller address at this point, we will find that it has the same number of bits as the above data segment rather than 32-bit or 64-bit, and again confirms that the label is on the data segment.

Execute the most important code at this time--Dynamically add Class

Class class = objc_allocateClassPair([GJAlertController class], "UIAlertController", 0);

This is the point of the finishing touches, we used to inherit a system class, dynamically add a custom class:

class], "Person", 0);

here is the opposite, here is to judge the absence of the system class, add a system class, inherited from our class: Gjalertcontroller, that is, in the lower version, there is no uialercontroller, we dynamically add this class, Let him inherit Gjalertcontroller, we are in the Gjalertcontroller, Implementing a set of APIs that are exactly the same as the system Uialertcontroller the illusion that a uialertcontroller can be used in a lower version is actually just a magic trick.

The Uialertcontroller we used in the lower version was dynamically added, and it did nothing to directly inherit the Gjalertcontroller, Gjalertcontroller declares and implements a set of APIs that are identical to the system Uialertcontroller. Our gjalertcontroller is not a VC is a nsobject, but their own uialertview and uiactionsheet encapsulated into the Uialertcontroller API, You should know all about it here.

The reason why I want to write this article is mainly to appreciate:

Class class = objc_allocateClassPair([GJAlertController class], "UIAlertController", 0);

The beauty and charm of this code, express my respect for this code, and think of the people who use this code of the admiration of course its practical other runtime functions here also can do the same thing, specifically see I just sent the foreigner's link.



Wen/Two Liang Zi (Jane book author)
Original link: http://www.jianshu.com/p/55180ade32d1
Copyright belongs to the author, please contact the author to obtain authorization, and Mark "book author".

What happens to classes that use a higher version in a low version?

Contact Us

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

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

A Free Trial That Lets You Build Big!

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

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

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

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