美文网首页
OC关于block探究

OC关于block探究

作者: GaoEnron | 来源:发表于2019-04-06 17:45 被阅读0次
Block分类

主要是根据内存管理进行分类

iOS中.m文件重写为C++文件

使用的命令行

clang 可以将OC源程序重写为.cpp的代码
clang  -rewrite-objc  main.m
一、Block根据内存分配分为三类
NSGlobalBlock 全局静态
NSMallocBlock 堆block
NSStackBlock 栈Block
二、Block无参数无返回值
Block : 保存一段代码块
持有 - 随时随地
匿名函数 --> block --> 对象
 // 无参数无返回值
void(^globleBlock)(void) = ^{
        NSLog(@"block调用");
};
globleBlock(); // block调用

NSLog(@"block = %@", globleBlock); //打印相应的类型 
block = <__NSGlobalBlock__: 0x1035760a0>
三、Block无参数无返回值访问外部局部变量

分配在堆上的Block

// 无参数无返回值
int a = 10;
// block 特性:自动捕获变量
对象底层:struts
/// 增加属性
/// 栈  ---> 操作符重写 --> copy
void(^mallocBlock)(void) = ^{
       NSLog(@"block调用访问外部变量:%d", a);
};
mallocBlock(); // block调用
/// 打印相应的类型
NSLog(@"block = %@", mallocBlock);
block = <__NSMallocBlock__: 0x600001643270>
四、不需要别名Block访问外部变量

分配在栈上的Block

 int a = 10;
    NSLog(@"block = %@", ^{
        NSLog(@"block = %d", a); //  block = <__NSStackBlock__: 0x7ffeed2379a8>
    });
五、内存指针小常识

0x7: 是栈指针开头 例如:<NSStackBlock: 0x7ffeed2379a8>
0x6: 是堆指针开头 例如: <NSMallocBlock: 0x600001643270>
0x1: 全局静态指针开头 <NSGlobalBlock: 0x1035760a0>

六、NSLog本质 --print

I/O操作比较耗时
方法本质:消息

七、Block循环引用
typedef void(^KCBlock)(void);
@interface ViewController ()
@property(strong ,nonatomic)Person * p;
/// 定义一个Block
@property (nonatomic, copy) KCBlock block;
/// 定义一个字符串的
@property (nonatomic, copy) NSString *name;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.name = @"正在使用的BLock";
    __weak typeof(self) weakSelf = self; // 导致循环引用使用__weak 
    self.block = ^{
        NSLog(@"输出形影的:%@", weakSelf.name);
    };
}
八、Block使用弱引用提前释放
  1. 界面已经消失,回调到才回来,导致无法执行后续方法
    self.name = @"正在使用的BLock";
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        //NSLog(@"输出形影的:%@", weakSelf.name);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            NSLog(@"当前打印:%@", self.name);// 获取的循环打印问题
        });
    };
  1. 解决提前释放的问题
需要延迟释放时候使用 __strong
__weak typeof(self) weakSelf = self;
 __strong typeof(weakSelf) strongSelf = weakSelf;
 self.name = @"正在使用的BLock";
 __weak typeof(self) weakSelf = self;
 __strong typeof(weakSelf) strongSelf = weakSelf;
 self.block = ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            NSLog(@"当前打印:%@", self.name);// 获取的循环打印问题
        });
 };
9、Block使用解决循环引用

1、 __block ViewController *vc = self; 解决循环引用,但会导致提前释放问题,没有走delloc函数
2、__block 会进行一次copy操作,对捕获的ViewController执行copy操作到当前的struct中被block持有
3.vc = nil时候释放当前的引用,内存回收

1. __block解决循环引用
    self.name = @"正在使用的BLock";
    __block ViewController *vc = self;
    self.block = ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            NSLog(@"当前打印:%@",vc.name);// 获取的循环打印问题
        });
    };
2. 上面__block导致delloc没有执行解决问题
  1. 要在异步回调中将 __block ViewController *vc = self;
    延迟执行的函数执行完毕释放掉 将 vc = nil
当前的self持有block  当前block又持有当前的vc 
当vc = nil 打破循环引用从而内存正常释放
self -> block --> vc --> self
  __block ViewController *vc = self;
  self.block = ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            NSLog(@"当前打印:%@",vc.name);// 获取的循环打印问题
            vc = nil
        });
    };
3. 解决循环引用可以直接使用传递当前的控制器解决
///
- (void)methedVCBlock {
    self.name = @"传递VCBlock实现解决循环引用问题";
    self.vcBlock = ^(ViewController *vc) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            NSLog(@"当前打印:%@", vc.name);
        });
    };
}
十、使用__block 能够在block中捕获当前的变量
  • 没有__block 是block进行捕获变量之后传递是值传递
  • 有__block 是block进行捕获变量之后进行的是指针传递
// 指针的使用
- (void)fourMethed {
    __block int a = 10; // 拷贝到堆区
    NSLog(@"进去之前时候: %p", &a);
    void(^myFirstBlock)(void) = ^{
         a++;
        NSLog(@"进去之后进行处理:%p", &a);
    };
    NSLog(@"执行完毕之后查看指针地址:%p", &a);
    myFirstBlock();
    
}
十一、auto static 修饰的变量
  1. 自动变量可能会销毁,block在执行的时候可能自动变量已经被销毁,那么此时如果再去访问被销毁的地址肯定会发生坏内存访问,因此对于自动变量一定是值传递而不可能是指针传递了。
  2. 静态变量不会被销毁,所以完全可以传递地址。因为传递的是值的地址,所以在block调用之前修改地址中保存的值,block中的地址是不会变得。所以值会随之改变。
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int a = 10;
        static int b = 11;
        void(^block)(void) = ^{
            NSLog(@"输出的结果是:, a = %d, b = %d", a,b);
        };
        a = 1;
        b = 2;
        block();
       };
    }
    return 0;
}

从下面c++代码可以知道,传递a 是值传递 而b是指针传递

这是上面代码重写为C++代码  
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a; // 传递的值
  int *b; // 传递的是指针
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int *_b, int flags=0) : a(_a), b(_b) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
十二、当变量为全局变量时候
int a = 10;
static int b = 11;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void(^block)(void) = ^{
            NSLog(@"hello, a = %d, b = %d", a,b);
        };
        a = 1;
        b = 2;
        block();
    }
    return 0;
}

转换为C++代码如下

int a = 10;
static int b = 11;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_s__l2kw7t3j7gv43m07ydztp7pc0000gn_T_main_7d7391_mi_0, a,b);  // 打印数调用直接传递 a, b 值
}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        a = 1;
        b = 2;
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

总结:

  • 局部变量都会被block捕获
  • 自动变量是值捕获
  • 静态变量为地址捕获
  • 全局变量则不会被block捕获

相关文章

  • OC关于block探究

    Block分类 主要是根据内存管理进行分类 iOS中.m文件重写为C++文件 使用的命令行 一、Block根据内存...

  • OC:深入探究 block

    主要分析了block在持有__block、__weak、__strong修饰的对象时,block结构发生的变化。 ...

  • OC中block底层原理总结(下)

    关于OC中block的本质结构、block的变量捕获方式请查看OC中block底层原理总结(上)需要先看懂上篇文章...

  • iOS 循环引用

    关于循环引用看着3篇文章就够了,拿走不谢! 循环引用 循环引用 OC中的block OC中的block 关于 bl...

  • iOS block的本质

    Block探究 block的本质问题 block其实是一个oc对象,因为其结构体第一个成员为class类型的isa...

  • 关于block(4)

    关于block(4) 标签: iOS 技术 接上篇,我们继续探究block。 block的copy属性 研究到这里...

  • OC block 内存泄漏探究

    可能大家都知道如果在block中显式或隐式地引用了self(比如引用了self的成员变量等),则会引发内存泄漏,但...

  • OC中Block的探究

    Block简介 Block 能够让我们的代码变得更简单,能够减少代码量,降低对于 delegate 的依赖,还能够...

  • Block学习总结(三)

    关于block的存储域 一、 block变量存储域 1. ARC和MRC不同的存储情况 通过对block本质的探究...

  • swift 调用 OC中的block

    OC中声明block; OC中实现block swift中实现

网友评论

      本文标题:OC关于block探究

      本文链接:https://www.haomeiwen.com/subject/dicriqtx.html