22

EDIT2:

No. The suggested answer is about async calls. I want & need synchronous calls, like in a normal, standard recursive call.

EDIT:

while

__unsafe_unretained void (^unsafe_apply)(UIView *, NSInteger) ;

compiles without warning or errors, it fails at runtime with a NULL stored into unsafe_apply.

However this:

- (void) applyToView: (UIView *) view {

    UIColor * (^colorForIndex)(NSInteger) = ^(NSInteger index) {
        return [UIColor colorWithHue: ((CGFloat) index / 255.0f)
                          saturation: 0.5f
                          brightness: 0.5f
                               alpha: 1.0f] ;
    } ;

    void (^applyColors) (UIView *, NSInteger index) = ^(UIView * view, NSInteger index) {
        view.backgroundColor = colorForIndex(index) ;
    } ;

    void (^__block recurse_apply)(UIView *, NSInteger) ;

    void (^apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
        applyColors(view, level) ;
        [view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
            recurse_apply(subview, 1+level) ;
        }] ;
    } ;

    recurse_apply = apply ;

    apply(view, 0) ;
}

compiles without warnings, but more importantly, actually runs.

But this is so ugly!


consider (colouring the view hierarchy, for exposing purpose ...):

- (void) applyToView: (UIView *) view {

    UIColor * (^colorForIndex)(NSInteger) = ^(NSInteger index) {
        return [UIColor colorWithHue: ((CGFloat) (index * 10.0f) / 255.0f)
                          saturation: 0.5f
                          brightness: 0.5f
                               alpha: 1.0f] ;
    } ;

    void (^applyColors) (UIView *, NSInteger index) = ^(UIView * view, NSInteger index) {
        view.backgroundColor = colorForIndex(index) ;
    } ;

    void (^apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
        applyColors(view, level) ;
        [view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
            apply(subview, 1+level) ;
        }] ;
    } ;

    apply(view, 0) ;
}

I get this warning:

/Users/verec/Projects/solotouch/SoloTouch/BubbleMenu.m:551:42: Block pointer variable 'apply' is uninitialized when captured by block

If I apply the suggested fix: Maybe you meant to use __block 'apply'

void (^__block apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {

I then get: /Users/verec/Projects/solotouch/SoloTouch/BubbleMenu.m:554:13: Capturing 'apply' strongly in this block is likely to lead to a retain cycle

I tried various ways to tamper with the code and get rid of those warnings

__weak typeof (apply) wapply = apply ;
if (wapply) {
    __strong typeof (wapply) sappy = wapply ;
    wapply(subview, 1+level) ;
}

But things just get worse, turning into errors.

I ended up with this:

__unsafe_unretained void (^unsafe_apply)(UIView *, NSInteger) ;

void (^apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
    applyColors(view, level) ;
    [view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
        unsafe_apply(subview, 1+level) ;
    }] ;
} ;

unsafe_apply = apply ;

apply(view, 0) ;

Anyone has a better solution, where I could do everything from within the block and not hideously back patch it as I had to do here?

Note Those SO Questions are about capturing self and those SO questions don't have any satisfactory answer.

Community
  • 1
  • 1
verec
  • 5,224
  • 5
  • 33
  • 40
  • Like in my edit? But having to back-patch is really ugly, error prone. Isn't there some normal, standard way to use recursive blocks? – verec Nov 09 '13 at 23:54
  • possible duplicate of [Asynchronously dispatched recursive blocks](http://stackoverflow.com/questions/5394647/asynchronously-dispatched-recursive-blocks) In particular, see http://stackoverflow.com/a/14730061/ – jscs Nov 09 '13 at 23:55
  • No! The referred to question is about _async_ calls. I need regular, run of the mill, standard recursive calls. No threading involved. At all. – verec Nov 10 '13 at 00:25
  • The solution applies to any recursive Block. Did you try it? – jscs Nov 10 '13 at 00:32
  • Are you serious? The "solution" you point at has TWO methods, 20+ lines of code and *is* asynchronous! I am already sad that my own version has 3 unnecessary lines, and you'd suggest that I add even more clutter, and for a result which depends on GCD???? – verec Nov 10 '13 at 00:37
  • Chill out for a second and _actually read the answer_. I'm trying to help you solve your problem. There's **three** lines you need: `dispatch_block_t RecursiveBlock(void (^block)(dispatch_block_t recurse)) { return ^{ block(RecursiveBlock(block)); }; }` – jscs Nov 10 '13 at 01:36
  • @Rob Deville's post uses 2K word to show the solution which fits in 5 lines at the bottom, and is exactly (use of the __block qualifier) what I want to get rid of ... Not to mention that his use of `copy' is superfluous ... – verec Nov 10 '13 at 02:04
  • Who is Rob Deville? I [linked to an answer](http://stackoverflow.com/a/14730061) by a user named "Berik". Instead of arguing with me, why don't you just fix your problem? – jscs Nov 10 '13 at 02:06
  • The user @rob gave this link: http://ddeville.me/2011/10/recursive-blocks-objc/ but then apparently deleted his comment. I was replying to him. – verec Nov 10 '13 at 02:09
  • This apparently is an old hat: [bblum](http://www.friday.com/bbum/2009/08/29/blocks-tips-tricks/) has the same `__block` based solution and even [Twitted it](https://twitter.com/bbum/status/3438225542) ... 4 years ago .... – verec Nov 10 '13 at 02:16

3 Answers3

64

You need to capture a __block variable, because blocks capture non-__block variables by value when they are created, and the assignment happens after the block has been created.

In ARC, __block variables of object pointer type (generally all variables are implicitly __strong) are retained by the block. So if the block captures a __block variable pointing to itself, it would create a retain cycle. The solution is to have it capture a weak reference. In versions of the OS that support __weak, __weak should be used instead of __unsafe_unretained.

However, if the only reference to the block was a __weak variable, there would be no strong references to the block, which means it can be deallocated. In order to use the block, it must have a strong reference to keep it around.

Therefore, you need two variables, one weak and one strong. The proper way to do it in ARC is:

__block __weak void (^weak_apply)(UIView *, NSInteger) ;
void (^apply)(UIView *, NSInteger) ;
weak_apply = apply = ^(UIView * view, NSInteger level) {
    applyColors(view, level) ;
    [view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
        weak_apply(subview, 1+level) ;
    }] ;
} ;

apply(view, 0) ;
newacct
  • 119,665
  • 29
  • 163
  • 224
  • 1
    When I try this with the latest version of Xcode the compiler flags it with a warning "assigning block literal to a weak variable; object will be released after assignment". Since your approach works exactly around that I assume it's safe to ignore the warning. Do you know how it can be turned off? – Drux Jan 28 '14 at 19:10
  • 4
    @Drux: changed the order of assignment – newacct Jan 28 '14 at 20:23
  • I've tested this with XCode 6, but if I add an allocation inside the block, the memory won't get freed :| – Lord Zsolt Aug 25 '15 at 13:02
1

The answer is no.

We can't seem to do better than using the __block qualifier.

under ARC

__block __weak void(^weakStrawberryFields)();
__block void(^strawberryFields)() = ^() { weakStrawberryFields(); };
weakStrawberryFields = strawberryFields;
strawberryFields();

pre ARC

__block void(^strawberryFields)();
strawberryFields = ^{ strawberryFields(); };
strawberryFields();

Thanks to Bill Bumgarner article about blocks.

Cœur
  • 37,241
  • 25
  • 195
  • 267
verec
  • 5,224
  • 5
  • 33
  • 40
  • 4
    This snippet predates ARC; the effect of `__block` has changed since then, and using it without `__weak` creates a retain cycle: http://stackoverflow.com/a/9303704/ – jscs Nov 10 '13 at 02:43
  • @jscs answer updated for ARC – Cœur Oct 07 '22 at 11:54
0

To avoid ARC warnings and building on @newacct 's answer, I found that setting the weak block inside the retained block works:

//a block definition
typedef void (^CompletionType)(NSDictionary * __nullable response, NSError * __nullable error);


//a block calling itself without the ARC retain warning
__block CompletionType completionBlock = nil;
__block __weak CompletionType weakCompletionBlock = nil;
completionBlock = ^(NSDictionary *response, NSError *error) {
    weakCompletionBlock = completionBlock;

        weakCompletionBlock();

    });
};
Dominic
  • 258
  • 2
  • 8
  • 3
    But then `completionBlock` is a strongly-held object that itself has a strongly-held reference to `completionBlock`. So you've got a retain cycle, exactly the same as if you eliminated all mention of `weakCompletionBlock`. – Tommy Aug 09 '16 at 14:08