1
votes

As the title states, in ARC mode, when I define a block using self reference (weak to avoid reference cycle):

...
id __weak weakSelf = self;
self.someBlock = ^(){
    if (weakSelf != nil){
        (do something with weakSelf...)
        return NO;
    }
    return YES;
};
...

In dealloc I call the block:

- (void)dealloc {
    ...
    BOOL isNil = self.someBlock();
    ...
}

Then I found isNil = YES.

When I want blocks in dealloc to do something with self, this seems to be a problem.

This also happen in callback functions, which I will not give an example here.

My solution is using __unsafe_unretained instead of __weak.

But this looks ugly.

Is there better ways to avoid nil weak self in dealloc?

Updated Question: Why weakSelf is niled even when self is not? Is it the reason of dealloc?


To make it clear, I paste the test code below:

#import <Foundation/Foundation.h>

@interface blockWeak : NSObject

@property (nonatomic, strong) BOOL (^someBlock)();
@property (nonatomic) int num;

- (void)makeBlock;

@end

@implementation blockWeak

- (void)makeBlock
{
    typeof(self) __weak weakSelf = self;
    self.someBlock = ^(){
        typeof(self) strongSelf = weakSelf;
        strongSelf.num = 2;
        return weakSelf == nil?YES:NO;
    };
}

- (void)dealloc
{
    BOOL isNil = self.someBlock();
    if (isNil) {
        NSLog(@"weakSelf is nil.");
    }
}

@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        blockWeak *bw = [[blockWeak alloc] init];
        bw.num = 1;
        [bw makeBlock];
    }
    return 0;
}
2
Apart from the apparent error mentioned in the answer, what's your use case? Checking if weakSelf has been already nilled in dealloc doesn't seem to be something you should have to do.Sulthan
The real use case is, I need the block to do some clean up job other than memory freeing in dealloc, which may use properties/ivars of the class or even self. I modify the question a bit to make it more clear.Juny
you can call anything in dealloc, even using self.Sulthan
I've tried that. Using self is OK. I copy the code on the block to dealloc and it works. But when you use blocks/functions that hold a weak pointer to self in dealloc, it doesn't work because when the program goes inside the block/function, "weakSelf" becomes nil. That's why I'm confused.Juny

2 Answers

1
votes

You do not call the block. That would be:

BOOL isNil = self.someBlock();
1
votes

The below works, and in my opinion is cleaner as no reference to self is then held. Also there is no real need for the return BOOL of the block now, but I've left this in...

#import <Foundation/Foundation.h>

@interface blockWeak : NSObject

@property (nonatomic, copy) BOOL (^someBlock)(blockWeak *);
@property (nonatomic) int num;

- (void)makeBlock;

@end

@implementation blockWeak

- (void)makeBlock
{
    self.someBlock = ^BOOL(blockWeak *object){
        object.num = 2;
        return object == nil?YES:NO;
    };
}

- (void)dealloc
{
    NSLog(@"num = %d", _num);
    BOOL isNil = _someBlock(self);
    if (isNil) {
        NSLog(@"block returned NO");
    } else {
        NSLog(@"block returned YES");
    }
    NSLog(@"num = %d", _num);
}

@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        blockWeak *bw = [[blockWeak alloc] init];
        bw.num = 1;
        [bw makeBlock];
    }
    return 0;
}

Output:

num = 1
block returned YES
num = 2