1. Related Concepts
Before we begin this note, 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 stacks and stack in the data structure.
Let's take a look at a structure that a program compiled by C/C++/OBJC consumes memory distributions:
Stack area (stack): Automatically allocated by the system, generally stored function parameter values, local variables and other values. Created and released automatically by the compiler. Its operation is similar to the data structure of the stack, that is, LIFO, advanced after the principle.
For example: Declare a local variable int b in a function, and the system automatically opens up space for B in the stack.
Heap Area (heap): Typically the programmer applies and indicates the size, which is ultimately released by the programmer. If the programmer does not release, the program may end up being reclaimed by the OS. The management of the heap area is managed by the chain table, the operating system has a list of free memory addresses, and when the application is received by the program to allocate memory, the operating system traverses the list, traversing to a linked list node where the memory address of the record is greater than the requested memory, and removes the node from the list. The memory address of the node record is then assigned to the program.
For example: malloc function in C
Copy Code code as follows:
Char P1;
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 linked list. The following is a chart of the one-way list:
A one-way list is the simplest of a list, which contains two regions, an information field and a pointer field. The Information field saves or displays information about the node, and the pointer field stores the address of the next node.
The information field of the above free memory address list holds the address of the free memory.
Global zone/Static area: As the name suggests, global variables and static variables are stored in this area. Only initialized global variables and static variables are stored in a piece, uninitialized global variables and static variables are stored together. The system is released after the program is finished.
Literal constant area: This area mainly stores string constants. The system is released after the program is finished.
Program code area: This area mainly holds the binary code of the function body.
Here is an example written by a predecessor:
Copy Code code as follows:
Main.cpp
int a = 0; Global initialization Area
Char *p1; Global uninitialized Zone
Main {
int b; Stack
Char s[] = "ABC"; Stack
Char *p2; Stack
Char *p3 = "123456"; 123456\0 in the constant area, p3 on the stack
static int c = 0;//global static initialization area
P1 = (char *) malloc (10);
P2 = (char *) malloc (20); The 10 and 20 bytes allocated are in the heap area.
strcpy (P1, "123456"); 123456\0 is in the constant area, the function of which is to copy a string of "123456" into a 10-byte heap area of the P1 request.
The P3 point "123456" and "123456" here may be optimized by the compiler into an address.
}
strcpy function
Prototype declaration: extern Char *strcpy (char* dest, const char *SRC);
Function: Copy the string containing the null terminator starting from the SRC address to the address space starting with dest.
1.2 Structural body (Struct)
In C, a struct (struct) refers to a data structure. A struct can be declared as a variable, pointer, or array to achieve a more complex data structure. A struct is also a collection of elements called members of a struct, and these members can be of different types, and members are generally accessed by name.
Let's look at the definition of the structure:
Copy Code code as follows:
struct tag {member-list} variable-list;
- struct: Structure keywords.
- Tag: Structural body label.
- Member-list: List of members of the structure body.
- Variable-list: A list of variables declared for the struct body.
In general, there will be at least two tag,member-list,variable-list of these three parts. Here's an example:
Copy Code code as follows:
The structure has 3 members, integral type A, character type B, double C
And a variable S1 is declared for the struct body.
The structure does not indicate its label
struct{
int A;
Char b;
Double C;
} S1;
The structure has the same three members
And the structure identifies the label example
The struct body does not declare a variable
struct example{
int A;
Char b;
Double C;
};
The structure of the example tag is also declared with variable T1, T2, T3
struct EXAMPLE t1, t2[20], *t3;
The above is the code example for the simple structure body. A member of a struct can contain other structures, or it can contain pointers to its own structure type. The variable of the struct body can also be a pointer.
Now let's look at the access to the members of the structure. Structural body members are generally 2 types of access, one for direct access and one for indirect access, depending on the type of structural variables. Direct access is applied to ordinary structure variables, and indirect access is applied to pointers to struct variables. Direct Access uses a struct variable name. Member name, indirect access using (* structure body pointer name). member name or-> member name using struct pointer name. The same member names are differentiated by different variable prefixes.
Copy Code code as follows:
struct example{
int A;
Char b;
};
Declares the structure body variable s1 and pointer to the struct variable s2
struct EXAMPLE s1, *s2;
Assign values to the members of the variable S1 and S2, noting that s1.a and s2->a are not the same members
s1.a = 5;
s1.b = 6;
S2->a = 3;
S2->b = 4;
Finally, let's look at the structure member storage. In memory, the compiler allocates memory for each struct member in the order of member list. If you want to confirm how much storage space the structure occupies, use the keyword sizeof, and if you want to know where a particular member of the structure is located in the structure, use the Offsetof macro (defined in stddef.h).
Copy Code code as follows:
struct example{
int A;
Char b;
};
To get the memory size of the example type structure
int size_example = sizeof (struct example);
Gets the offset of member B's stored address relative to the example
int offset_b = offsetof (struct EXAMPLE, b);
1.3 Closure (Closure)
A closure is a function, or a pointer to a function, plus a nonlocal variable that the function executes.
In layman's parlance, a closure allows a function to access a variable that declares the run context of the function, or even a variable that is running in a different operation.
Let's look at it in scripting language:
Copy Code code as follows:
function Funa (callback) {
Alert (callback ());
}
function Funb () {
var str = "Hello world"; Local variable of function FUNB, Funa variable of function
Funa (
function () {
return str;
}
);
}
We can see from the above code that, by conventional thinking, variable str is a local variable of function Funb, the scope only in function Funb, function Funa is inaccessible to Str. However, the callback in function Funa in the preceding code example can be accessed to STR, which is why, because of the closures.
2.blcok Basic Knowledge
Block is actually the Objective-c language to the closure of the implementation.
The prototype and definition of 2.1 block
Let's take a look at Block's prototype:
Copy Code code as follows:
NSString * (^ myblock) (int);
The code above declares a block (^) prototype, named Myblock, that contains an int parameter, and returns a pointer to the NSString type.
Let's look at the definition of block:
Copy Code code as follows:
Myblock = ^ (int parama)
{
return [NSString stringWithFormat: @ "passed Number:%i", Parama];
};
In the code above, 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 the block.
Once you have defined the block, you can use it just as you would with a standard function:
Copy Code code as follows:
Because the syntax of the block data type reduces the reading 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.
Copy Code code as follows:
Person.h
#import//Define A new type for the block
typedef nsstring * (^GETPERSONEDUCATIONINFO) (NSString *);
typedef nsstring * (^GETPERSONFAMILYINFO) (NSString *);
@interface Person:nsobject
-(NSString *) Getpersoninfowitheducation: (getpersoneducationinfo) Educationinfo
Andfamily: (getpersonfamilyinfo) Familyinfo;
@end
We summarize the structure of the block with a diagram from a master article:
2.2 Passing the block as a parameter
Copy Code code as follows:
. h
-(void) Testblock: ( NSString * (^) (int)) Myblock;
. m
-(void) Testblock: ( NSString * (^) (int)) Myblock
{
NSLog (@ "block returned:%@", Myblock (7));
}
Because Objective-c is a mandatory type language, the block as a function argument must also specify the type of return value and the associated parameter type.
2.3 Closure of the package
As mentioned above, block is actually the implementation of OBJC closure.
Let's take a look at the following code:
Copy Code code as follows:
#import void Logblock (int (^ theblock) (void))
{
NSLog (@ "Closure var X:%i", Theblock ());
}
int main (void)
{
NSAutoreleasePool * POOL;
int (^ myblock) (void);
int x;
Pool = [[NSAutoreleasePool alloc] init];
x = 42;
Myblock = ^ (void)
{
return x;
};
Logblock (Myblock);
[Pool release];
return exit_success;
}
The above code declares an integral type in the main function, assigns a value of 42, and also declares a block that will return 42. The block is then passed to the Logblock function, which shows the returned value of 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 static.
Replication and modification of variables in 2.4 block
For variable references outside of the block, the block defaults to its data structure to achieve access, as shown in the following figure:
A variable that is closed through a block is const. That means you cannot modify these variables directly in the block. Take a look at what happens when block tries to increase the value of x:
Copy Code code as follows:
Myblock = ^ (void)
{
x + +;
return x;
};
The compiler will complain that the variable x is read-only in the block.
Sometimes it is really necessary to handle variables in the block. Don't worry, we can use the __block keyword to declare the variable, so we can modify the variable in the block.
Based on the previous code, add the __block keyword to the x variable, as follows:
Copy Code code as follows:
For external variable references that are decorated with __block, the block copies its reference address to achieve access, as shown in the following figure:
3. Block in the compiler
Definition of data structure of 3.1 block
We use a picture from the master article to illustrate:
The structure above is the structure in the stack, so let's look at the corresponding structure definition:
Copy Code code as follows:
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy) (void *dst, void *src);
void (*dispose) (void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke) (void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
As you'll see from the code above, Block_layout is the definition of a block structure:
ISA pointer: A class that points to the type of the block.
Flags: The bit bits indicate some additional information about the block, such as judging the block type, judging the block reference count, determining whether the block needs to perform auxiliary functions, and so on.
Reserved: Keep the variable, my understanding is to represent the number of variables inside the block.
Invoke: function pointer pointing to the function invocation address of the specific block implementation.
Descriptor:block additional descriptive information, such as retaining the number of variables, block size, copy or dispose of the auxiliary function pointers.
Variables: Because block is closed, you can access local variables outside the block. These variables are the addresses of external local variables or variables that are copied to the structure body.
Type of 3.2 block
There are several different types of blocks, each of which has a corresponding class, in which the ISA pointer points to this class. There are three common types listed here:
_nsconcreteglobalblock: global static block, does not access any external variables, does not involve any copy, such as an empty block. For example:
Copy Code code as follows:
#include int main ()
{
^{printf ("Hello, world!\n");} ();
return 0;
}
_nsconcretestackblock: Blocks in the stack are guaranteed to exist and are destroyed when the function returns. For example:
#include int main ()
{
char a = ' a ';
^{printf ("%c\n", a);} ();
return 0;
}
_nsconcretemallocblock: Blocks in the heap are guaranteed to exist and are destroyed when the reference count is 0 o'clock. This type of block is formed from the stack of _nsconcretestackblock type blocks copied into the heap. For example, in the following code, the block or _nsconcretestackblock type in the Exampleb_addblocktoarray method is copied into the heap in the Exampleb method, becoming _ Block of type Nsconcretemallocblock:
Copy Code code as follows:
void Exampleb_addblocktoarray (Nsmutablearray *array) {
Char b = ' B ';
[Array addobject:^{
printf ("%c\n", b);
}];
}
void Exampleb () {
Nsmutablearray *array = [Nsmutablearray array];
Exampleb_addblocktoarray (array);
void (^block) = [array objectatindex:0];
Block ();
}
To sum up:
- The _nsconcreteglobalblock type block is either an empty block or a block that does not access any external variables. It is neither on the stack nor in the heap, and I understand that it may be in the global area of memory.
- The _nsconcretestackblock type block has a closure behavior, that is, access to an external variable, and the block only executes once, because the space in the stack is reusable, so when the block in the stack executes once, it is cleared out of the stack. So it cannot be used more than once.
- The _nsconcretemallocblock type block 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 multiple executions.
3.3 How the compiler compiles
We use a simple example to illustrate:
Copy Code code as follows:
#import typedef void (^blocka) (void);
__attribute__ ((Noinline))
void Runblocka (Blocka block) {
Block ();
}
void Doblocka () {
Blocka block = ^{
Empty Block
};
Runblocka (block);
}
The code above defines a block type named Blocka, which is implemented in the function Doblocka, as a function Runblocka parameter, and finally calls function Runbloacka in the function Doblocka.
Note: If block creation and invocation are within a function, then the optimizer (Optimiser) can optimize the code so that we do not see some of the operations in the compiler, so use __attribute__ ((noinline)) Add Noinline to the function Runblocka so that the optimizer does not inline-optimize the call to Runblocka in the Doblocka function.
Let's take a look at what the compiler does:
Copy code code as follows:
#import __attribute__ ((noinline))
void Runblocka (struct block_layout * Block) {
block->invoke ();
}
void Block_invoke (struct block_layout *block) {
//Empty block function
}
void Doblocka () {
struct block_descriptor descriptor;
descriptor->reserved = 0;
descriptor->size = 20;
descriptor->copy = NULL;
descriptor->dispose = NULL;
struct block_layout block;
Block->isa = _nsconcreteglobalblock;
block->flags = 1342177280;
block->reserved = 0;
block->invoke = Block_invoke;
block->descriptor = descriptor;
Runblocka (&block);
}
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 in the future, we have to copy the block, that is, from the stack space to the heap space. So the copy operation needs to invoke the Block_copy () function, and the descriptor of the block has a copy () auxiliary function, which executes in block_copy () and is used when the block needs to copy the object. Copy auxiliary functions retain objects that have been copied.
Since there is a copy then there should be release, and Block_copy () corresponds to the function is Block_release (), it is self-evident that we do not need to use the block, Block's descriptor has a Dispose () auxiliary function, which is executed in block_release () and is responsible for doing the opposite of the copy () auxiliary function, such as releasing all the variables copied in the block.
4. Let's look at a few specific examples of running:
The 4.1 parameter is a nsstring* code block
Copy Code code as follows:
void (^printblock) (NSString *x);
Printblock = ^ (nsstring* str)
{
NSLog (@ "print:%@", str);
};
Printblock (@ "Hello world!");
The result of the operation is: Print:hello world!
4.2 Code is sorted by string array
Copy Code code as follows:
Nsarray *stringarray = [Nsarray arraywithobjects:@ "ABC 1", @ "abc", @ "abc", @ "abc", @ "abc", nil];
Nscomparator Sortblock = ^ (ID string1, id string2)
{
return [string1 compare:string2];
};
Nsarray *sortarray = [Stringarray sortedarrayusingcomparator:sortblock];
NSLog (@ "sortarray:%@", Sortarray);
Run Result:
Sortarray: (
"abc",
"ABC 1",
"abc",
"abc",
"abc"
)
4.3 Recursive calls to code blocks
code blocks want to be called recursively, the code block variable must be a global variable or a static variable, so that when the program starts, the code block variable is initialized and can be called recursively
Copy Code code as follows:
static void (^ const blocks) (int) = ^ (int i)
{
if (i > 0) {
NSLog (@ "num:%d", I);
Blocks (i-1);
}
};
Blocks (3);
Run Print results:
4.4 Using local and global variables in code blocks
you can use and change global variables in a block of code
Copy Code code as follows:
int global = 1000;
int main (int argc, const char * argv[])
{
@autoreleasepool {
void (^block) (void) = ^ (void)
{
global++;
NSLog (@ "global:%d", global);
};
Block ();
NSLog (@ "global:%d", global);
}
return 0;
}
Run Print results:
Local variables can be used, but they cannot be changed.
Copy Code code as follows:
int local = 500;
void (^block) (void) = ^ (void)
{
local++;
NSLog (@ "local:%d", local);
};
Block ();
NSLog (@ "local:%d", local);
Changing local variables in a code block compilation does not pass. How do you change a local variable in a block of code? Precede a local variable with a keyword: __block
Copy Code code as follows:
__block int local = 500;
void (^block) (void) = ^ (void)
{
local++;
NSLog (@ "local:%d", local);
};
Block ();
NSLog (@ "local:%d", local);
Run Result: