This time the main content is block, for beginners, the code involved in the content of the block is really easy to confuse. First of all, the concept of block, block is an apple for C, C + + and OC add a feature, it contains a part of the code, can be treated as a parameter to the function, and its essence is OC in the object, that is, we can put it into the collection, such as we can define Nsarray or Nsdictionary objects to place a series of blocks and then code to decide which block to execute. Block also has a large feature, that is, you can intercept the value of the variable from the corresponding code block, just like a closure or lambda expression, but the block is only a simple value.
Grammar
For the declaration of the block, direct use of a ^ on it, just like this
^{ NSLog(@"This is a block");}
In this case, the block and the normal code is not much different, but the block has the code block can not achieve the characteristics. For example, after a block is defined, it will exist as an OC object, and ordinary blocks of code do not. Since blocks can exist as objects, then we should have a way to get a block object and then use it. For this, we can do this with a pointer:
void (^simpleBlock)(void);
In fact, this pointer form and function pointer form very much like, the first void indicates the return type, the second void indicates the argument, and then the middle is the name of the block. Considering the other properties of the block, we can actually think of the block as a special function. Here we continue to look at this pointer, if we want it to point to a specific block of code, as long as this:
simpleBlock = ^{ NSLog(@"This is a block");};
In fact, this is the process of assigning a value, and when the assignment is complete, we can call the block directly:
simpleBlock();
Parameters and return values
As mentioned before, the block can be regarded as a special function, then it is also possible to define the parameters and return type, and also mentioned the meaning of the various parts of the block definition, according to the previous instructions, if we want to define the parameters and the return value of the block, then it should be this form:
double (^multiplyTwoValues)(double, double);
In this case, it is true that the declaration of the block pointer is almost identical to the declaration of the function pointer. and returns the corresponding data by returning the keyword. Also, calls to blocks are very similar to calls to functions:
double (^multiplyTwoValues)(double, double) =
^(double firstValue, double secondValue) {
return firstValue * secondValue;
};
double result = multiplyTwoValues(2,4);
NSLog(@"The result is %f", result);
So in the concept of the block is still confused, difficult to understand, it may be considered as a special function.
Intercept of values
As mentioned earlier, a block can intercept values within the corresponding scope. For example, if a block is defined in a function, it will save the current state in the scope of the function. It contains the values within the scope, so you can use them directly:
- (void)testMethod {
int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(@"Integer is: %i", anInteger);
};
testBlock();
}
But the interception is based on value, which means that the value we use in the block may not be the current value of the variable, but rather a record that was intercepted at the time the block was created. That is to say, in the block we have no way to get the value of the external variable in real time, and there is no way to modify the value of the variable, as follows:
int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(@"Integer is: %i", anInteger);
};
anInteger = 84;
testBlock();
The final output of this piece of code is an Integer is:42, so it is clear that we did not directly get the value of the variable, but saved a copy. Also, we cannot modify the value of the captured variable in a block, because it is of type Const.
Use __block to share variables
Previously, we could not get the real-time value of the variable that was intercepted in the block, nor could we modify the obtained value, but for the original variable, we could use __block to decorate it. This statement is actually a declaration of a store, and when we use __block to modify a variable, it is placed in the same scope as it was in, and all the blocks were shared by the store.
In the previous example, we changed the Aninteger to a variable shared between blocks:
__block int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(@"Integer is: %i", anInteger);
};
anInteger = 84;
testBlock();
The result of this run is Aninteger, that is, we've got the value of the variable in real time, and at this point we can change the value of the variable inside the block.
Take a block as a parameter
In fact, we define the block, not to be called directly after the definition, more often, we just want to be able to invoke a piece of code after an operation, according to the result of this operation, but there is no way to define this code beforehand, so this time we can use the block, This is also an important feature of the block, which is passed as a parameter. In general, when we pass a block, we mainly use it as the content of the callback or as a multithreaded development.
We can use a very simple small example to illustrate, for example, the network request, generally in the network request, we will first release a load box, wait until the request is completed and then close the load box, then we can implement this process:
- (IBAction)fetchRemoteInformation:(id)sender {
[self showProgressIndicator];
XYZWebTask *task = ...
[task beginTaskWithCallbackBlock:^{
[self hideProgressIndicator];
}];
}
Here comes the question of self, in the block, we see that the self variable does not do any processing, directly call the corresponding method, in fact, for the self in the block, is to be more careful, because it is very easy to produce a strong reference loop.
Here first look at Begintaskwithcallbackblock this function, in fact, this function is relatively simple, need to care about his statement, we first look at the declaration of this function:
- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock;
Essentially similar to a block declaration, the first void indicates the return type, the second void describes the parameter type, and the middle indicates the block. Unlike the declaration of a block, the name of the block is placed on the last side.
The block should be placed on the last side of the parameter list as a parameter
This can be considered a coding specification, when writing a function, if there is a block in the argument, then put it to the last side, after all, so in the call, the code looks more concise and clear. Generally for the name of the block, either use callback as before, or directly called completion, basically the name of the block is two.
Simplifying the syntax of a block with a custom type
The declaration of a block variable is actually quite troublesome, especially if you want to declare multiple blocks of variables, and repeat the return type, parameters, and so on, the estimate is pretty deadly. So if you want to simplify the wording here, we can pre-define the type of the good one corresponding block, that is, using the TypeDef keyword to implement:
typedef void (^XYZSimpleBlock)(void);
Like the above, a block type Xyzsimpleblock that defines a return value, which is empty, and then defines the type of the block, can be defined directly:
XYZSimpleBlock anotherBlock = ^{
...
};
Of course, it feels like a custom type is not useful if you look at it this way, but it's a lot easier if the code involves multiple blocks of the same kind. And the parameters of the block are involved in the function, the definition here is much more convenient:
- (void)beginFetchWithCallbackBlock:(XYZSimpleBlock)callbackBlock {
...
callbackBlock();
}
The above code is basically the same as the normal function parameter declaration No difference, all of a sudden simplified a lot.
Of course, there is another case where it is very necessary to customize a type, for example, if the return value of a block is another block, you can consider how to write it first. The standard block declaration is this (the return value) (^ block name) (parameter), then if you change the return value to another block then look at:
void (^(^complexBlock)(void (^)(void)))(void) = ^ (void (^aBlock)(void)) {
...
return ^{
...
};
};
To tell the truth, I personally feel a headache to see this code, this code is basically a block, it will return a (void) (void) type of block, and then look at the definition of the type, the entire head is large, the block itself is also used another block as a parameter, It's basically a state where you can't play well, but if we customize the Void (^) (void) type, let's see what this code will look like:
XYZSimpleBlock (^betterBlock)(XYZSimpleBlock) = ^ (XYZSimpleBlock aBlock) {
...
return ^{
...
};
};
All of a sudden the entire code is clear, and sometimes readability is so important.
Set a block to a property
As a matter of fact, the block has a very interesting place, that is, it is actually an OC object, so we can completely think of it as a property of a class. If you want to put a block as an object of a class, then you have to consider its role, otherwise it is not very important to deliberately set a property for the callback of a function alone. Of course, this is mainly a description of usage:
@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end
Because of the special nature of the block, we have no way to write a simple type directly at the time of declaration, unless it is custom, it can only be written like this. It is also worth noting that if you want to take a block as a property, it should be set to copy, because when a block is capturing the state of the external domain, a block will be copied, the process is a bit like a snapshot, we save is actually a state at that time. After the block is used as the object's property, its use does not actually change much:
self.blockProperty = ^{
...
};
self.blockProperty();
Of course, it would look better if you matched the custom type:
typedef void (^XYZSimpleBlock)(void);
@interface XYZObject : NSObject
@property (copy) XYZSimpleBlock blockProperty;
@end
For the understanding of the block, one cannot forget that its essence is an object of OC.
Avoid strong reference loops
It's strange to see a lot of people here, the block is like a snapshot when capturing a variable, why is a strong reference loop generated? In fact, when a block is capturing a variable, it produces a strong reference to the object, which may sound strange, but, after all, we have a way to get the variable in the block's store, and the block can go directly to the variable, so for insurance, A strong reference is used when capturing to avoid the object being destroyed when the code in the block is executed halfway. Look back at the strong reference loop, because the block is also an object, so if a strong reference loop is generated, then it is the state of the mutual reference between objects, and then combined with the previous block as a property, the reason for the strong reference loop is very clear. The key problem is how to solve, in fact, as with ordinary strong reference loops, the solution is to add a weak reference loop, but the problem is to set which side to be a weak reference. Considering the inner part of a block, this time the block itself will certainly not be released, and at this point it holds a strong reference to a object, which keeps a strong reference to the block, which means that the case of the block to release, you must first put the object to its strong reference to undo, Because blocks do not end up being eliminated, so what we need to do is change the block reference to the object to a weak reference, which can be done in a small way:
- (void)configureBlock {
XYZBlockKeeper * __weak weakSelf = self;
self.block = ^{
[weakSelf doSomething]; // capture the weak reference
// to avoid the reference cycle
}
}
Through this code, we can also find another thing, that is the block to the variable capture way, because here in fact we capture the weakself, not self, otherwise the strong reference loop still exists, which means that the block to the variable capture is not the global, but local capture.
Common uses of blocks
When it comes to the use of blocks, the first thing you'll think about is a callback, yes, the block is often used for function callbacks, because it happens to be a unit of execution, which makes it very convenient to implement some asynchronous operations with blocks.
Speaking of which, let's briefly talk about multithreading in OC. The multithreading in OC in strict sense can be said in two, C language of thread, OC provides operation queue. If you relax a little bit, iOS and OS X can correspond to POSIX threading standards, and there's a GCD in iOS, which is essentially an operation queue, just a fixed queue that's officially packaged for us. Multi-threaded specific content will be further explained in detail, now donuts.
Action Queue
Go back and look at the block, when using the operation queue, we usually create an instance of Nsoperation, which is actually encapsulating some operations, then we will add it to nsoperationqueue this queue to execute. The use of nsoperation is generally as follows:
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
...
}];
We can see that when we instantiate the nsoperation, we use the block to instantiate, and then consider the definition of nsoperation, in fact, the block is in line with the requirements of the Operation queue. You can then look at the usage of the Operation queue:
// schedule task on main queue:
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperation:operation];
// schedule task on background queue:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
Here directly take a mainqueue, in fact, we can also define a queue, so we can be very flexible to get the queue we want, parallel or serial, synchronous or asynchronous, priority, etc. can be flexibly customized, of course, these are later content.
GCD
For iOS development, GCD should be very familiar with, iOS in a few large multithreaded programming, GCD should be the most convenient, the system for each of our applications have prepared a few queues, want to use the time directly to take it, and the common queue is covered, of course, based on GCD We can also define a queue for ourselves, but let's take a look at the usage of GCD:
dispatch_async(queue, ^{
NSLog(@"Block for asynchronous execution");
});
In a simple sentence, we get a queue, and we can tell the system what kind of queue we want by parameters. Then we can start to do the following:
dispatch_async(queue, ^{ NSLog(@"Block for asynchronous execution");});
Enumeration
In fact, for the APIs in Cocoa and Cocoa Touch, they will accept a block as an object to simplify the process, such as for a collection such as enumerations. Take Nsarray as an example:
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;
This method takes a block as a parameter, and then executes the block for each element in the collection. For a collection class, many methods take blocks as parameters.
Summarize
About the content of the block, as a whole, so to see, for the convenience of understanding, we can think of it as a function pointer, but this pointer is special, it is itself an object, and it is different from the function, it is a set of code units can be executed. In addition, blocks can capture external variables, but this capture is a local capture, and if we use global variables, it also captures global variables, and the capture of variables is similar to a snapshot, but captures a value. If you want to access a variable in real time in a block, let that variable be stored in a fast shared area. Finally, the block reference to the object is a strong reference, so in order to avoid a strong reference loop, we need to actively change the block reference to the object to a weak reference.
Programming with OBJECTIVE-C (vi)