LOGIC – Block

一、block 的种类
{
    int a = 10;  // 栈

    // __NSGlobalBlock__   全局 block(没有引入外部变量)
    void (^ block)(void) = ^ {
        NSLog(@"哈哈哈");
    };
    NSLog(@"%@", block);  // %@ -》 对象 -》 NSObject -》objc_object
    
    // __NSMallocBlock__  堆 block
    void (^ block2) (void) = ^ {
        NSLog(@"哈哈哈 :%d", a);
    };
    NSLog(@"%@", block2);
    
    // __NSStackBlock__ 栈 block
    NSLog(@"%@", ^ {
        NSLog(@"哈哈哈 :%d", a);
    });
}

①、全局 blockdata 段;

②、a 在栈上,跨域请求就把全局的 block 放入栈中;

③、重写操作符 “c”,执行 copy 操作,objc 源码中的 _block_copy() 方法;

// 全局(data) -》栈 -》重写 "="运算符 -》堆
id objc_retainBlock(id x) {
    return (id)_Block_copy(x);
}

2 1

 

二、解决循环引用
typedef void(^Block)(void);

@interface ViewController ()
@property (nonatomic, copy) Block block;
@property (nonatomic, copy) NSString *  name;
@end

- (void)viewDidLoad
{
    [super viewDidLoad];
       
    self.name = @"哈哈哈";
    self.block = ^ {
        NSLog(@"%@", self.name);  // 产生循环引用
    };
    self.block();
}

- (void)dealloc
{
    NSLog(@"dealloc");
}

3

错误解决实例:

    __weak typeof(self) weakSelf = self;
    self.block = ^ {
        weakSelf.name = @"哈哈哈";
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", weakSelf.name);
        });
    };
    self.block();

self 调用 dealloc 之后,NSLog(@"%@", weakSelf.name); 将输出 (null)

 

解决方法 1

    __weak typeof(self) weakSelf = self;
    self.block = ^ {
        weakSelf.name = @"哈哈哈";
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            __strong typeof(self) strongSelf = weakSelf;
            NSLog(@"%@", strongSelf.name);
        });
    };
    self.block();

使用 __strong__weak

 

解决方法 2

    __block ViewController * blkVC = self;
    self.block = ^ {
        blkVC.name = @"哈哈哈";
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", blkVC.name);
            blkVC = nil; // 关键,不然 dealloc 不会调用
        });
    };
    self.block();

__block 将栈区的数据 copy 一份到堆区。

4

 

解决方法 3

循环引用的实质:self -> block -> self

typedef void(^Block)(ViewController *);

    self.block = ^ (ViewController * vc){
        vc.name = @"哈哈哈";
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", vc.name);
        });
    };
    self.block(self);  // 传值

block 内不使用 self,使用传递的值

 

三、操作捕获的变量
    int a = 10;  // 栈
    
    // block 在堆中
    self.block = ^ {
        a++;
    };
    self.block();

5

使用 __block

    __block int a = 10;  // 栈
    
    NSLog(@"1 --- %p", &a);
    
    self.block = ^ {
        a++;
        NSLog(@"2 ---- %p", &a);
    };
    self.block();
    NSLog(@"3 ---- %p", &a);


1 --- 0x7ffeed8438e8
2 ---- 0x600002059578
3 ---- 0x600002059578

经验:

0x7 在栈,0x6 在堆,0x1 在全局静态区(区分已初始化和未初始化的)。

__block 的作用是从栈区复制到堆区