About Block

Block in our development everywhere, for example we used the AFN framework and ReactiveCocoa framework, multi thread in GCD, various methods of correction and so on, we can see the importance of Block in our development, today I have something on the Block with the arrangement, if have what problem is welcome to join the discussion.

Definition

Here to borrow a screenshot of the Internet, the feeling is still more detailed

About Block

The Block type declared above is int (^) (int)

Note: the code in the Block block is not executed and will be executed only when a specific call is made

In addition, we can use inlineBlock shortcuts to generate a section of Block code

About Block

Block to redefine
likely to need to repeat the same returns the same value of the parameter list of Block variables using typedef, if repeatedly written a long string of code to declare variables will be very cumbersome, so we can use typedef to define the Block type

Typedef int (^sumBlock) (int, int); @implementation ViewController (void) viewDidLoad viewDidLoad] sumBlock {[super; myblock (int = a, int ^ b) {return a+b;}; myblock (20,30);}

Memory management for Block

In detail before, spots a little knowledge, in the project of how we can quickly determine the current file management or management of ARC is MRC?
1. by dealloc method, to see if dealloc] calls the [super method; only MRC can call super
2. to see if using retain, release and other key, if you can use is MRC

Sum up:

  1. There is no reference to external local variables Block, both at ARC and non ARC, type __NSGlobalBlock__, for this type of Block we don’t need to consider the scope of the problem, and for his copy or retain operation is invalid.
  2. MRC: as long as Block refers to an external local variable, Block’s memory is allocated to the stack; Block is best to use copy and cannot use retain; if retain is used, Block is still inside the stack
  3. ARC: as long as Block references external local variables, the memory of Block is allocated inside the heap
MRC, Block memory location

No external variables are referenced

Void (^myBlock) (NSLog) (MRC = ^{@ Block memory management ");}; (myBlock); NSLog (@"% @ ", myBlock);

Print results:

Block[12349:398796] MRC under Block memory management block[12349:398796] < __NSGlobalBlock__: 0x109e3f070>

In the MRC environment, you simply define a block, which is stored in the global context by default

Referencing external local variables

Int num = 10; void (^myBlock) (NSLog) (MRC = ^{@ Block memory management:%d, Num);}; (myBlock); NSLog (@ "% @", myBlock);

Print results:

Block[12467:411703] MRC under Block memory management: 10 block[12467:411703] < __NSStackBlock__: 0x7fff5a3ffa58>

In the MRC environment, Block references external local variables, and Block is placed in the stack area. In the MAC environment, why does Block use copy instead of
instead of retain?

About Block

Through the above code, we can see the retain modified Block in reference to the external local variable memory is still stored in the stack, the stack in the area can not be shared globally, beyond the scope of variables on the stack, Block and __block variables will be destroyed.
, and only in the heap area of the object, the variables will be shared globally, so use copy copy a copy of Block to the heap area, so that Block will be global sharing.

ARC, Block memory location

No external variables are referenced

Void (^myBlock) (NSLog) =^{(@ BLock memory management ");}; (myBlock); NSLog (@"% @ ", myBlock);

Print results:

Block[12930:472650] BLock memory management block[12930:472650] < __NSGlobalBlock__: 0x104853090>

In the ARC environment, you simply define a block, which is stored in the global context by default

Referencing external local variables

The following code, if you don’t see the result, do you know the value of the myBlock printed internally in temp?

Int temp=10; NSLog (@ Block upper temp variable address: & a=%p, & temp); void (^myBlock) (NSLog) =^{(@ temp=%d ", temp); NSLog (" Block temp @ internal variable address: & a=%p, & temp NSLog;}); (@ "at the bottom of the Block variable temp address: & & a=%p, temp); temp = 30; (myBlock); NSLog (@"% @ ", myBlock);
About Block
timg

Console print results:

Block[12744:448223] Block upper temp address: & a=0x7fff58b28a8c block[12744:448223] variables; the bottom of the Block variable temp address: & a=0x7fff58b28a8c; block[12744:448223] temp= block[12744:448223] 10 Block internal temp address: & a=0x60800004d610 block[12744:448223]; variable < __NSMallocBlock__: 0x60800004d5f0>

The answer you guessed it? All the things below us to analyze why it is 10

First of all, Block memory is allocated on the heap in this area is of no doubt.
second, we note that the temp memory address in the inside and outside the Block is not the same, that when a local variable Block system will use external external variables with a copy to the heap memory.
so in spite of the above code before the call to myBlock to modify the value of the variable, but because external variables used in myBlock is copy, so it is not on the Block internal variables have no effect.

At this point, if we try to modify the temp inside the Block, the system will give us an error prompt, as shown in the figure below:

About Block

The system tells us that if you want to modify the external local variables, in the name of a variable with __block
through the above analysis, I believe that the buddies should know why can’t modify the value of the variable Block outside of it, then why in the name of the variable with a __block on it?

We then proceed to the analysis:

__block int temp=10; NSLog (@ Block upper temp variable address: & a=%p, & temp); void (^myBlock) =^{) (temp = 20; NSLog (@ "Block internal variable temp address: & & a=%p, temp);}; NSLog (@ Block" at the bottom of the temp variable address: & a=%p, & temp "); temp = 30; (myBlock); NSLog (@"% @ ", myBlock);

Console print results:

Block[12835:461193] Block upper temp address: & a=0x7fff58363a88 block[12835:461193] variables; the bottom of the Block variable temp address: & a=0x608000222a38 block[12835:461193] Block temp; a=0x608000222a38 block[12835:461193] & variable address: < __NSMallocBlock__: 0x608000058390>

In the ARC environment, if you use the __block variable, the variable will be copy to the heap memory, and the original variables will point to the
space in heap memory that __block modified variable is a reference

Some usage scenarios for Block

Call the block as the object property at the right time

A scene from A
as our Modal controller to the B controller in the B controller, click on a button to transfer some data to the A controller, which can also use a proxy to achieve, but I feel that Block is more concise:

B controller:

@interface BViewController: UIViewController, @property (nonatomic, strong), void (^buttonClickBlock) (NSString, *param); @end
- - (void) buttonClick{if (_buttonClickBlock) {_buttonClickBlock (@ parameter needed to pass')}}

A controller:

BViewController *modalVc = [[BViewController alloc] modalVc.view.backgroundColor = init]; [UIColor = brownColor]; modalVc.buttonClickBlock ^ (NSString *param) {NSLog (@ "passed parameters:% @", param);}; / / [self presentViewController:modalVc animated:YES completion:nil] jump;

Scene two:
has a UITableView, then click on each line will do different things, if not Block you are not thinking about when clicked to determine the line number, then according to the number of things to do in the corresponding? But we can simplify this process by Block:

In the corresponding entity model, we only need to define a specific Block to do, for example:

@interface CellItem: NSObject @property (nonatomic, strong) NSString *title; / / keep each cell do @property (nonatomic, strong) void (^doSomethingBlock) (@end);

Then in tableView: when you click on the corresponding code block you can do, is not it a lot easier?

- (void) viewDidLoad {[super viewDidLoad]; / / *item1 = [CellItem itemWithTitle:@ CellItem model to create a "call"]; / / to do things (code) to save ^{(NSLog model item1.doSomethingBlock = @ "call");}; CellItem *item2 = [CellItem itemWithTitle:@ "texting"]; item2.doSomethingBlock = NSLog ^{(@ "texting");}; CellItem *item3 = [CellItem itemWithTitle:@ "e-mail"]; NSLog (item3.doSomethingBlock = ^{@ "email");}; _items = @[item1, Item2, item3];} / / click when the cell executes the corresponding Block code block can be - (void) (tableView: UITableView tableView didSelectRowAtIndexPath: (NSIndexPath) * * indexPath) {CellItem *item = self.items[indexPath .row]; if (item.doSomethingBlock) {item.doSomethingBlock ()}}
Passing block as a function argument

When we encapsulate a method, but this method is not specific to do what method can decide, but what time is done by the internal decisions (i.e., internal and external decision execution time, specifically what to do), this time you can use block as a function parameter

Name a frequently used example:

+ (void) animateWithDuration: (NSTimeInterval) duration animations: (void (^) (void)) animations completion: (void (^ __nullable) (BOOL, finished)) completion

For the animation method provided by the system, what kind of animation should be executed and what should be done after the execution of the animation, the method is not known internally and needs to be handed out to the user himself

Scenario two:
requires the inclusion of a calculator class, CacultorManager, which provides a calculation method: how the calculation is determined by the outside world and when the calculation is determined by the internal

CacultorManager.h

@interface CacultorManager: NSObject / save results **/ @property (nonatomic, assign) - NSInteger result; (void) cacultor: (NSInteger (^) (NSInteger result) cacultorBlock @end);

CacultorManager.m

@implementation, CacultorManager - (void) cacultor: (NSInteger (^)) (NSInteger) cacultorBlock {if (cacultorBlock) {_result = cacultorBlock (_result)}} @end

Use this class specifically:

- (void) viewDidLoad {[super viewDidLoad]; / / create a calculator management CacultorManager *mgr = [[CacultorManager alloc] init]; [mgr cacultor:^ (NSInteger result) {result = 5; result = 6; return result;}]; NSLog (@ "%ld", mgr.result);} @end
Passing block as a function return value

I believe we’ve all written code like this before

[View mas_makeConstraints:^ (MASConstraintMaker *make) {make.top.equalTo (anotherView); make.left.equalTo (anotherView); make.width.mas_equalTo (@60); make.height.mas_equalTo (@60);}];

Similar to the Masonry for.Top.equalTo (anotherView); the way we called chain programming, and the realization of this idea is the key method of the return value must be block, and the block must return to itself, the parameters of the Block is that we need to manipulate the data

Let’s write a small function like this: take the calculator class CalculatorManager above for example:

CalculatorManager.h

@interface CalculatorManager: NSObject / save results **/ @property (nonatomic, assign) - (int result; CalculatorManager * (^) (int) - (add); CalculatorManager (* ^) (int)) sub @end;

CalculatorManager.m

@implementation CalculatorManager - (CalculatorManager (* ^) add (int)) {return (int ^ value) {_result = value; return self;};} - (CalculatorManager (* ^ (int))) sub (int value) {return ^ {_result = value; return self;}}; @end

In the implementation class:

- - (void) viewDidLoad {[super viewDidLoad]; CalculatorManager *mgr = [[CalculatorManager alloc] init]; mgr.add (5).Add (5).Sub (6).Add (5); NSLog (@%d ', mgr.result);} @end

Block variable transfer summary: if a local variable is accessed inside the block, the value is passed, or else the pointer passes

The circular reference problem of Block

The two objects hold each other, which causes circular references, as shown in the following figure:

About Block
2

In Figure 1, object A holds object B, object B holds object A, holds each other, and eventually causes two objects to be freed.

Before listing Demo, I’d like to make a conclusion:

If [block [internal] uses a strong reference of external statements] [object A], then [block] will automatically generate a [strong reference] point [object A]

[if block [internal] uses a weak reference of an external statement] access [object A], then [block] automatically generates a [weak reference] pointing to [object A]

Give a simple example:

Person.h

@interface: Person NSObject / * * * / @property name (nonatomic, copy) NSString *name block; / * * * / @property (nonatomic, copy) void (^dosomethingBlock) (@end);

Person.m

@implementation, Person - (void) dealloc {NSLog (@%s', __func__)} @end

Instantiate Person objects in ViewController:

- (void) viewDidLoad Person *per {[super viewDidLoad]; [[Person alloc] = init]; per.name = @ "Jack"; per.dosomethingBlock = NSLog ("^{@ -----------%@", per.name);}}; @end

Running the above code, and found no call to the dealloc method in the Person object. The circular reference issues at this time, because the Block internal access to the external statement strong reference Person object, so the Block will automatically produce a strong reference to the Person [] to [object]

So how do we solve this problem at this point? The usual practice is to create a __weak modified weak reference to the person object. The code is as follows:

- (void) viewDidLoad [super viewDidLoad] Person {*per = [[Person; alloc] init]; __weak typeof (Person) *weakPerson = per; per.name = Jack; per.dosomethingBlock = @ (@ NSLog ^{"-----------%@", weakPerson.name);};}

Running the above code, Person object calls the dealloc method. Because the Block internal use of [weakPerson] weak reference object Person] [access to external declarations, so the Block will automatically produce a [if] [Person] object reference point

Nesting of Block

In the above example, we delayed three seconds after weakPerson.name printing in per.dosomethingBlock, carefully check the code below, you will find the above problems?

- (void) viewDidLoad [super viewDidLoad] Person {*per = [[Person; alloc] init]; __weak typeof (Person) *weakPerson = per; per.name = Jack; per.dosomethingBlock = @ ^{NSLog (@ beign-------); dispatch_after (dispatch_time (DISPATCH_TIME_NOW (int64_t) (3 * NSEC_PER_SEC)), dispatch_get_main_queue (^{). NSLog (@ "after-----------%@", weakPerson.name);};}); (per.dosomethingBlock);}

Console output:

2017-08-14, 17:48:17.441, block[23041:1182337], beign-------, 2017-08-14, 17:48:17.441, block[23041:1182337], -[Person, dealloc], 2017-08-14, 17:48:20.729, block[23041:1182337],, after----------- (null)

We can see through the print results when Block is executed immediately print beign——- information, and then the Person object is destroyed, 3 seconds after the printing of after——- information, note that because the Person object has been destroyed, so print out of Null

So, in the deferred execution of the Block, in order to keep the Person object from being destroyed, we need to use a strong flag to keep the Person object alive and change the code slightly:

- (void) viewDidLoad [super viewDidLoad] Person {*per = [[Person; alloc] init]; __weak typeof (Person) *weakPerson = per; per.name = Jack; per.dosomethingBlock = @ ^{NSLog (@ beign-------); Person *strongPerson = weakPerson; dispatch_after (dispatch_time (DISPATCH_TIME_NOW (int64_t) (3 * NSEC_PER_SEC)). Dispatch_get_main_queue (NSLog), ^{(@ "after-----------%@", strongPerson.name);};}); (per.dosomethingBlock);}

Console output:

2017-08-14, 17:50:04.947, block[23068:1184458], beign-------, 2017-08-14, 17:50:08.234, block[23068:1184458], after-----------Jack, 2017-08-14, 17:50:08.234,, block[23068:1184458], -[Person, dealloc]

From the above results, we can see that a strong reference strongPerson is used in dispatch_after to hold the life of the Person object, so this is what we want