objective-c Block实质

本文深入剖析了Block的本质,包括其作为带有自动变量值的匿名函数的特性,并详细解释了Block如何捕获自动变量值以及__block变量的工作原理。此外,还探讨了Block在不同存储位置间的复制机制及其对对象引用的影响。

一. Block本质
用一句话描述就是Block是带有自动变量值的匿名函数,举一个简单的例子
void(^blk)(void) = { printf(“Block\n”)};
blk();
系统底层是这样实现的
1. 创建一个结构体(命名为main_block_imp);
struct main_Block_imp{
void *FunctPtr; // 函数指针
…..(含有其他一些成员变量)
_main_Block_imp(void *func…….){
//构造函数
}
}
2. 定义一个静态函数:(包含要执行的代码块)
如 static void main_block_func(struct main_Block_imp *_cSelf){
printf(“Block\n”);
}
3.再 实例化一个结构体变量
main_block_imp temp = _main_Block_imp(main_block_func); 将上面的静态函数以参数的形式传入
4. 创建自动变量
创建结构体实例指针void(^blk)(void),
blk = & temp;
5. 执行blk();
*blk->FunctPtr(blk)

二 截获自动变量值
int val = 10;
void(^blk)(void) = { printf(“val = %d”, val);};
blk();
1. 系统自动创建一个静态函数
static void main_block_func(struct main_Block_imp *_cSelf){
int val = _cSelf -> val;
printf(“val = %d”, val);
}
2. 创建一个结构体实例
struct main_Block_imp{
void *FunctPtr; // 函数指针
int val;
…..(含有其他一些成员变量)
_main_Block_imp(void *func, int tempVal …….) : (val(tempVal)){
//构造函数
}
3. 原代码void(^blk)(void) = { printf(“val = %d”, val);};
void(*blk)(void) = &_main_Block_imp(main_block_func, val);
4. 原代码blk();
blk->FunctPtr(blk);
总结:也就是说变量val是按值传递的方式复制到带有自动变量的结构体struct main_Block_imp中

__block 变量

__block int val = 10;
void(^blk)(void) = { printf(“val = %d”, val);};
val = 20;
blk();


  1. __block int val = 10;
    系统会自动创建一个结构体实例
    struct __blockVal {
    __blockVal * self; (指向该实例自己的指针)
    int val; (这就是使用值,初始化为10)
    }
  2. 而上面提到的main_Block_imp将包含一个指向__blockVal实例的指针
  3. 给__block 变量赋值,就是该变量__blockVal实例的成员变量val

全局变量/全局静态变量和静态局部变量在block中的使用

这些变量在block中使用和平常使用一致,实际在block中传递的是该变量的指针

block变量和对象

代码:__block id obj = [[NSObject alloc]init];实际上就等于:
__block id __strong obj = [[NSObject alloc]init];

系统转换成__block变量用结构体形式如下:
struct __Block__obj {
void *__isa;
__Block__obj *forwarding; //指向自己内存地址的指针
void (* __Block__obj__copy)(void*, void *);
void (* __Block__obj__dispose)(void*);
__strong id obj; // 对对象的强引用
}

static void __block__obj__copy(void* dst, void *src){
_block_object_assign(……..);
}
statuc void __block__obj_dispose(void *src){
_block_object_dispose(……..);
}

当Block从栈复制到堆时,使用__block__obj__copy函数,持有Block截获的对象,当堆上的Block被废弃时,使用__block__obj_dispose函数释放所持有的对象。所以如果block被复制到堆上,只有block存在,那么所截获的对象就不会释放。

那么一开始在栈上的Block什么时候会复制到堆上呢?有以下几种情况会将Block从栈上复制到堆上:

  1. 调用Block的copy实例方法
  2. Block作为函数返回值返回时
  3. 将Block赋值给附有__strong修饰符id类型的类或者Block类型成员变量时
  4. 在方法中含有usingBlock的Cocoa框架方法或者Grand Central Dispatch(GCD)的API中传递Block时。

    注意:将Block从栈上复制到堆上时是比较耗CPU的,所以尽量避免这种情况。

所以在定义@property (nonatomic, strong/copy) (void)(^my_block)(void);
block类型的属性变量时,要用copy和strong,在ARC环境下,copy和strong本质是一样的,都是把block从声明的栈中复制到堆,不然它的生命周期随着栈走,随时可能被收回

block的循环引用

示例代码:
typedef void (^blk_t)(void);
@interface Myobject : NSObject
{
blk_t _block;
}
@end

@implementation Myobjec
- (id)init
{
self = [super init];
_block = ^{NSLog{@”self = %@”, self}; };
return self;
}
- (void)dealloc
{
NSLog(@”dealloc”);
}

int main()
{
id object = [[Myobject alloc]init];
return 0;
}
该代码中的dealloc方法一定没有被调用
将Block赋值给带有__strong修饰符的成员变量的时候,block从栈上复制到堆上,block持有self对象的强引用, 而self又持有该Block,就造成了循环引用,解决方法是用__weak 修饰符

内容概要:本文介绍了一个基于多传感器融合的定位系统设计方案,采用GPS、里程计和电子罗盘作为定位传感器,利用扩展卡尔曼滤波(EKF)算法对多源传感器数据进行融合处理,最终输出目标的滤波后位置信息,并提供了完整的Matlab代码实现。该方法有效提升了定位精度与稳定性,尤其适用于存在单一传感器误差或信号丢失的复杂环境,如自动驾驶、移动采用GPS、里程计和电子罗盘作为定位传感器,EKF作为多传感器的融合算法,最终输出目标的滤波位置(Matlab代码实现)机器人导航等领域。文中详细阐述了各传感器的数据建模方式、状态转移与观测方程构建,以及EKF算法的具体实现步骤,具有较强的工程实践价值。; 适合人群:具备一定Matlab编程基础,熟悉传感器原理和滤波算法的高校研究生、科研人员及从事自动驾驶、机器人导航等相关领域的工程技术人员。; 使用场景及目标:①学习和掌握多传感器融合的基本理论与实现方法;②应用于移动机器人、无人车、无人机等系统的高精度定位与导航开发;③作为EKF算法在实际工程中应用的教学案例或项目参考; 阅读建议:建议读者结合Matlab代码逐行理解算法实现过程,重点关注状态预测与观测更新模块的设计逻辑,可尝试引入真实传感器数据或仿真噪声环境以验证算法鲁棒性,并进一步拓展至UKF、PF等更高级滤波算法的研究与对比。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值