关于block访问外部变量的一些小注意点

Block机制详解
本文深入探讨了Block机制的基础概念及其在Objective-C中的应用。Block能够携带并访问外部变量的状态数据,并在多线程环境中作为回调函数使用。文章还详细介绍了如何在Block内部正确地访问和修改外部变量。

       Block类型是一个C级别的语法和运行机制。它与标准的C函数类似,不同之处在于,它除了有可执行代码以外,它还包含了与堆、栈内存绑定的变量。因此,Block对象包含着一组状态数据,这些数据在程序执行时用于对行为产生影响。 

  你可以用Block来写一些可以传到API中的函数语句,可选择性地存储,并可以使用多线程。作为一个回调,Block特别的有用,因为block既包含了回调期间的代码,又包含了执行期间需要的数据。 

  由于Objective-C 和 C++ 都是衍生自C,block被设计为可同时兼容这三种语言。

       今天主要来说说block访问外部变量的一些东西!

1)在block内部可以访问block外部的变量

在block内部可以使用外部变量


但是要注意,这是一个新的内存空间变量 



block内部也可以定义和block外部的同名的变量(局部变量),此时局部变量会暂时屏蔽外部变量的作用域


2)在block内部不可以修改block "外部的变量"

  非局部变量会以const变量被拷贝并存储到block中,也就是说在block中是只读的。如果尝试在block内部给m变量赋值,会抛出编译器错误。




  冻结中的非局部变量是一个常量值,这也是一种默认的安全行为——因为这可以防止在block中的代码对非局部变量做了意外的修改。



给局部变量加上__block关键字,则这个局部变量可以在block内部进行修改。 

注意:

1、静态变量和全局变量在加和不加__block都会直接引用变量地址。也就意味着可以修改变量的值。在没有加__block关键字的情况下。

2、常量变量(NSString *a=@"hello"; a为变量,@“hello”为常量。)
  不加__block类型,block会直接取常量值(浅拷贝)。
  加__block类型,block会去引用变量的地址。(如:a变量,a = @"abc".可以任意修改a 指向的内容。)

如果不加__block 直接在block内部修改变量 ,会编译报错。block内部改变量是只读的。


<think> 我们正在讨论外部变量的使用。根据提供的引用,外部变量是在一个源文件中定义,而在另一个源文件中需要使用的变量。在C语言中,使用`extern`关键字来声明外部变量。同时,在Shell脚本中使用awk时,也有引用外部变量的方法。此外,在iOSBlock中,外部变量的捕获也有特定的规则。 我将从以下几个方面详细解释外部变量的使用方法: 1. C语言中外部变量的定义和声明 2. awk中引用外部变量的方法 3. iOS Block中对外部变量的捕获 ### 1. C语言中外部变量的定义和声明 在C语言中,外部变量(全局变量)可以在多个源文件之间共享。使用步骤如下: - **定义**:在某个源文件(如`file1.c`)中定义全局变量,例如: ```c // file1.c int globalVar = 10; // 定义并初始化 ``` - **声明**:在其他需要使用该变量的源文件(如`file2.c`)中,使用`extern`关键字声明该变量(不分配内存,只是告诉编译器该变量在其他地方定义): ```c // file2.c extern int globalVar; // 声明外部变量 void func() { printf("%d\n", globalVar); // 使用外部变量 } ``` 注意事项: - 外部变量的定义只能出现在一个源文件中(避免重复定义链接错误)。 - 声明可以出现在多个文件中(使用`extern`)。 - 外部变量的生存期是整个程序运行期间(程序开始分配内存,结束释放)[^1][^3]。 ### 2. awk中引用外部变量的方法 在Shell脚本中,awk命令常常需要引用外部定义的变量。以下是四种常用方法(引用[4]): - **方法1:双引号内使用转义双引号**(适用于变量替换在awk命令内部): ```bash var="bash" echo "unix script" | awk "{gsub(/unix/,\"$var\"); print}" ``` 输出:`bash script` - **方法2:单引号内用双引号包裹变量**(变量在函数外): ```bash var="bash" echo "unix script" | awk 'gsub(/unix/,"'"$var"'")' ``` - **方法3:单引号内三个单引号**(变量在函数内): ```bash i=10 df -h | awk '{if(int($5)>'''$i'''){print $6":"$5}}' ``` - **方法4:使用`-v`选项**(推荐,清晰且安全): ```bash var="bash" echo "unix script" | awk -v var="$var" '{gsub(/unix/,var); print}' ``` 这种方法通过`-v`选项将Shell变量传递给awk的内部变量,避免了复杂的引号嵌套,且可读性更强[^4]。 ### 3. iOS Block中对外部变量的捕获 在Objective-C中,Block可以捕获外部变量,但行为取决于变量类型: - **基本类型局部变量**:默认捕获值(值捕获),且在Block内部不能修改。例如: ```objectivec int age = 10; void (^myBlock)(void) = ^{ NSLog(@"age = %d", age); // 输出10 }; age = 18; myBlock(); // 输出10,捕获的是执行Block定义时的值 ``` - **使用`__block`修饰的变量**:捕获变量引用(引用捕获),可以在Block内部修改。例如: ```objectivec __block int age = 10; void (^myBlock)(void) = ^{ age = 20; // 允许修改 NSLog(@"age = %d", age); }; age = 18; myBlock(); // 输出20,且外部age也变为20 ``` 注意事项: - 全局变量或静态变量Block中可以直接修改(无需`__block`修饰)。 - 使用`__block`修饰的变量会被特殊处理,存储到堆上以实现跨Block访问[^5]。 ### 总结 外部变量的使用在不同场景下有不同方法: - C语言:通过`extern`声明跨文件共享的全局变量。 - awk脚本:通过引号嵌套或`-v`选项传递Shell变量。 - iOS Block:通过值捕获或`__block`修饰实现引用捕获。 在实际编程中,需注意作用域、生命周期和访问权限,避免错误。 §§相关问题§§ 1. C语言中外部变量和静态全局变量有何区别? 2. 如何在Python中实现类似C语言的外部变量共享? 3. awk中使用`-v`选项传递变量有哪些局限性? 4. Swift闭包中捕获外部变量的规则与Objective-C Block有何异同? </think> ### 外部变量的使用方法详解 根据引用内容,外部变量指在**当前作用域外定义**的变量,通过特定语法实现跨文件/跨作用域访问。下面按编程场景分类说明: --- #### 一、C语言中的外部变量(基于引用[1][3]) **1. 定义与声明** - **定义**:在某个源文件(如`file1.c`)中分配存储空间 ```c // file1.c int globalVar = 10; // 实际定义(分配内存) ``` - **声明**:在其他文件(如`file2.c`)中通过`extern`引用 ```c // file2.c extern int globalVar; // 声明(不分配内存) void func() { printf("%d\n", globalVar); // 输出10 } ``` **2. 关键规则** - 定义只能出现一次(避免重复定义错误) - 声明可多次出现(需用`extern`) - 生命周期:整个程序运行期(从启动到结束) - **错误示例**(重复定义): ```c // file1.c int var; // file2.c int var; // 链接错误:multiple definition ``` --- #### 二、awk脚本引用外部变量(基于引用[2][4]) **1. 四种方法对比** | 方法 | 语法示例 | 适用场景 | |-----------|---------------------------------------------|-----------------| | 转义双引号 | `awk "{print \"$var\"}"` | 简单变量替换 | | 单引号嵌套 | `awk 'gsub(/old/, "'"$var"'")'` | 正则替换操作 | | 三重单引号 | `awk '{if($1 > '''$var''') print}'` | 条件判断内部 | | `-v`参数 | `awk -v awk_var="$shell_var" '{print awk_var}'` | **推荐方法**(安全清晰) | **2. 最佳实践** 优先使用`-v`选项(避免引号嵌套混乱): ```bash # 示例:计算列总和(引用外部变量$i) awk -v col_index="$i" -F'\t' '{sum += $col_index} END{print sum}' table.txt ``` --- #### 三、iOS Block捕获外部变量(基于引用[5]) **1. 默认行为(值捕获)** ```objectivec int age = 10; void (^myBlock)(void) = ^{ NSLog(@"%d", age); // 输出10(捕获定义时的值) }; age = 20; myBlock(); // 仍输出10 ``` **2. 使用`__block`(引用捕获)** ```objectivec __block int age = 10; // 添加修饰符 void (^myBlock)(void) = ^{ age = 30; // 允许修改 NSLog(@"%d", age); }; age = 20; myBlock(); // 输出30(外部age同步变为30) ``` **3. 核心区别** | 类型 | 修饰符 | Block内是否可修改 | 内存管理 | |------------|----------|------------------|------------| | 局部变量 | 无 | ❌(编译错误) | 值拷贝 | | 外部变量 | `__block`| ✅ | 引用传递 | --- ### 关键总结 1. **C语言跨文件访问** - 定义:`int var;` - 声明:`extern int var;` 2. **Shell/awk脚本** - 首选 `awk -v var="$value"` 3. **iOS Block** - 需修改变量时用 `__block` 修饰 4. **通用原则** - 避免重复定义 - 注意作用域边界 - 明确生命周期(如Block可能引发循环引用)[^1][^3][^4][^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值