strong、weak等详解

weak表示的是一个弱引用,这个引用不会增加对象的引用计数,并且在所指向的对象被释放之后,weak指针会被设置的为nil。通俗的话来表述是:weak 可以在不增加对象的引用计数的同时,又使得指针的访问是安全的。weak 的常见场景是在 delegate,block,NSTimer 中使用,以避免循环引用所带来的内存泄漏。

iOS5中新的关键字strong, weak, unsafe_unretained. 可以与以前的关键字对应学习strong与retain类似,weak与unsafe_unretained功能差不多(有点区别,等下会介绍,这两个新 关键字与assign类似)。在iOS5中用这些新的关键字,就可以不用手动管理内存了,从java等其它语言转过来的程序员非常受用。

strong关键字与retain关似,用了它,引用计数自动+1,用实例更能说明一切

@property (nonatomic, strong) NSString *string1;   
@property (nonatomic, strong) NSString *string2;
有这样两个属性
@synthesize string1;   
@synthesize string2; 

实现以下代码

self.string1 = @"String 1";   
[self.string2 = self.string1;   
[self.string1 = nil;  
[NSLog(@"String 2 = %@", self.string2); 

结果是:String 2 = String 1

由于string2是strong定义的属性,所以引用计数+1,使得它们所指向的值都是@"String 1", 如果你对retain熟悉的话,这理解并不难。

接着我们来看weak关键字:

如果这样声明两个属性:

@property (nonatomic, strong) NSString *string1;   
@property (nonatomic, weak) NSString *string2; 
并定义
@synthesize string1;   
@synthesize string2; 
实现代码

self.string1 = [[NSString alloc] initWithUTF8String:"string 1"];   
self.string2 = self.string1;   
self.string1 = nil;  
NSLog(@"String 2 = %@", self.string2); 

结果是:String 2 = null

分析一下,由于 self.string1与self.string2指向同一地址,且string2没有retain内存地址,而self.string1=nil释放 了内存,所以string1为nil。声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。在 c/c++开发过程中,为何大牛都说指针的空间释放了后,都要将指针赋为NULL. 在这儿用weak关键字帮我们做了这一步

接着我们来看unsafe_unretained

从名字可以看出,unretained且unsafe,由于是unretained所以与weak有点类似,但是它是unsafe的,什么是unsafe的呢,下面看实例。

如果这样声明两个属性:

并定义

@property (nonatomic, strong) NSString *string1;   
@property (nonatomic, unsafe_unretained) NSString *string2; 
实现代码
self.string1 = [[NSString alloc] initWithUTF8String:"string 1"];   
self.string2 = self.string1;   
self.string1 = nil;  
NSLog(@"String 2 = %@", self.string2); 
根本不会有输出,你的程序会crash掉。 原因是什么,其实 就是野指针造成的,所以野指针是可怕的。为何会造成野指针呢?同于用unsafe_unretained声明的指针,由于 self.string1=nil已将内存释放掉了,但是string2并不知道已被释放了,所以是野指针。然后访问野指针的内存就造成crash.  所以尽量少用unsafe_unretained关键字。

什么是空指针和野指针

1.空指针

1> 没有存储任何内存地址的指针就称为空指针(NULL指针)

2> 空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0。

Student *s1 = NULL;
Student *s2 = nil;

2.野指针

"野指针"不是NULL指针,是指向"垃圾"内存(不可用内存)的指针。野指针是非常危险的。

例子:

自定义Student类,在main函数中添加下列代码

Student *stu = [[Student alloc] init];

[stu setAge:10];

[stu release];

[stu setAge:10];
运行程 序,你会发现最后行报错了,是个野指针错误!

报错原因

1> 执行完第1行代码后,内存中有个指针变量stu,指向了Student对象

Student *stu = [[Student alloc] init];

假设Student对象的地址为0xff43,指针变量stu的地址为0xee45,stu中存储的是Student对象的地址0xff43。即指针变量stu指向了这个Student对象。

2> 接下来是第2行代码

[stu setAge:10];

这行代码的意思是:给stu所指向的Student对象发送一条setAge:消息,即调用这个Student对象的setAge:方法。目前来说,这个Student对象仍存在于内存中,所以这句代码没有任何问题。

 

3> 接下来是第3行代码

[stu release];
这行代码的意思是:给stu指向的Student对象发送一条release消息。在这里,Student对象接收到release消息后,会马上被销毁,所占用的内存会被回收。

Student对象被销毁了,地址为0xff43的内存就变成了"垃圾内存",然而,指针变量stu仍然指向这一块内存,这时候,stu就称为了野指针

 

4> 最后执行了行代码

[stu setAge:10];
这句代码的意思仍然是:  给stu所指向的Student对象发送一条setAge:消息 但是在执行完第5行代码后,Student对象已经被销毁了,它所占用的内存已经是垃圾内存,如果你还去访问这一块内存,那就会报野指针错误。这块内存已经不可用了,也不属于你了,你还去访问它,肯定是不合法的。所以,这行代码报错了!

如果改动一下代码,就不会报错

Student *stu = [[Student alloc] init];

[stu setAge:10];

[stu release];

stu = nil;

[stu setAge:10];

第4行 stu变成了空指针,stu就不再指向任何内存了

因为stu是个空指针,没有指向任何对象,因此第9行的setAge:消息是发不出去的,不会造成任何影响。当然,肯定也不会报错。


总结

1> 利用野指针发消息是很危险的,会报错。也就是说,如果一个对象已经被回收了,就不要再去操作它,不要再尝试给它发消息。

2> 利用空指针发消息是没有任何问题的,也就是说下面的代码是没有错误的:

参考:1. 点击打开链接

     2.点击打开链接

<think> 我们讨论的是C/C++中的`__weak`关键字(或通过`__attribute__((weak))`定义的弱符号)的作用。弱符号允许在链接时,如果存在同名强符号,则弱符号被忽略;如果不存在,则使用弱符号。这在库函数中常用,以便用户能够覆盖这些函数。 为了设计实验证明`weak`关键字的作用,我们可以设计两个源文件:一个定义弱符号函数,另一个定义强符号的同名函数(或不定义)。然后观察在链接时,程序使用的是哪一个函数。 ### 实验设计 **实验1:验证弱符号可以被强符号覆盖** 1. 创建两个源文件: - `weak_symbol.c`:定义一个弱符号函数。 - `main.c`:定义一个强符号的同名函数,并调用该函数。 **weak_symbol.c:** ```c #include <stdio.h> // 使用弱属性定义函数 __attribute__((weak)) void my_function() { printf("This is the weak function.\n"); } ``` **main.c:** ```c #include <stdio.h> // 定义强符号函数(覆盖弱符号) void my_function() { printf("This is the strong function.\n"); } int main() { my_function(); return 0; } ``` 编译并运行: ```bash gcc weak_symbol.c main.c -o test ./test ``` 预期输出:`This is the strong function.`,因为强符号覆盖了弱符号。 **实验2:验证在没有强符号时使用弱符号** 1. 修改`main.c`,移除强符号函数,改为声明并调用。 **main.c:** ```c #include <stdio.h> // 声明函数(但不定义强符号) void my_function(); int main() { // 如果没有强符号,则使用弱符号 my_function(); return 0; } ``` 编译并运行: ```bash gcc weak_symbol.c main.c -o test ./test ``` 预期输出:`This is the weak function.`,因为此时没有强符号,所以使用了弱符号。 **实验3:验证多个弱符号时链接器行为** 如果存在多个弱符号,链接器会选择其中一个(通常是最先链接的或者任意一个,具体取决于链接器),但通常我们不依赖这种行为。不过,为了完整展示,可以设计三个文件: - `weak1.c`: 定义一个弱符号函数 - `weak2.c`: 定义另一个弱符号的同名函数 - `main.c`: 调用该函数 **weak1.c:** ```c #include <stdio.h> __attribute__((weak)) void my_function() { printf("Weak function 1.\n"); } ``` **weak2.c:** ```c #include <stdio.h> __attribute__((weak)) void my_function() { printf("Weak function 2.\n"); } ``` **main.c:** ```c void my_function(); int main() { my_function(); return 0; } ``` 编译并运行(注意链接顺序): ```bash gcc weak1.c weak2.c main.c -o test ./test ``` 预期输出可能是`Weak function 1.`或`Weak function 2.`,这取决于链接器的顺序(通常先链接的会被使用)。但需要注意的是,多个弱符号存在时,链接器不会报错,而是任意选择一个。 ### 实验结论 通过上述实验,我们可以验证弱符号的作用: 1. 当存在强符号时,强符号覆盖弱符号。 2. 当不存在强符号时,使用弱符号。 3. 多个弱符号存在时,链接器选择其中一个(不报错),但实际应用中应避免多个弱符号。 ### 引用说明 在实验中,我们使用了`__attribute__((weak))`来模拟`__weak`关键字,因为在某些编译环境中(如实验中的Keil C99模式)可能不支持`__weak`关键字,而是使用类似的宏定义[^1]。同时,弱引用的处理机制(如实验3中所述)也类似于对象销毁时对弱引用的置空处理[^3]。 ### 相关问题 1. 弱符号和强符号在链接时的优先级规则是什么? 2. 除了函数,变量是否可以声明为弱符号? 3. 在大型项目中,弱符号通常用于哪些场景? </think> ### 设计实验验证`weak`关键字的作用 #### 实验目标 验证`__weak`(或`__attribute__((weak))`)关键字在以下场景中的作用: 1. 允许弱符号被强符号覆盖 2. 避免重复定义冲突 3. 实现函数/变量的默认实现 #### 实验设计 ```c // weak_demo.c #include <stdio.h> // 弱符号声明(默认实现) __attribute__((weak)) void custom_print() { printf("Default weak implementation\n"); } // 弱符号变量 __attribute__((weak)) int config_value = 10; int main() { printf("调用函数: "); custom_print(); // 实际调用取决于是否有强符号覆盖 printf("配置值: %d\n", config_value); return 0; } ``` ```c // strong_override.c #include <stdio.h> // 强符号覆盖弱函数 void custom_print() { printf("Strong override implementation\n"); } // 强符号覆盖弱变量 int config_value = 999; ``` #### 实验步骤 1. **单独编译弱符号版本** ```bash gcc weak_demo.c -o weak_default ./weak_default ``` **预期输出**: ``` 调用函数: Default weak implementation 配置值: 10 ``` *证明弱符号提供默认实现* 2. **编译覆盖版本** ```bash gcc weak_demo.c strong_override.c -o strong_override ./strong_override ``` **预期输出**: ``` 调用函数: Strong override implementation 配置值: 999 ``` *证明强符号覆盖弱符号* 3. **缺失弱符号测试** 删除`weak_demo.c`中的`custom_print()`定义后编译: ```bash gcc weak_demo.c -o missing_weak ``` **预期结果**:链接错误 `undefined reference to 'custom_print'` *证明弱符号避免了未定义错误* 4. **多重弱符号测试** 创建`another_weak.c`声明同名弱函数: ```c __attribute__((weak)) void custom_print() { printf("SECOND weak implementation\n"); } ``` 编译: ```bash gcc weak_demo.c another_weak.c -o multi_weak ``` **预期结果**:编译成功,输出取决于链接顺序 *证明允许多个弱符号共存* #### 实验原理 1. **符号覆盖机制** 链接器优先选择强符号(strong symbol),弱符号(weak symbol)仅当无强符号时使用[^1]。 2. **内存管理关联** 弱引用在对象销毁时可自动置空,避免悬垂指针(实验3通过移除弱符号模拟)[^3]。 3. **编译器实现** 通过`__attribute__((weak))`属性实现符号表特殊标记,验证时可用: ```bash nm ./weak_default | grep ' custom_print' ``` 输出中的 `W` 表示弱符号(如:`W custom_print`) #### 实验结论 该实验可验证: 1. 弱符号提供可覆盖的默认实现 ✅ 2. 强符号优先于弱符号 ✅ 3. 弱符号避免多重定义冲突 ✅ 4. 弱引用机制与内存安全相关 ✅ --- ### 相关问题 1. 弱符号在动态链接库中有何特殊行为? 2. 如何用汇编代码验证弱符号的底层实现? 3. 弱符号机制如何解决嵌入式系统中的外设驱动兼容性问题? 4. 弱引用在ARC内存管理中的作用是什么? [^1]: 编译器通过`__attribute__((weak))`实现弱符号特性 [^3]: 弱引用在对象销毁时自动置空,保证内存安全
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值