弱引用的使用

弱引用的使用

在一些情况下,可能需要使用某个实例,但是又不想对其进行强引用而导致其不能被释放。

通常情况下,我们可以直接使用 NSPointerArrayNSHashTableNSMapTable 类,创建一个持有弱引用实例的集合,那么当添加到集合中的实例被释放时,相应的其也会被移出该集合。

另外,在将基本类型添加到集合中时,我们需要借助 NSValue 类对其进行包装,而该类中的 NSValueExtensionMethods 分类还提供了对实例对象和指针的封装方法。

@interface NSValue (NSValueExtensionMethods)

+ (NSValue *)valueWithNonretainedObject:(nullable id)anObject;
@property (nullable, readonly) id nonretainedObjectValue;

+ (NSValue *)valueWithPointer:(nullable const void *)pointer;
@property (nullable, readonly) void *pointerValue;

@end

测试代码:

@interface Info : NSObject
@end

@implementation Info
@end

@interface ViewController ()

@property (nonatomic, strong) NSMapTable *dic;
@property (nonatomic, strong) NSPointerArray *array;

@property (nonatomic, strong) Info *info;

@property (nonatomic, weak) NSString *str;
@property (nonatomic, weak) NSMutableString *str1;
@end

ViewController 中声明用来持有实例的两个集合,一个 Info 类实例,以及两个字符串。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.array = [NSPointerArray weakObjectsPointerArray];
    self.dic = [NSMapTable strongToWeakObjectsMapTable];
    
    Info *info = [Info new];
    
    NSString *str = @"this is a test ";
    NSMutableString *str1 = [NSMutableString stringWithString:str];
    
    self.str1 = str1;
    self.info = info;
    self.str = str;

    NSValue *value = [NSValue valueWithNonretainedObject:str];
    NSValue *value1 = [NSValue valueWithNonretainedObject:str1];
    NSValue *value2 = [NSValue valueWithPointer:(void *)self.info];
    
    [self.dic setObject:value forKey:@"key"];
    [self.dic setObject:value1 forKey:@"key1"];
    [self.dic setObject:value2 forKey:@"key2"];
    [self.dic setObject:str forKey:@"key3"];
    [self.dic setObject:str1 forKey:@"key4"];
    [self.dic setObject:self.info forKey:@"key5"];
    [self.dic setObject:self.str forKey:@"key6"];
    [self.dic setObject:self.str1 forKey:@"key7"];
    
    [self.array addPointer:(void *)value];
    [self.array addPointer:(void *)value1];
    [self.array addPointer:(void *)value2];
    [self.array addPointer:(void *)str];
    [self.array addPointer:(void *)str1];
    [self.array addPointer:(void *)self.info];
    [self.array addPointer:(void *)self.str];
    [self.array addPointer:(void *)self.str1];
    
    NSLog(@"%@",self.dic);
    
    NSLog(@"%@",self.array.allObjects);
    
    NSLog(@"str : %p , &str : %p , str1 : %p , &str1 : %p , self.str : %p , self.str1 : %p",str,&str,str1,&str1,self.str,self.str1);
}

输出结果如下:

NSMapTable {
[3] key1 -> <10cefd00 00600000>
[4] key3 -> this is a test 
[5] key -> <8880d10b 01000000>
[6] key4 -> this is a test 
[7] key7 -> this is a test 
[8] key5 -> <Info: 0x600000392f80>
[9] key6 -> this is a test 
[12] key2 -> <802f3900 00600000>
}

(
    "<8880d10b 01000000>",
    "<10cefd00 00600000>",
    "<802f3900 00600000>",
    "this is a test ",
    "this is a test ",
    "<Info: 0x600000392f80>",
    "this is a test ",
    "this is a test "
)

str : 0x10bd18088 , &str : 0x7ffee3ee7a20 , str1 : 0x600000fdce10 , &str1 : 0x7ffee3ee7a18 , self.str : 0x10bd18088 , self.str1 : 0x600000fdce10

之后,再执行下面的方法:

- (void)btnClick {
    self.info = nil;
    NSLog(@"%@",self.dic);
    NSLog(@"%@",self.array.allObjects);   
}

输出结果如下:

NSMapTable {
[4] key3 -> this is a test 
[9] key6 -> this is a test 
}
(
    "this is a test ",
    "this is a test "
)

如果将 self.info = nil 注释,那么输出结果如下:

NSMapTable {
[4] key3 -> this is a test 
[8] key5 -> <Info: 0x6000006210e0>
[9] key6 -> this is a test 
}
(
    "this is a test ",
    "<Info: 0x6000006210e0>",
    "this is a test "
)

通过上面三个输出结果的比较可知,当实例销毁时,集合中的实例会被移除。而需要注意的是,字符串比较特殊,对于 NSString 而言,其实际的值是保存在内存的数据段区域的,而不像 NSMutableString 保存在堆中。从下面的输出地址也可推断出各个变量大致存储在栈中还是堆中:

str : 0x10bd18088 , &str : 0x7ffee3ee7a20 ,

str1 : 0x600000fdce10 , &str1 : 0x7ffee3ee7a18 ,

self.str : 0x10bd18088 , self.str1 : 0x600000fdce10

所以,这三个输出中都包含了 strself.str 的值。

还有一个问题值得注意,当使用 NSValue 封装指针时,如果将封装后的 NSValue 对象放在普通的集合中,那么如果指针所指向的实例对象销毁了,NSValue 的实例并不会被移出集合,并且访问 nonretainedObjectValuepointerValue 属性得到的也并不是 nil

现在,添加一个集合属性,并将 info 属性封装后放入该集合。

@property (nonatomic, strong) NSMutableDictionary *dic1;

- (void)viewDidLoad {
	[super viewDidLoad];
	self.dic1 = [NSMutableDictionary dictionary];
	[self.dic1 setObject:value2 forKey:@"key2"];
}
- (void)btnClick {
    
    NSLog(@"%@",self.dic);
    
    NSLog(@"%@",self.array.allObjects);

    NSValue *value = [self.dic1 objectForKey:@"key2"];
    
    if (value.nonretainedObjectValue){
        NSLog(@"%@",value.nonretainedObjectValue);
    }
}

得到输出结果如下:

NSMapTable {
[4] key3 -> this is a test 
[8] key5 -> <Info: 0x6000038eae80>
[9] key6 -> this is a test 
[12] key2 -> <80ae8e03 00600000>
}
(
    "<80ae8e03 00600000>",
    "this is a test ",
    "<Info: 0x6000038eae80>",
    "this is a test "
)
<Info: 0x6000038eae80>

此时,临时变量 value2 并没有被释放,而其封装的 Info 实例对象也是可以访问的。

@property (nonatomic, weak) Info *info;

但是如果将属性 info 的修饰字段 strong 修改为 weak 如上,那么会发现程序输出下面的结果后,便报错了。

NSMapTable {
[4] key3 -> this is a test 
[9] key6 -> this is a test 
[12] key2 -> <a0c7ae00 00600000>
}
(
    "<a0c7ae00 00600000>",
    "this is a test ",
    "this is a test "
)

对比上面的结果可知,info 实例对象已经被释放了,而此时的 value2 所封装的地址 <a0c7ae00 00600000> 却并未置为空,再去访问value.nonretainedObjectValue 具体封装的实例对象,便造成了指针越界。

所以除非确定封装的对象在访问时不会被销毁,否则不要使用普通的集合来保存封装的指针。

这里的地址 <a0c7ae00 00600000> 是按字节从低到高打印的,实际地址为 0x00 00 60 00 00 ae c7 a0

当然,除此之外,还可以利用 block 的特性来实现弱引用的使用。将实例作为一个参数来创建一个 block 对象,然后持有该对象,需要获取实例时,便执行持有的 block 对象,返回封装的实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java集合框架中提供了一种弱引用的集合类:WeakHashMap。WeakHashMap是基于哈希表实现的,它使用弱引用来持有键。当某个键不再被其他对象引用时,该键会被垃圾回收器回收,并从WeakHashMap中自动移除。 使用弱引用的集合可以在某些情况下很有用,例如在缓存系统中,当缓存对象不再被其他对象引用时,可以通过使用WeakHashMap来自动清理缓存。 下面是使用WeakHashMap的示例代码: ```java import java.util.Map; import java.util.WeakHashMap; public class WeakHashMapExample { public static void main(String[] args) { Map<Key, Value> weakMap = new WeakHashMap<>(); // 创建弱引用对象作为键 Key key1 = new Key(1); Key key2 = new Key(2); // 创建值对象 Value value1 = new Value("Value 1"); Value value2 = new Value("Value 2"); // 将键值对放入WeakHashMap中 weakMap.put(key1, value1); weakMap.put(key2, value2); // 输出WeakHashMap中的键值对数量 System.out.println("Size before GC: " + weakMap.size()); // 设置键对象为null,不再有其他引用指向它们 key1 = null; key2 = null; // 手动执行垃圾回收 System.gc(); // 输出WeakHashMap中的键值对数量 System.out.println("Size after GC: " + weakMap.size()); } static class Key { private int id; public Key(int id) { this.id = id; } } static class Value { private String data; public Value(String data) { this.data = data; } } } ``` 输出结果为: ``` Size before GC: 2 Size after GC: 0 ``` 可以看到,在手动执行垃圾回收后,WeakHashMap中的键值对被自动移除了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值