1
//Parent.m
#import "Parent.h"
@implementation Parent{
    dispatch_block_t  _block;
    NSTimer  *_timer;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        [self commonInitialization];
    }
    return self;
}

-(void)commonInitialization{
    __unsafe_unretained typeof(self) weakSelf=self;
    //__weak typeof(self) weakSelf=self; the same conculsion

    //apple doc:The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated.

    _timer=[NSTimer timerWithTimeInterval:0.5 target:weakSelf selector:@selector(sayHello) userInfo:nil repeats:YES];// make weakSelf retain count +1

    _block=^{
        __strong Parent *parent=weakSelf;//also make weakSelf retain count +1,but the actual is that this wont make self retain count +1

        [parent sayHello];//
    };

   // my question is why weakSelf can make _block wont retain self,but _timer will retain self,it look like contradictory
}

-(void)sayHello{
    NSLog(@"hello");
}

-(void)dealloc{
    NSLog(@"Parent instance can dealloc");
}

@end

In _block I retain weakSelf again like NSTimer will retain target:weakSelf.

My question is why __unsafe_unretained or __weak can make _block unretained self but _timer not. It looks contradictory.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
9527
  • 13
  • 3

3 Answers3

0

Retain cycles with blocks happen when a class instance has a strong reference to the class and the block, in turn, has a strong reference to that instance. By capturing a weak reference to the instance within the block (weakSelf), that captured reference can be set to nil. Making the reference within the block a strong reference only means that while the block is executing, the instance will not be dealloc'd (because the block still has a live reference).

The timer internally maintains a strong reference to the instance, just as the block does while it's executing. The main difference is that the timer is a long-lived object, and unless you invalidate it or nil all your references to it, the timer still exists, along with its strong reference to your instance.

Avi
  • 7,469
  • 2
  • 21
  • 22
  • in your mean,if the block never execute,the retain recyle of block wont happen,but i test again,the retain cyle happened even if the block never execute. my mean is just init block by self,the retain cyle happened. – 9527 May 11 '16 at 05:07
  • If you capture a weak reference, there is no retain cycle. If you capture a strong reference, there may be a retain cycle. I don't really understand what you're trying to ask. – Avi May 11 '16 at 05:18
  • Indeed, @9527, you are right about that: The retain happens, when the block is created, but depending on the strength of the reference var. A timer always retains. See my answer for a longer explanation. – Amin Negm-Awad May 11 '16 at 05:18
  • @Avi What he says is, that the execution of the block is completely meaningless. That's correct. – Amin Negm-Awad May 11 '16 at 05:20
  • No. The execution is *not* meaningless. A strong reference to a weak reference causes the object being referred to remain valid while that strong reference is itself valid. That strong reference is valid only while the block is executing, not while it exists. If it didn't work this way, there would be either no point to only capturing weak references or no point to making them strong within the block. – Avi May 11 '16 at 06:46
  • a) References point to objects. Therefore there is no "strong reference to the weak reference". b) The strong reference inside the block cannot cause any retain cycle, because it only lives inside the block and is removed automatically by ARC. So it is completely meaningless for retain cycles. c) The *captured* reference causes the retain cycle. It is captured at creation time, not at execution time. – Amin Negm-Awad May 11 '16 at 07:27
  • Sorry. I misunderstood your point. Yes, for cycles, a strong reference created within the block is irrelevant. I didn't mean to imply otherwise. – Avi May 11 '16 at 07:29
0

I really do not know, whether I understood you correctly. Do you mean with "makes retained" "retains"?

However, the difference between the two pieces of code is the time of execution and how references are handled:

Keep in mind that not object references are retained, but the object they points to.

A. Timer

{
  __weak typeof(self) weakSelf=self;
  _timer=[NSTimer timerWithTimeInterval:0.5 target:weakSelf …];
}

With this code you create an additional local var called weakSelf, that does not retain the object it points to. Moreover, the extent ("lifetime") of weakSelf ends with the C-block (not the closure what you called __block), that means with the closing }. So we have:

{
  __weak typeof(self) weakSelf=self;
  // does not retain self; no problem

  _timer=[NSTimer timerWithTimeInterval:0.5 target:weakSelf …];

  // weakSelf dies, since it is weak nothing happens.
}

In such a case it is completly meaningless to weakify self:

{
  id anotherSelf=self;
  // does retain self: +1;

  _timer=[NSTimer timerWithTimeInterval:0.5 target:weakSelf …];

  // anotherSelf dies: -1
}

This is always balanced, because ARC care about it. No problem.

So why is there a retain cycle? It is "inside the timer." According to the documentation:

The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated.

Therefore, lets go back to your example:

{
  __weak typeof(self) weakSelf=self;
  // does not retain self;

  _timer=[NSTimer timerWithTimeInterval:0.5 target:weakSelf …];
  // timer retains the object, weakSelf points to: +1.
  // self retains the timer: +1
  // result: retain cycle

  // weakSelf dies, since it is weak nothing happens.
}

What -timerWithInterval… does, does not depend of the strength of weakSelf. The method does not even see the strength of the reference. It retains an object that the argument points to. Period.

B. Block

Having a block it is different:

{
  __weak typeof(self) weakSelf=self;
  // does not retain self; no problem

_block=^{
    …
  };
  // weakSelf dies, since it is weak nothing happens.
}

As you can see, there is no problem. Why can be there a retain cycle? This is quite easy: Having a reference inside the block, the referred object is retained when the block is created (similar to timers) and the reference is strong(different to timers):

{
  __weak typeof(self) weakSelf=self;
  // does not retain self; no problem

_block=^{
   … self …  // the object self points to is retained, because self is strong. +1
   … weakSelf … // the object weakSelf points to is not retained. 
    …
  };
  // weakSelf dies, since it is weak nothing happens.
}

This reference lives as long as the block lives. You have to read it correctly: It is like the reference itself has a longer lifetime. Therefore it depends of the strength of the reference.

So there is a big difference, whether you use weakSelf or self (or any other weak or strong reference.)

What is done inside the block …:

_block=^{
  __strong  id strongSelf=weakSelf;
};

… is meaningless, because this is done, when the block is executed and the local strongSelf will immediately lose its extent. (Again with the closing }.

Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50
0

ARC and block are all compiler work, compiler will add retain release and it will translate block to struct.

NSTimer will hold a strong reference to target, so it will send retain method (it may call objc_retain(id value) in ARC) to weakSelf, self hold timer as a instance variable, retain cycle happens.

Let's take about the block, by default block will retain any NSObject it captures when the block is move from stack to heap.

the _block(in your case) is a instance variable, when it is assigned a block, the block will be send a copy method, so it will move from stack to heap.

But if object it captured is with __weak or __unsafe_unretained attribute, it won't send retain method to it. The rules are defined by compiler, so it works.

If you want to learn the detail, check the source code runtime.c, you may also need to assemble your code because runtime.c doesn't have ARC code.

If you were confused about this line of code __strong Parent *parent=weakSelf , check this answer.

Community
  • 1
  • 1
KudoCC
  • 6,912
  • 1
  • 24
  • 53