IOS Interview Questions:
IOS Basic Interview Questions 1
IOS Interview Collection + Answer (1)
IOS Interview Collection + Answer (2)
IOS Interview Collection + Answer (3)
IOS Interview Collection + Answer (4)
IOS Interview Collection + Answers (5)
Common iOS Development Interview Questions
A learning route for iOS developers
[0. Brief introduction of block] block is a C extension introduced in iOS4.0+ and Mac OS X 10.6+ to implement anonymous functions.
In Wikipedia terms, blocks are a feature that Apple Inc. added to C, C++, and Objective-C, allowing those languages to create closures using a lambda-like syntax.
In The words of Apple documentation, A block is an anonymous inline collection of code, and sometimes also called A closure.
A closure is a function that can read the variables inside another function.
This interpretation is also appropriate for blocks: a function defines a block that accesses the internal variables of that function.
A simple example of a Block is as follows:
int (^maxBlock)(int, int) = ^(int x, int y) { return x > y ? x : y; };
Copy the code
If written as a Python lambda expression, it can be written as follows:
f = lambda x, y : x if x > y else y
Copy the code
However, because of the language nature of Python, it is natural to use def statements to define embedded functions within the body of functions defined by def, because these functions are objects in nature.
If BNF is used to represent the context-free grammar of blocks, it would look something like this:
1. block_expression ::= ^ block_declare block_statement 2. block_declare ::= block_return_type block_argument_list 3. Empty block_return_type: : = return_type | 4. Block_argument_list: : = argument_list | nullCopy the code
In addition to the ability to define parameter lists and return types, a block can also obtain the lexical state (such as local variables) at the time it is defined and modify these states under certain conditions (such as using __block variables). In addition, these modifiable states are shared between blocks within the same lexical scope, and can continue to be shared or modified even outside of that lexical scope (e.g., stack unrolling, out of scope).
In general, blocks are encapsulated pieces of short code that can be used as units of work, typically for concurrent tasks, traversals, and callbacks.
For example, we can do some things while traversing an NSArray:
1. - (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;
Copy the code
If I set stop to YES, I’ll break out of the loop and not continue through it.
In many frameworks, blocks are increasingly used as callbacks instead of traditional callbacks.
- Using blocks as callbacks makes it easier for programmers to write code without having to run to another place to write a callback, and sometimes to think about where to put the callback. With blocks, you can write subsequent processing code directly when a function is called and pass it as an argument to be called back when its task finishes.
- Another benefit is that using blocks as callbacks allows direct access to local variables. For example, IF I want to change the name of a user in a batch of users, I will update the cell UI of the corresponding user through the callback. In this case, I need to know the index of the user cell. If I use the traditional callback method, I need to bring the index back to the user cell. Use the external scope to record the index of the current operation cell (this limits the change to only one user name at a time); We need to go through and find the user. With block, you can access the index of the cell directly.
This document mentions several applications for blocks:
-
Callback processing when the task is completed
-
Message listening callback processing
-
Error callback handling
-
Enumerated the callback
-
View animation, transformation
-
The sorting
[2. About __block_impl]
Clang provides an intermediate code presentation option to further understand how blocks work.
Let’s take a very simple piece of code:
Compile with the -rewrite-objc option:
You get a copy of the block0.cpp file in which you can see the following code snippet:
The name indicates that this is an implementation of the block, and that the block is implemented in front of the Clang compiler to generate C intermediate code. Many languages can implement only the compiler front end, generate C intermediate code, and then take advantage of many of the existing C compiler backends.
The members of the structure, Flags, Reserved, can be skipped, the isa pointer indicates that a block can be an NSObject, and the FuncPtr pointer is clearly a function pointer to a block.
Thus, the mystery of the block was unveiled.
But where do I put the variables associated with blocks? As mentioned above, a block can capture the state within the lexical scope (or outer context, scope) and modify the state even outside that scope. How does this work?
[3. Implementation of a simple block]
Let’s look at a block that outputs only one sentence.
Generate the intermediate code and get the following snippet:
The first structure that appears is __main_block_IMPL_0, which is named after the function (main) and the sequence (0th) in which it appears. If it is a global block, it is named according to the variable name and sequence of occurrences. __main_block_IMPL_0 contains two member variables and a constructor. The member variables are the __block_impl structure and the description Desc. The constructor then initializes the block type and function pointer.
Next comes the __main_block_func_0 function, which is the body of the function that corresponds to the block. This function takes a __cself argument, the corresponding block itself.
Below that is the __main_block_desc_0 structure, where the more valuable information is the block size.
Finally, the block is created and called in main. We can see that executing a block is calling a function that takes the block itself as an argument, which corresponds to the execution body of the block.
In this case, the block type is _NSConcreteStackBlock, indicating that this block is on the stack. Likewise, there’s _NSConcreteMallocBlock and _NSConcreteGlobalBlock.
Since the block is also an NSObject, we can retain it. However, when a block is passed to the underlying framework as a callback, the underlying framework needs to make a copy of it. For example, if you use a callback block as an attribute, instead of retain, use copy. We usually write blocks on the stack, and when we call back, we call back that the block is no longer on the stack, and we use the copy property to put the block on the heap. Or use Block_copy() and Block_release().
[4. Capture local variable]
Let’s look at a block that accesses a local variable.
Generate the intermediate code and get the following snippet:
The block structure __main_block_IMPL_0 has a member variable **** I, which is used to store the local variable I (1024). And you can see how the __cself argument works, similar to this in C++ and self in Objective-C.
If we try to modify the local variable I, we get the following error:
The error message is verbose, both telling us that the variable is not assignable and reminding us to use the __block type identifier.
Why can’t I assign a value to the variable I?
Because the local variable I in main is not in the same scope as the function __main_block_func_0, only the value is passed during the call. Of course, in the above code, we can use Pointers to modify local variables. However, this is due to the fact that when __main_block_func_0 is called, the main stack has not yet been expanded and the variable I is still on the stack. In many cases, however, the block is passed as a parameter for subsequent callbacks to execute. Usually in these cases, the function stack on which the block was defined is already unrolled and the local variable is no longer on the stack (where is the block?). , and then use pointer access…
Therefore, for local variables of type Auto, it is reasonable not to allow block modifications.
[5. Modify static local variable]
We can then infer how static local variables are modified in the body of a block — via Pointers.
Because static local variables exist in the data segment, there is no risk of illegal access after the stack is unrolled.
The difference between the above intermediate snippet and the previous snippet is that the address of I (&i) is passed in the main function, and the member I in the __main_block_IMPL_0 structure becomes a pointer type (int *).
The value is then modified through Pointers when the block is executed.
Of course, global variables and static global variables can be modified within the block execution body. More precisely, a block can modify variables in the scope in which it is called (in this case __main_block_func_0). For example, when a block is used as a member variable, it can also access other member variables in the same object.
[6. Implementation of __block variable]
So how do __block variables support modification?
We add the __block indicator to int variables so that the variable I can be modified within the body of the block function.
Now if you look at the intermediate code, you get a lot more information. The first is the structure corresponding to the __block variable:
It is also known from the first member __isa pointer that __Block_byref_i_0 can also be NSObject.
The second member __forwarding points to self. Why does it point to self? Pointing to itself is meaningless, other than to say that sometimes you need to point to another __Block_byref_i_0 structure.
The last member is the target storage variable I.
In this case, __main_block_IMPL_0 has the following structure:
The member variable I of __main_block_IMPL_0 becomes of type **__Block_byref_i_0 ***.
The corresponding function __main_block_func_0 looks like this:
The highlight is that the __Block_byref_i_0 pointer type variable I operates on another member variable through its __forwarding pointer. : -)
The main function looks like this:
With this somewhat complicated change, we can change the value of the variable I. However, the same problem exists: the __Block_byref_i_0 variable I is still on the stack. When the block is called back, the stack of the variable I has been expanded.
At such critical moments, __main_block_desc_0 steps up:
__main_block_desc_0 has two more member functions: copy and Dispose, which point to __main_block_copy_0 and __main_block_dispose_0, respectively.
When a block is copied from the stack to the heap, __main_block_copy_0 is called to copy the member variable I of type __block from the stack to the heap; When the block is released, __main_block_dispose_0 is called accordingly to free the member variable I of type __block.
It’s on the stack, it’s on the heap, so what if this variable is being operated on both the stack and the heap?
This is where __forwarding comes in: when an __block variable is copied from the stack to the heap, the __forwarding pointer in the __Block_byref_i_0 structure on the stack also points to the structure on the heap.
If you like it, remember to follow it! Thank you very much!!