Block in block, with __weak self

I am trying to find out if I am doing this correctly:

If I have one block, I will do the following:

__weak MyClass *weakSelf = self; [self performBlock:^{ //<< Should I use self, or weakSelf here? [weakSelf doSomething]; } afterDelay:delay]; 

But what happens if there is a block in a block? Is it correct?

 __weak MyClass *weakSelf = self; [self performBlock:^{ [weakSelf doSomething]; [self performBlock:^{ [weakSelf doSomething]; } afterDelay:1.0f]; } afterDelay:delay]; 

Also, in the function below, do I need to use [block copy]?

 - (void)performBlock:(void (^)(void))block afterDelay:(float)delay { if (block) { if (delay > 0) { [self performSelector:@selector(executeBlockAfterDelay:) withObject:[block copy] afterDelay:delay]; } else { [self executeBlockAfterDelay:[block copy]]; } } } - (void)executeBlockAfterDelay:(void(^)(void))block { if (block) block(); } 
+4
source share
4 answers

In this case (below) use only strong self , because the block is copied only in these few seconds. And usually, if you want self execute a block, you want it to stay alive until then, so a strong link is fine.

 [self performBlock:^{ [self doSomething]; // strong is OK } afterDelay:delay]; 

Block inside a block? In your case, these two blocks are simply delayed by single-block blocks, so use the strong ones just like above. But there are differences between the blocks. If you are storing a block for a longer time , perhaps for several calls you should avoid saving cycles.

Example:

 self.callback = ^{ [self doSomething]; // should use weakSelf }; 

This may cause a save loop. Actually, it depends on how the block is used. We see that the block is saved (copied) in the property for later use. However, you can prevent cycles from being saved by destroying a block that will no longer be used. In this case:

 self.callback(); //invoke self.callback = nil; //release 

When using ARC, you do not need to copy blocks yourself. There were errors in earlier versions after adding blocks, but now the compiler under ARC knows when to copy blocks. It is smart enough to copy it in this case:

 [self performSelector:@selector(executeBlockAfterDelay:) withObject:block afterDelay:delay]; 
+5
source

Instead of doing -performBlock:afterDelay: just use dipatch_after() . Among other things, this is not a message passed to the object, so there is no question of which receiver will target it.

In fact, there is no problem with memory management. Usually, as a rule, only a β€œweak” approach is required when the object saves the block, and the block (possibly implicitly) saves the same object. However, the object does not save the block. It is retained by the framework until there is -performSelector:withObject:afterDelay: but this is not a save loop.

If there was a save loop, then you should not reference self in blocks. Thus, your nested case is incorrect when calling the self message, not weakSelf .

Finally, yes, you need to [block copy] whenever you save a block after execution, leaves the scope of its declaration, or passes it to the non-blocking API. That is, you do not need to copy the block when you pass it, say dispatch_async() , because it is a block-backed API that knows to make its own copy as needed. But -performSelector:withObject:afterDelay: not block. It simply considers its argument as a generic object and stores it. So, you need to copy the block, passing it to this.

+5
source

The most important thing you need to understand about blocks is that they capture part of the code (including values) in an abstract object that can be manipulated as an atomic object (store it somewhere, transfer, copy, etc.)). In fact, this is implemented in such a way as to ensure that by default your block will remain valid and execute safely later.

Then you need to capture and save the necessary dependencies inside the block.

Unfortunately, in some cases (quite often actually) the block is saved by the instance that creates it, and saves this instance. This is called a hold cycle and makes your object and your block impossible to exclude unless you break one of the hold relationships yourself. This can happen if you reference your block with an instance variable, for example, and you do not manually clear it.

This is probably the main problem with blocks, especially because once you do not know that your block saves your own instance (for example, NSAssert in your block). Then:

  • If you immediately start your block and release it (use your block and send it free after execution) there is no risk, since you are sure that your object that is referenced by itself still exists.

  • But if execution is delayed, it is important to keep your object inside your block. But in this case, your object should not save your block in order to avoid a save cycle (A saves B and B saves A). If you define and possibly refer to your block in the private area of ​​a method, that’s fine.

  • About copying. Yes, it might be safer to use a copy if your block is passed as an argument to the method to make sure you have a clean exclusive block in this area with +1 keepCount. But perhaps ARC will already do it for you. Not sure about that. For example, it performs the function of WhithSelector, it seems to do it for free, and then copying is not dangerous. Just useless. Sometimes the compiler can optimize this by deleting it, but it needs to be checked.

0
source

I usually do this:

 __unsafe_unretained __block id blockSelf = self; 

and then use it in my blocks without problems.

So in your case:

 __unsafe_unretained __block MyClass *blockSelf = self; [self performBlock:^{ [weakSelf doSomething]; [self performBlock:^{ [weakSelf doSomething]; } afterDelay:1.0f]; } afterDelay:delay]; 

Also, to make your life a little easier - make a utility class and put it in the header

 void RunAfterDelay(NSTimeInterval delayInSeconds, dispatch_block_t block); 

and then this in the .m file

 void RunAfterDelay(NSTimeInterval delayInSeconds, dispatch_block_t block) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC), dispatch_get_main_queue(), block); } 

Import the utilities into your prefix and you can go:

 __unsafe_unretained __block MyClass *blockSelf = self; RunAfterDelay(1.0f,^{ [blockSelf doSomething]; RunAfterDelay(delay,^{ [blockSelf doSomething]; }) }); 

I read it a little better than the detailed ones by default.

Hope this helps :)

-3
source

All Articles