OC中block为什么用copy修饰

在 Objective-C 中,block 是一种匿名函数,可以捕获上下文中的变量。为了确保 block 在内存管理中的正确性,通常使用 copy 修饰符。以下是详细原因和解释:


1. block 的内存管理

  • block 的类型
    • NSGlobalBlock:存储在全局区,不捕获任何外部变量。
    • NSStackBlock:存储在栈区,捕获外部变量。
    • NSMallocBlock:存储在堆区,由栈区 block 复制而来。
  • 默认行为
    • 当 block 捕获外部变量时,默认是 NSStackBlock,存储在栈区。
    • 栈区的 block 在作用域结束后会被释放,如果此时尝试访问该 block,会导致崩溃。

2. 为什么用 copy 修饰

  • 将 block 从栈复制到堆
    • 使用 copy 修饰符可以将 NSStackBlock 复制为 NSMallocBlock,存储在堆区。
    • 堆区的 block 生命周期由引用计数管理,可以安全地在作用域外使用。
  • 避免野指针
    • 如果不使用 copy,栈区的 block 在作用域结束后会被释放,后续访问会导致野指针问题。
  • ARC 下的行为
    • 在 ARC(自动引用计数)环境下,copy 是默认行为,即使不显式使用 copy,编译器也会自动将 block 复制到堆区。
    • 但在 MRC(手动引用计数)环境下,必须显式使用 copy

3. 代码示例

  • MRC 环境下
    typedef void (^MyBlock)(void);
    
    @interface MyClass : NSObject
    @property (nonatomic, copy) MyBlock block;
    @end
    
    @implementation MyClass
    - (void)setupBlock {
        int value = 10;
        self.block = [^{
            NSLog(@"Value: %d", value);
        } copy]; // 必须使用 copy
    }
    @end
    
  • ARC 环境下
    @interface MyClass : NSObject
    @property (nonatomic, copy) MyBlock block;
    @end
    
    @implementation MyClass
    - (void)setupBlock {
        int value = 10;
        self.block = ^{
            NSLog(@"Value: %d", value);
        }; // ARC 下会自动 copy
    }
    @end
    

4. 注意事项

  • 循环引用
    • 使用 copy 修饰 block 时,如果 block 捕获了 self,可能导致循环引用。
    • 解决方法:使用 __weak 弱引用打破循环。
      __weak typeof(self) weakSelf = self;
      self.block = ^{
          __strong typeof(weakSelf) strongSelf = weakSelf;
          [strongSelf doSomething];
      };
      
  • 性能影响
    • copy 操作会涉及内存分配和复制,可能对性能有轻微影响。
    • 但对于大多数场景,这种影响可以忽略。

5. 总结

  • 在 Objective-C 中,使用 copy 修饰 block 是为了将 block 从栈区复制到堆区,确保其生命周期和内存安全。
  • 在 MRC 环境下必须显式使用 copy,而在 ARC 环境下编译器会自动处理。
  • 使用 copy 时需注意循环引用问题,可以通过弱引用解决。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容