Block对变量的引用

news/2024/7/20 22:22:39 标签: ios, objective-c, 开发语言, macos

如果在 Block 内部使用外部的变量,Block 会持有这个变量。下面来看几中特殊的情况,看 Block 对变量的持有情况如何。

typeof

@interface X : NSObject

@property (nonatomic, copy) void(^testBlock1)(void);
@property (nonatomic, copy) void(^testBlock2)(void);
@property (nonatomic, copy) NSString *str;

@end

@implementation X

- (void)test {
    self.testBlock1 = ^{
        typeof (self) x;
    };
}

@end

上面代码中, test 方法里面设置了 Block,并且 Block 里面有一句 typeof (self) x,那么在这种情况下,Block 会持有 self 吗?

使用 clang -rewrite-objc 命令,查看 c++ 代码,代码如下:

struct __X__test_block_impl_0 { // Block实现,没有持有 self
  struct __block_impl impl;
  struct __X__test_block_desc_0* Desc;
  __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // Block方法实现

  X * x; // typeof (self) x;
 }

从代码可以看到,typeof (self) x 被替换成了 X *x,因此这种情况下 Block 不会持有 self。

sizeof

将 test 方法修改如下:

- (void)test {
    self.testBlock1 = ^{
        size_t i = sizeof (self);
    };
}

在上面的方法中,Block 内部有使用 sizeof (self),那么 Block 会持有 self 吗?

同样查看 c++ 代码,代码如下:

struct __X__test_block_impl_0 { // Block实现
  struct __block_impl impl;
  struct __X__test_block_desc_0* Desc;
  X *self; // 持有了 self
  __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, X *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // Block方法
  X *self = __cself->self; // bound by copy

  size_t i = sizeof (self);
 }

可以看到,在使用 sizeof 时,Block 是持有了 self的。

是否会持有属性

将 test 方法修改如下:

- (void)test {
    self.testBlock1 = ^{
        self.str = @"KK"; // 设置属性
    };
}

@end

上面代码中,Block 除了会持有 self,还会持有属性 str 吗?

查看 c++ 代码,代码如下:

struct __X__test_block_impl_0 { // Block实现
  struct __block_impl impl;
  struct __X__test_block_desc_0* Desc;
  X *self; // 只持有了self,未持有属性str
  __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, X *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // Block方法实现
  X *self = __cself->self; // bound by copy

  ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setStr:"), (NSString *)&__NSConstantStringImpl__var_folders_p6_jy49zvqx2656qb8rbq64hc_w0000gn_T_variable_a3a045_mi_0); // 调用设置属性方法
 }

可以看到,Block 没有持有属性 str。

是否会持有实例变量

将 test 方法修改如下:

- (void)test {
    self.testBlock1 = ^{
        _str = @"KK"; // 调用实例变量
    };
}

此时,Block 会持有实例变量吗?

查看 c++ 代码,代码如下:

struct __X__test_block_impl_0 { // Block实现
  struct __block_impl impl;
  struct __X__test_block_desc_0* Desc;
  X *self; // 只持有self,未持有实例变量
  __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, X *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // Block方法实现
  X *self = __cself->self; // bound by copy

  (*(NSString **)((char *)self + OBJC_IVAR_$_X$_str)) = (NSString *)&__NSConstantStringImpl__var_folders_p6_jy49zvqx2656qb8rbq64hc_w0000gn_T_variable_d40340_mi_0; // 使用偏移设置实例变量
 }

可以看到,Block 没有持有实例变量。

嵌套Block

将 test 方法修改如下:

- (void)test {
    int i = 1;
    self.testBlock1 = ^{
        self.testBlock2 = ^{ // 嵌套Block
            self.str = @"KK";
            int j = i;
        };
    };
}

上面代码中使用了嵌套 Block,内层Block使用了变量 i,而外层 Block 没有使用变量 i,外层 Block 是否会持有变量 i 呢?

查看 c++ 代码,代码如下:

struct __X__test_block_impl_0 { // 内层Block实现
  struct __block_impl impl;
  struct __X__test_block_desc_0* Desc;
  X *self; // 持有了self
  int i; // 持有了变量i
  __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, X *_self, int _i, int flags=0) : self(_self), i(_i) { 
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // 内层Block 方法实现
  X *self = __cself->self; // bound by copy
  int i = __cself->i; // bound by copy

   ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setStr:"), (NSString *)&__NSConstantStringImpl__var_folders_p6_jy49zvqx2656qb8rbq64hc_w0000gn_T_variable_926b0d_mi_0);
   int j = i;
  }



struct __X__test_block_impl_1 { // 外层Block实现
  struct __block_impl impl;
  struct __X__test_block_desc_1* Desc;
  X *self; // 持有了self
  __X__test_block_impl_1(void *fp, struct __X__test_block_desc_1 *desc, X *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __X__test_block_func_1(struct __X__test_block_impl_1 *__cself) { // 外层Block方法实现
  X *self = __cself->self; // bound by copy

  ((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setTestBlock2:"), ((void (*)())&__X__test_block_impl_0((void *)__X__test_block_func_0, &__X__test_block_desc_0_DATA, self, i, 570425344)));
 }

从代码上看,内层 Block 持有 self 和变量 i,外层 Block 只是持有了 self,并未持有变量 i。


http://www.niftyadmin.cn/n/724580.html

相关文章

小知识

临时起意,试了几个CMD命令.net user 用户名 密码 /add >命令行状态下增加用户名net user 用户名 密码 >修改该用户的登录密码net localgroup administrators 用户名 /add >将该用户提升到管理员权限.net user 用户名 /delete >删除用户转载于:https://blog.…

多线程的学习笔记

线程的简单学习笔记: 1、进程与线程的概念 进程:从用户角度看进程是应用程序的一个执行过程。 从操作系统核心角度看进程代表的是操作系统分配的内存和CPU时间片等资源的基本单位,是为正在运行的程序提供的运行环境。 线程:是程序…

@weakify 与 @strongify 实现原理

为了解决 Block 造成的循环引用,iOS 开发过程中常常使用 weakify 与 strongify 来解决这个问题。下面就来看下 weakify 与 strongify 的实现原理。 准备知识 宏参数(Arguments)的扩展 可变参数宏 宏定义中的重复副作用 宏定义里面为什么要加括号? Block对…

WinForm中使用XtraGrid控件,实现在界面中动态修改列显示,列名列宽等

在使用XtraGrid的gridControl或DataGridView中,里面栏目的设置比较麻烦 。为此我找出了一个比较简便的解决方法。 大致思路如下:定义一个和表结构类似的XML文件,保存表字段的显示标题、是否显示、宽度等信息,在GridControl显示的时…

memcache 详解

memcache函数所有的方法列表如下: Memcache::add – 添加一个值,如果已经存在,则返回false Memcache::addServer – 添加一个可供使用的服务器地址 Memcache::close – 关闭一个Memcache对象 Memcache::connect – 创建一个Memcache对象 memc…

POJ 1631 Bridging signals 2533 Longest Ordered Subsequence

两个都是最长上升子序列,所以就放一起了 1631 因为长度为40000,所以要用O(nlogn)的算法,其实就是另用一个数组c来存储当前最长子序列每一位的最小值,然后二分查找当前值在其中的位置;如果当前点不能作为当前最长子序列…

iOS LLVM 中的宏定义

在阅读 Objc 库源码时常常会遇到很多宏定义,比如宏 SUPPORT_INDEXED_ISA、SUPPORT_PACKED_ISA,代码如下所示: // Define SUPPORT_INDEXED_ISA1 on platforms that store the class in the isa // field as an index into a class table.// Note, keep th…

配电箱的组价方法

怎样编制配电箱的预算价格<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />编者按&#xff1a;1、本文作者提供的“配电箱预算价格编制方法”是一种经验公式&#xff0c;在没有配电箱成品价格的情况下编制电气安装工程预算(招投标…