Manage objective c object memory in Block

本文探讨了Objective-C中Block的内存管理机制,包括不同情况下Block如何处理对象引用,以及如何避免循环引用等问题。提供了使用__block关键字及ARC环境下推荐的最佳实践。

Look at the document from Apple:

When a block is copied, it creates strong references to object variables used within the block. If you use a block
within the implementation of a method:
●If you access an instance variable by reference, a strong reference is made to self;
●If you access an instance variable by value, a strong reference is made to the variable.
The following examples illustrate the two different situations:

dispatch_async(queue, ^{
// instanceVariable is used by reference, a strong reference is made to self
doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable;
dispatch_async(queue, ^{
/*
localVariable is used by value, a strong reference is made to localVariable
(and not to self).
Blocks and Variables
Object and Block Variables
*/
doSomethingWithObject(localVariable);
});
To override this behavior for a particular object variable, you can mark it with the __block storage type
modifier.

使用方法总结如下:(只对应NON-ARC的情况)

  • 如果block没有被copy,则block中的变量在stack空间中被创建。一旦此scope执行完毕,此stack空间被释放。
  • 如果block被copy,则block中的所有变量被拷贝到heap中。
    此时,可以看作所有的变量都被重新拷贝了一份。
    1. 对于 int, float等基本类型的变量,不需要担心release的问题,但是赋值操作将不会影响到stack空间中的变量(即block scope外的原变量)。
    2. 对于objective c对象,如果直接调用self中变量,则self会被retain。由于在self中copy了这个block,会造成循环引用。
    解决方法是:声明__block blockSelf = self, 在block中调用blockSelf的变量。
    (注意:TestClass *blockSelf = self, blockSelf和self都指向同一个内存地址,当self先于blockSelf被释放时,blockSelf就变成了野指针!!
    真的是这样吗?实际测试过,如果加上了__block,self不会先释放,会等到block执行完之后才释放。)
  • 如果是
id localVariable = instanceVariable;
把localVariable传进block,则localVariable会被retain。 在block在最后需要release在执行完此block后,localVariable会被自动release(前提是此block要被释放)
例子:

@ property (nonatomic, copy) RequestCompleteBlock copiedCompleteBlock;

@ property (nonatomic, copy) RequestFailureBlock failure;

TestClass *testObject = [TestClass node]; self.copiedCompleteBlock = ^(NSData *data) { [self performSelector:@selector(execute:data:) withObject:testObject afterDelay:3.0f]; }; self.failure = ^(NSError *error){ testObject.name = @"wang"; }; // set request type, HTML FWURLRequest *defaultRequest = [FWURLRequest defaultRequest]; defaultRequest.requestType = FWE_REQUESTTYPE_HTML; [defaultRequest requestWithURL:url paramsDicData:params completeBlock:self.copiedCompleteBlock failBlock:self.failure];
因为在copiedCompleteBlock和failure中testObject都被retain了,但是这两个block只有一个会被调用到,所以无论如何testObject都不会被释放。

3. 如果声明了__block的objective c对象,可以理解成,此对象的指针地址被拷贝到了heap(其指针实际指向的地址并没有被拷贝),它指向实际地址空间的retain count并没有增加,所以读写都有效。也不需要进行额外的release。
注意,如上例子,如果
__block TestClass *testObject = [TestClass node];
因为testObject不会被retain,当程序运行到当前scope外时,此testObject会被release,如果在此之后copiedCompleteBlock才被调用,则程序会崩溃。
建议:
1. 对于非self的object,不要使用__block,因为在block释放时会自动释放这些object。
2. 对于self的object,
You have a few options.

1) When not using ARC, the __block qualifier on an object pointer causes it to be non-retained. Thus:

__block Whatever *weakSelf = self;
^{ [weakSelf ...]; }

2) When using ARC on 10.7/iOS5, the __weak qualifier gets the same job done, but even better. __weak qualified variables are zeroing, so they're safer to use.

3) If you're using ARC but have to support 10.6/iOS4, the __unsafe_unretained qualifier will give you a non-zeroing weak pointer.

4) My MAZeroingWeakRef library provides functionality similar to __weak, but works without ARC and on older OSes. For example:

MAZeroingWeakRef *weakSelfRef = [MAZeroingWeakRef refWithTarget: self];
^{ [[weakSelfRef target] ...]; }

Since this is a zeroing weak reference, it's safer to use than __block or __unsafe_unretained. You can get it here with a liberal license:

https://github.com/mikeash/MAZeroingWeakRef

<think>我们正在分析代码 `Struct_CAN_Manage_Object CAN2_Manage_Object = {0};` 的含义。 根据代码结构,我们可以推断以下几点: 1. `Struct_CAN_Manage_Object` 是一个自定义的结构体类型。这个结构体可能用于管理CAN总线的状态、配置、缓冲区等。 2. `CAN2_Manage_Object` 是一个变量名,从命名上看,它是用于管理CAN2(第二个CAN控制器)的对象。 3. `= {0};` 是C/C++中的初始化方式,它将结构体的所有成员初始化为0(或者更准确地说,是进行“值初始化”)。对于基本类型,初始化为0;如果是结构体成员,则递归初始化;指针初始化为NULL。 这种初始化方式在嵌入式系统中很常见,尤其是在初始化硬件相关结构体时,确保所有成员都处于已知状态,避免出现未定义行为。 参考引用[1]中的示例,我们看到在函数内部使用static局部变量,但这里没有static,所以这个变量可能是全局变量或者局部变量。具体取决于它声明的位置: - 如果是在函数内部,那么它是一个局部变量,每次函数执行时都会被初始化(但要注意,如果是局部静态变量,则只会初始化一次,但这里没有static关键字)。 - 如果是在函数外部,那么它是一个全局变量,具有静态存储期(程序运行期间一直存在),并在程序启动时初始化。 另外,引用[3]提到了unique_ptr的释放条件,但这里没有使用动态内存,所以不涉及。 引用[4]提到了控制流的相关内容,可能与CAN管理对象的作用有关,即这个结构体可能包含了控制决策流所需的状态信息。 因此,我们可以这样解释: 代码 `Struct_CAN_Manage_Object CAN2_Manage_Object = {0};` 的含义是: - 定义了一个名为 `CAN2_Manage_Object` 的变量,其类型为 `Struct_CAN_Manage_Object`。 - 这个变量用于管理CAN2控制器的相关状态、配置等信息。 - 通过 `{0}` 初始化,将该结构体中的所有成员设置为0(或等价于0的值,如NULL)。 这样做的好处是确保所有成员都有一个确定的初始值,避免使用未初始化的变量,提高系统稳定性。 接下来,我们可以生成相关问题。 </think>### 代码解析:`Struct_CAN_Manage_Object CAN2_Manage_Object = {0};` #### 1. **类型声明分析** - **`Struct_CAN_Manage_Object`** 自定义结构体类型,通常用于管理CAN通信的核心参数: ```c typedef struct { CAN_HandleTypeDef* hcan; // CAN硬件句柄 uint32_t baud_rate; // 波特率 uint8_t mode; // 工作模式(正常/回环) void* rx_buffer; // 接收缓冲区 void* tx_buffer; // 发送缓冲区 } Struct_CAN_Manage_Object; ``` 类似引用[1]中的`drm_plane_state`[^1],封装硬件操作所需的完整上下文。 --- #### 2. **变量定义解析** - **`CAN2_Manage_Object`** **CAN控制器实例**:命名表明针对具体硬件(如STM32的CAN2外设) - **`= {0}`** 零初始化操作: ```c /* 等效于 */ Struct_CAN_Manage_Object CAN2_Manage_Object = { .hcan = NULL, .baud_rate = 0, .mode = 0, .rx_buffer = NULL, .tx_buffer = NULL }; ``` 确保所有成员初始化为零值(类比引用[2]智能指针的确定性初始化[^2]) --- #### 3. **嵌入式开发意义** | 关键点 | 说明 | 必要性 | |----------------|---------------------------------------------------------------------|------------------------| | **硬件抽象** | 封装CAN控制器的硬件相关参数 | 降低驱动与应用的耦合性 | | **状态管理** | 集中存储CAN通信状态(类似引用[4]的控制决策流[^4]) | 实现状态机驱动的通信流程 | | **资源隔离** | 为每个CAN控制器(如CAN1/CAN2)创建独立实例 | 支持多通道并发通信 | | **安全初始化** | 清零操作防止未初始化内存导致的随机故障 | 符合MISRA-C安全规范 | --- #### 4. **典型使用场景** ```c /* 初始化CAN控制器 */ void CAN2_Init() { CAN2_Manage_Object.hcan = &hcan2; // 关联硬件寄存器 CAN2_Manage_Object.baud_rate = 500000; CAN2_Manage_Object.rx_buffer = malloc(RX_BUF_SIZE); HAL_CAN_Start(CAN2_Manage_Object.hcan); // 启动硬件 } /* 中断服务程序 */ void CAN2_RX_IRQHandler() { // 通过CAN2_Manage_Object访问接收缓冲区 process_data(CAN2_Manage_Object.rx_buffer); } ``` --- #### 5. **对比动态内存方案** | 特性 | 静态实例 `CAN2_Manage_Object` | 动态分配(unique_ptr[^3]) | |--------------------|-------------------------------------------|----------------------------------| | **内存来源** | 数据段(编译期分配) | 堆内存(运行时分配) | | **生命周期** | 整个程序运行期 | 随智能指针释放[^3] | | **实时性** | 无分配延迟,适合中断上下文 | 可能因内存分配阻塞实时任务 | | **安全风险** | 无内存泄漏风险 | 需防范智能指针误操作 | > ✅ 在资源受限的嵌入式系统中,静态实例是更可靠的选择 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值