Objective-C创建弱块来检查内存释放

nr5*_*nr5 0 objective-c ios automatic-ref-counting

我已经读过如果你将一个新对象分配给一个弱属性,那么该对象将在赋值后被释放.甚至编译器的警告都是一样的.

@interface RetainCycleObjCViewController ()
{
}
@property (nonatomic, weak) void (^weakBlock)(void);
@end

@implementation RetainCycleObjCViewController
- (void)viewDidLoad {
    [super viewDidLoad];

 _weakBlock = ^void{
        NSLog(@"Execution inside a weakBlock");
    };

_weakBlock();
}
@end
Run Code Online (Sandbox Code Playgroud)

我得到了与weakBlock相同的警告:将块文字分配给弱变量; 对象将在分配后释放

但是当我在下一行执行_weakBlock()时,它仍会打印语句.这怎么可能?因为现在应该从内存中删除新创建的块对象,给定0引用计数?

bbu*_*bum 6

您实际上并没有在该代码中分配块.块体是全部静态内容; 编辑后没有任何改变.

如果你这样做:

    _weakBlock = ^void{
        NSLog(@"Execution inside a weakBlock");
    };
    NSLog(@"%@", [_weakBlock class]);
Run Code Online (Sandbox Code Playgroud)

你会看到这个:

2018-10-21 09:33:48.827423-0500 kdkdkdkds[9367:1777827] __NSGlobalBlock__
Run Code Online (Sandbox Code Playgroud)

编译器识别出该块不捕获可能在运行时更改的任何内容,因此创建了永远不会分配的全局静态块.

正如@Brandon在评论中所说,如果你在你的作用域中声明了一个局部变量并导致分配了一个堆栈块,那么在作用域结束之前不会消失.

@autoreleasepool {
    void __weak (^_weakBlock)(void);
    int k = 2;
    _weakBlock = ^void{
        NSLog(@"Execution inside a weakBlock (%d)", k);
    };
    NSLog(@"C; %@", [_weakBlock class]);
    NSLog(@"P; %p", _weakBlock);
}
2018-10-21 09:36:31.585458-0500 kdkdkdkds[9392:1780803] C; __NSStackBlock__
2018-10-21 09:36:31.586291-0500 kdkdkdkds[9392:1780803] P; 0x7ffeefbff4c0
Run Code Online (Sandbox Code Playgroud)

现在,实例变量赋值会稍微更改规则,因为您要分配超出范围.

@interface SpongeBob:NSObject
@end

@interface SpongeBob ()
{
}
@property (nonatomic, weak) void (^weakBlock)(void);
@end

@implementation SpongeBob
- (instancetype)init
{
    self = [super init];
    if (self) {
        int k = 2;
        _weakBlock = ^void{
            NSLog(@"Execution inside a weakBlock (%d)", k);
        };
        NSLog(@"C; %@", [_weakBlock class]);
        NSLog(@"P; %p", _weakBlock);
    }
    return self;
}
@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [[SpongeBob alloc] init];
    }
    return 0;
}

2018-10-21 09:43:39.518545-0500 kdkdkdkds[9480:1787889] C; __NSStackBlock__
2018-10-21 09:43:39.518883-0500 kdkdkdkds[9480:1787889] P; 0x7ffeefbff488
Run Code Online (Sandbox Code Playgroud)

等待.什么?如果这个值真的被分配给了伊达,那么这就是定时炸弹.那么,让我们尝试一下:

如果我们添加:

- (void)squarePants
{
    NSLog(@"C; %@", [_weakBlock class]);
    NSLog(@"P; %p", _weakBlock);
}
Run Code Online (Sandbox Code Playgroud)

然后叫它:

    SpongeBob *bob = [[SpongeBob alloc] init];
    [bob squarePants];
Run Code Online (Sandbox Code Playgroud)

它正如预期的那样崩溃,因为堆栈块不再作为init范围有效

(这似乎很有意思.再说一次,如果你消除警告,那么崩溃就会消失.)

现在,如果你在赋值时复制块,那么分配时的立即释放确实会启动:

    _weakBlock = [^void{
        NSLog(@"Execution inside a weakBlock (%d)", k);
    } copy];

2018-10-21 09:48:04.295762-0500 kdkdkdkds[9510:1792549] C; (null)
2018-10-21 09:48:04.296167-0500 kdkdkdkds[9510:1792549] P; 0x0
Run Code Online (Sandbox Code Playgroud)