0

I have an object with a property that points to a block:

typedef void (^ThingSetter)();
@property(nonatomic, strong) ThingSetter setup;

I initialize the property with a block. Within the block I refer to the object instance:

Thing *thing = [[Thing alloc] init];

thing.setup = ^() {

    plainOleCFunction(thing.number);
    [thing doSomethingWithString:@"foobar"];
};

However I get compile warnings about a retain loop:

capturing 'thing' strongly in this block is likely to lead to a retain cycle
block will be retained by the captured object

What is the correct way to do this?

Thanks, Doug

dugla
  • 12,774
  • 26
  • 88
  • 136

2 Answers2

4

you have to assign thing as weak ref:

Thing *thing = [[Thing alloc] init];
__weak Thing *weakThing = thing;

thing.setup = ^() {

    plainOleCFunction(weakThing.number);
    [weakThing doSomethingWithString:@"foobar"];
};

or you could provide thing as Parameter to the block:

Thing *thing = [[Thing alloc] init];

thing.setup = ^(Thing *localThing) {

    plainOleCFunction(localThing.number);
    [localThing doSomethingWithString:@"foobar"];
};
thing.setup(thing);
Jonathan Cichon
  • 4,396
  • 16
  • 19
  • 2
    It's worth noting that depending on your compile settings, accessing weak pointers can also be treated as warnings. Your answer is correct, but to be extra careful it's often worth assigning the weakThing pointer to another strong pointer inside the block to make sure it stays around while you're messaging it within the block. – Jessedc Feb 09 '13 at 13:56
  • 1
    @Jessedc true. You could also assign it as `__block` and set the variable to nil after it was used. I would prefer my second solution in the context of the question, as i dont see the need of using variables from outside the block. – Jonathan Cichon Feb 09 '13 at 14:03
  • Thanks Jonathan. My motivation was to try and avoid passing an instance to an instance method - thing.setup(thing). It looks a bit odd and could confuse dev team members. That was my previous implementation and it works fine. I guess I'll go back to it. Cheers. – dugla Feb 09 '13 at 15:26
  • @JonathanCichon: that would only work if the block is always run exactly once – newacct Feb 09 '13 at 21:20
1

Because you are using "thing" inside the block, block will maintain a strong pointer to "thing" until the block goes out of scope or the block itself leaves the heap (i.e. no one points strongly to the block anymore) - which is what you want because blocks are first traveled and then the code is executed at some later point so you obviously do not want the block to loose the pointer to "thing" after the block is first traveled without execution.

Now, you have the block maintaining a strong pointer to "thing" and you have "thing" maintaining a strong pointer to the block through its property "setup". Neither "thing" nor the block can ever escape the heap now. That’s because there will always be a strong pointer to both of them (each other’s pointer). This is called a memory “cycle.”

The answer is to declare "thing" as weak:

Thing *thing = [[Thing alloc] init];
__weak Thing *weakThing = thing;

And use "weakThing" in the block. This solves the problem because now the block only has a weak pointer to "weakThing" . "thing" still has a strong pointer to the block through its property setup, but that’s okay.

Hope this was helpful

Khaled Barazi
  • 8,681
  • 6
  • 42
  • 62