1. Related Concepts
Before this note is started, we need to understand the following concepts.
1.1 Stacks and heaps in the operating system
Note: The heap and stack mentioned here are not the same as the heap and stack in the data structure.
Let's take a look at a structure that the program compiled by C/C++/OBJC consumes the memory distribution:
Stack: Automatically allocated by the system, generally storing function parameter value, local variable value and so on. Automatically created and disposed by the compiler. Its operation is similar to the stack in the data structure, that is, the principle of last-first-out, advanced-out.
For example: Declare a local variable int b in the function, and the system automatically opens up space for B in the stack.
heap: Typically applied by programmers and indicated by size, and eventually released by programmers. If the programmer does not release, the program may end up being recycled by the OS. For the management of the heap area is linked to the management of the list, the operating system has a record of the free memory address of the linked list, when the program allocates memory to receive the application, the operating system will traverse the linked list, traverse to a record memory address is greater than the requested memory of the linked list node, and the node is removed from the list, The memory address recorded by the node is then assigned to the program.
For example, in C, the malloc function
1 char p1; 2 P1 = (char) malloc (10);
But the P1 itself is in the stack.
Linked list: Is a common basic data structure, generally divided into one-way linked list, two-way linked list, circular chain list. The following is a chart of the unidirectional list:
One-way linked list is the simplest of the list, which contains two regions, an information field, and a pointer field. The Information field holds or displays information about the node, and the pointer field stores the address of the next node.
The information field of the free memory address list above holds the address of the free memory.
Global zone/Static zone: As the name implies, global variables and static variables are stored in this area. Only initialized global variables and static variables are stored in a block, and uninitialized global variables and static variables are stored in a block. Released by the system after the program is finished.
Literal constant area: This area mainly stores string constants. Released by the system after the program is finished.
Program code area: This area mainly stores the binary code of the function body.
Here is an example written by a predecessor:
1//main.cpp 2 int a = 0; Global Initialization Zone 3 char *p1; Global Uninitialized Zone 4 main {5 int b;//stack 6 char s[] = "abc";//Stack 7 char *p2;//stack 8 char *p3 = "123456";//1234 56\0 in the constant area, p3 on the stack 9 static int c = 0;//global static initialization zone: p1 = (char *) malloc ( 20), p2 = (char *) malloc (10); And the 20-byte area is in the heap area of strcpy (P1, "123456");//123456\0 in the constant area, the function is to copy the string "123456" into a heap that is placed in the 10-byte area of the P1 request. The "123456" and the "123456" of the P3 point may be optimized by the compiler to an address. 14}
strcpy function
Prototype declaration: extern Char *strcpy (char* dest, const char *SRC);
Function: Copies a string that starts from the SRC address and contains a null terminator to the address space beginning with dest.
1.2 Structure (struct)
In C language, struct (struct) refers to a data structure. Structs can be declared as variables, pointers, or arrays to implement more complex data structures. A struct is also a collection of elements, called members of a struct (member), that can be accessed by names for different types and members in general.
Let's take a look at the definition of a struct:
struct tag {member-list} variable-list;
struct: struct-body keyword.
TAG: Structure tag.
Member-list: List of struct members.
Variable-list: A list of variables declared for the struct.
In general, there are at least two of these three parts of Tag,member-list,variable-list. Here's an example:
1//The struct has 3 members, integral type A, character type B, double type C 2//And a variable is declared for the struct S1 3//The struct does not indicate its label 4 struct{5 int A; 6 char b; 7 double C 8} s1; 9//The struct has the same three members 10//And the struct is labeled EXAMPLE11//The struct does not declare a variable in the struct example{13 int a;14 char b;15 double c;16 };17//With EXAMPLE tag structure, the variable t1, t2, t318 struct EXAMPLE t1, t2[20], *t3 are also declared;
The above is a code example of a simple struct. Members of a struct can contain other structures, or they can contain pointers to their own struct types. A variable of a struct can also be a pointer.
Let's take a look at the access of struct members. Struct members according to the different types of structure variables, there are generally 2 kinds of access, one for direct access, one for indirect access. Direct access is applied to normal struct variables, and indirect access is applied to pointers to struct-body variables. Direct Access uses struct variable name. Member name, indirect access using (* struct pointer name). member name or member name using struct pointer name. The same member names depend on different variable prefixes.
1 struct example{2 int A; 3 char B; 4}; 5//DECLARE struct variable s1 and pointer to struct variable s2 6 struct EXAMPLE s1, *s2; 7//Assign values to variables S1 and S2, note Italy s1.a and S2->a are not the same members 8 s1.a = 5; 9 s1.b = 6;10 S2->a = 3;11 s2->b = 4;
Finally, let's look at the structure member store. In memory, the compiler allocates memory for each struct member in the order of the member list, respectively. If you want to confirm how much storage space the struct occupies, use the keyword sizeof, and if you want to know where a particular member of the struct is in the structure, use the Offsetof macro (defined in stddef.h).
1 struct example{2 int a;3 char b;4};5//Get EXAMPLE type structure the memory size 6 int size_example = sizeof (struct EXAMPLE); 7//Get The offset of member B relative to the EXAMPLE storage address is 8 int offset_b = offsetof (struct EXAMPLE, b);
1.3 Closures (Closure)
A closure is a function, or a pointer to a function, plus a non-local variable that the function executes.
The popular point is that closures allow a function to access variables that are declared in the context of the function's run, and can even access different runs above the variables.
Let's take a look at the scripting language:
1 function Funa (callback) {2 alert (callback ()), 3} 4 function Funb () {5 var str = "Hello world";//local variable of function Funb, letter Non-local variable of number Funa 6 funa (7 function () {8 return str; 9 }10 ); 11}
From the above code we can see that, according to conventional thinking, variable str is a function Funb local variables, scope only in the function Funb, function Funa is not accessible to Str. But the callback in the function Funa in the code example above can be accessed to STR, which is why, because of the closure.
2.blcok Basic Knowledge
Block is actually the implementation of the Objective-c language for closures.
Prototype and definition of 2.1 block
Let's look at the prototype of block:
NSString * (^ myblock) (int);
The above code declares a block (^) prototype, named Myblock, that contains an int parameter, and a pointer that returns a value of type NSString.
Let's look at the definition of block:
1 Myblock = ^ (int parama) 2 {3 return [NSString stringWithFormat: @ "Passed number:%i", Parama];4};
In the above code, a function body is assigned to the Myblock variable, which receives a parameter named Parama and returns a NSString object.
Note: Be sure not to forget the semicolon behind block.
Once you've defined a block, you can use it just like you would with a standard function:
Myblock (7);
Because the syntax of the block data type reduces the readability of the entire code, a typedef is often used to define the block type. For example, the following code creates two new types of getpersoneducationinfo and getpersonfamilyinfo, so that we can use more semantic data types in the following methods.
1//PERSON.H2 #import//Define A new type for the BLOCK3 typedef nsstring * (^GETPERSONEDUCATIONINFO) (NSString *); 4 type def NSString * (^getpersonfamilyinfo) (NSString *), 5 @interface PERSON:NSOBJECT6-(NSString *) getpersoninfowitheducatio N: (getpersoneducationinfo) EducationInfo7 andfamily: (getpersonfamilyinfo) familyinfo;8 @end
Let's summarize the structure of block with a graph from a master article:
2.2 Passing the block as a parameter
1//. H2-(void) Testblock: ( NSString * (^) (int)) MYBLOCK;3//. M4-(void) Testblock: ( NSString * (^) (int)) MYBLOCK5 {6 NSLog (@ "Block returned:%@", Myblock (7)); 7}
Because Objective-c is a mandatory type language, a block that is a function parameter must also specify the type of the return value, as well as the associated parameter type.
2.3 Closure of the package
As stated above, block is actually the implementation of OBJC for closures.
Let's take a look at the following code:
1 #import void Logblock (int (^ theblock) (void)) 2 {3 NSLog (@ "Closure var X:%i", Theblock ()), 4} 5 int Main ( void) 6 {7 nsautoreleasepool * POOL; 8 int (^ myblock) (void); 9 int x;10 pool = [[Nsautoreleasepo Ol alloc] init];11 x = 42;12 myblock = ^ (void) for x;15 (};16 logblock myblock ); 17< c11/>[Pool release];18 return exit_success;19}
The above code declares an integral type in the main function, assigns a value of 42, and declares a block, which returns 42. The block is then passed to the Logblock function, which displays the returned value 42. Even if the block is executed in the function Logblock, and the block is declared in the main function, the block can still access the X variable and return the value.
NOTE: Block can also access global variables, even if it is static.
Copying and modification of variables in 2.4 block
For variable references outside the block, the block is copied to its data structure by default for access, such as:
The variables that are closed by block are const. This means that you cannot modify these variables directly in the block. Let's see what happens when block tries to increase the value of x:
1 Myblock = ^ (void) 2 {3 x++;4 return x;5};
The compiler will make an error indicating that the variable x is read-only in the block.
Sometimes you really need to deal with variables in a block. Don't worry, we can use the __block keyword to declare a variable so that you can modify the variable in the block.
Based on the previous code, add the __block keyword to the x variable, as follows:
__block int x;
For external variable references that are decorated with __block, the block replicates its reference address for access, such as:
3. Block in the compiler
3.1 Block's data structure definition
We illustrate through a picture in the master article:
This structure is the structure in the stack, let's take a look at the corresponding structure definition:
1 struct Block_descriptor {2 unsigned long int reserved; 3 unsigned long int size; 4 void (*copy) (void *dst, void *src); 5 Void (*dispose) (void *); 6}; 7 struct Block_layout {8 void *isa; 9 int flags;10 int reserved;11 Void (*invoke) (void *, ...); The struct block_descriptor *descriptor;13/ * imported variables. */14};
As seen from the above code, Block_layout is the definition of the block structure:
Isa pointer: points to the class that indicates the block type.
Flags: The bit bits indicate additional information about the block, such as judging the block type, judging the block reference count, determining if the block needs to perform auxiliary functions, and so on.
Reserved: reserved variables, my understanding is to indicate the number of variables inside the block.
Invoke: The function pointer, which points to the specific block implementation of the function call address.
Additional descriptive information about the Descriptor:block, such as the number of reserved variables, the size of the block, the auxiliary function pointers for copy or Dispose.
Variables: Because the block has a closure, you can access local variables outside the block. These variables are the addresses of external local variables or variables copied into the struct.
Type of 3.2 block
There are several different types of blocks, each of which has a corresponding class, and the ISA pointer in the above refers to this class. There are three common types listed here:
_nsconcreteglobalblock: A global static block that does not have access to any external variables and does not involve any copies, such as an empty block. For example:
1 #include int main () 2 {3 ^{printf ("Hello, world!\n");} (); 4 return 0;5}
_nsconcretestackblock: Block in the stack is saved and destroyed when the function returns. For example:
1 #include int main () 2 {3 char a = ' a '; 4 ^{printf ("%c\n", a);} (); 5 return 0;6}
_nsconcretemallocblock: The block in the heap is saved and is destroyed when the reference count is 0 o'clock. Blocks of this type are all formed from the stack by a block of type _nsconcretestackblock that is copied into the heap. For example, in the code below, the block or _nsconcretestackblock type in the Exampleb_addblocktoarray method is copied to the heap in the Exampleb method and becomes _ Nsconcretemallocblock type of block:
1 void Exampleb_addblocktoarray (Nsmutablearray *array) {2 char b = ' B '; 3 [Array addobject:^{4 printf ("%c\n ", b); 5 }]; 6} 7 void Exampleb () {8 Nsmutablearray *array = [Nsmutablearray array]; 9 exampleb_addblocktoarray (array); 10
void (^block) () = [Array objectatindex:0];11 block (); 12}
To summarize:
A block of type _nsconcreteglobalblock is either an empty block or a block that does not have access to any external variables. It is neither in the stack nor in the heap, and I understand that it may be in the global zone of memory.
The block of the _nsconcretestackblock type has a closure behavior, which is to have access to an external variable, and the block is only executed once, because the space in the stack is reusable, so when the block in the stack is executed once it is cleared out of the stack, So it can't be used more than once.
A block of type _nsconcretemallocblock has a closure behavior, and the block needs to be executed multiple times. When multiple executions are required, the block is copied from the stack to the heap for execution multiple times.
3.3 How the compiler compiles
We illustrate by a simple example:
1 #import typedef void (^blocka) (void); 2 __attribute__ ((noinline)) 3 void Runblocka (Blocka block) {4 block (); 5} 6 void Doblocka () {7 Blocka block = ^ {8 //Empty block 9 };10 Runblocka (block); 11}
The above code defines a block type named Blocka, which is implemented in the function Doblocka and is used as the parameter of the function Runblocka, and finally calls the function Runbloacka in the function Doblocka.
Note: If the block is created and called in a function, then the optimizer (optimiser) may be able to optimize the code so that we do not see some of the actions in the compiler, so use __attribute__ ((noinline)) Add Noinline to the function Runblocka so that the optimizer does not inline optimize the Runblocka call in the Doblocka function.
Let's take a look at what the compiler does:
1 #import __attribute__ ((noinline)) 2 void Runblocka (struct block_layout *block) {3 block->invoke (); 4} 5 void BL Ock_invoke (struct block_layout *block) {6 //Empty Block function 7} 8 void Doblocka () {9 struct block_descript or descriptor;10 descriptor->reserved = 0;11 descriptor->size = 20;12 descriptor->copy = NULL;13 descriptor->dispose = null;14 struct block_layout block;15 Block->isa = _nsconcreteglobalblock;16 block->flags = 1342177280;17 block->reserved = 0;18 Block->invoke = block_invoke;19 Block->descriptor = descriptor;20 Runblocka (&block); 21}
The above code, combined with the block's data structure definition, makes it easy to understand what the compiler is doing with block.
3.4 Copy () and Dispose ()
As mentioned above, if we want to continue to use a block later, we have to copy the block, that is, from the stack space to the heap space. So the copy operation needs to call the Block_copy () function, and the block's descriptor has a copy () helper function, which is executed in block_copy () to be used when the block needs to copy the object. Copy helper functions retain objects that have already been copied.
Since there is a copy then there should be release, and block_copy () corresponding function is Block_release (), its role is self-evident, is to release the block we do not need to use, The block's descriptor has a Dispose () helper function, which executes in block_release () and is responsible for doing the opposite of the copy () auxiliary functions, such as releasing all the variables copied in the block.
4. Summary
The above content is my study of the Masters of the article on their own learning situation of a record, some of the text and code examples are from the master's articles, there are some of their own understanding, if there are errors and please make corrections
block[in Objective-c]