我需要在NSArray中存储对象的弱引用,以防止保留周期。我不确定使用正确的语法。这是正确的方法吗?
Foo* foo1 = [[Foo alloc] init];
Foo* foo2 = [[Foo alloc] init];
__unsafe_unretained Foo* weakFoo1 = foo1;
__unsafe_unretained Foo* weakFoo2 = foo2;
NSArray* someArray = [NSArray arrayWithObjects:weakFoo1, weakFoo2, nil];
请注意,我需要支持iOS 4.x ,因此__unsafe_unretained
代替__weak
。
编辑(2015-02-18):
对于那些想要使用真__weak
指针(而非__unsafe_unretained
)的人,请查看此问题:Collections of zeroing weak references under ARC
答案 0 :(得分:73)
正如Jason所说,你不能让NSArray
存储弱引用。实现Emile建议将对象包装在另一个存储弱引用的对象内的最简单方法如下:
NSValue *value = [NSValue valueWithNonretainedObject:myObj];
[array addObject:value];
另一个选项:category使NSMutableArray
可选择存储弱引用。
请注意,这些是“不安全的未保留”引用,而不是自置零弱引用。如果在释放对象后数组仍然存在,那么你将拥有一堆垃圾指针。
答案 1 :(得分:54)
使用 NSValue 帮助程序或创建集合(数组,集合,字典)对象和disable its Retain/Release callbacks的解决方案都不是关于使用ARC的100%故障安全解决方案。< / p>
正如对这些建议的各种评论所指出的,这样的对象引用不会像真正的弱引用那样工作:
ARC支持的“适当”弱属性有两种行为:
现在,虽然上述解决方案符合行为#1,但它们没有展示#2。
要获得行为#2,您必须声明自己的助手类。它只有一个弱的属性来保持你的参考。然后,将此辅助对象添加到集合中。
哦,还有一件事:iOS6和OSX 10.8据称可以提供更好的解决方案:
[NSHashTable weakObjectsHashTable]
[NSPointerArray weakObjectsPointerArray]
[NSPointerArray pointerArrayWithOptions:]
这些应该为您提供容纳弱引用的容器(但请注意下面的matt的评论)。
答案 2 :(得分:24)
在编写c ++ 20年后,我是Objective-C的新手。
在我看来,Objective-C在松散耦合的消息传递方面非常出色,但对数据管理却很糟糕。
想象一下,我发现xcode 4.3支持objective-c ++非常高兴!
所以现在我将所有.m文件重命名为.mm(编译为objective-c ++)并使用c ++标准容器进行数据管理。
因此“弱指针数组”问题成为__weak对象指针的std :: vector:
#include <vector>
@interface Thing : NSObject
@end
// declare my vector
std::vector<__weak Thing*> myThings;
// store a weak reference in it
Thing* t = [Thing new];
myThings.push_back(t);
// ... some time later ...
for(auto weak : myThings) {
Thing* strong = weak; // safely lock the weak pointer
if (strong) {
// use the locked pointer
}
}
这相当于c ++习语:
std::vector< std::weak_ptr<CppThing> > myCppThings;
std::shared_ptr<CppThing> p = std::make_shared<CppThing>();
myCppThings.push_back(p);
// ... some time later ...
for(auto weak : myCppThings) {
auto strong = weak.lock(); // safety is enforced in c++, you can't dereference a weak_ptr
if (strong) {
// use the locked pointer
}
}
概念证明(根据Tommy对矢量重新分配的担忧):
main.mm:
#include <vector>
#import <Foundation/Foundation.h>
@interface Thing : NSObject
@end
@implementation Thing
@end
extern void foo(Thing*);
int main()
{
// declare my vector
std::vector<__weak Thing*> myThings;
// store a weak reference in it while causing reallocations
Thing* t = [[Thing alloc]init];
for (int i = 0 ; i < 100000 ; ++i) {
myThings.push_back(t);
}
// ... some time later ...
foo(myThings[5000]);
t = nullptr;
foo(myThings[5000]);
}
void foo(Thing*p)
{
NSLog(@"%@", [p className]);
}
示例日志输出:
2016-09-21 18:11:13.150 foo2[42745:5048189] Thing
2016-09-21 18:11:13.152 foo2[42745:5048189] (null)
答案 3 :(得分:13)
如果您不需要特定订单,则可以将NSMapTable
与特殊键/值选项
<强> NSPointerFunctionsWeakMemory 强>
使用适合ARC或GC的弱读写屏障。使用NSPointerFunctionsWeakMemory对象引用将在上次发布时变为NULL。
答案 4 :(得分:10)
我认为最好的解决方案是使用NSHashTable或NSMapTable。键或/和值可能很弱。您可以在此处详细了解:http://nshipster.com/nshashtable-and-nsmaptable/
答案 5 :(得分:4)
最简单的解决方案:
NSMutableArray *array = (__bridge_transfer NSMutableArray *)CFArrayCreateMutable(nil, 0, nil);
NSMutableDictionary *dictionary = (__bridge_transfer NSMutableDictionary *)CFDictionaryCreateMutable(nil, 0, nil, nil);
NSMutableSet *set = (__bridge_transfer NSMutableSet *)CFSetCreateMutable(nil, 0, nil);
注意:这也适用于iOS 4.x.
答案 6 :(得分:4)
要向NSMutableArray添加弱自引用,请创建一个具有弱属性的自定义类,如下所示。
NSMutableArray *array = [NSMutableArray new];
Step 1: create a custom class
@interface DelegateRef : NSObject
@property(nonatomic, weak)id delegateWeakReference;
@end
Step 2: create a method to add self as weak reference to NSMutableArray. But here we add the DelegateRef object
-(void)addWeakRef:(id)ref
{
DelegateRef *delRef = [DelegateRef new];
[delRef setDelegateWeakReference:ref]
[array addObject:delRef];
}
第3步:稍后,如果属性为delegateWeakReference == nil
,则可以从数组中删除对象
该属性将为nil,并且引用将在适当的时间解除分配,与此数组引用无关
答案 7 :(得分:3)
不,那不对。那些实际上并不是弱参考。您现在无法在数组中存储弱引用。您需要有一个可变数组,并在完成它们时删除引用,或者在完成它时删除整个数组,或者滚动您自己的支持它的数据结构。
希望这是他们将在不久的将来解决的问题(NSArray
的弱版本。)
答案 8 :(得分:2)
我刚遇到同样的问题,发现我的ARC之前的解决方案在按照设计转换后工作。
// function allocates mutable set which doesn't retain references.
NSMutableSet* AllocNotRetainedMutableSet() {
CFMutableSetRef setRef = NULL;
CFSetCallBacks notRetainedCallbacks = kCFTypeSetCallBacks;
notRetainedCallbacks.retain = NULL;
notRetainedCallbacks.release = NULL;
setRef = CFSetCreateMutable(kCFAllocatorDefault,
0,
¬RetainedCallbacks);
return (__bridge NSMutableSet *)setRef;
}
// test object for debug deallocation
@interface TestObj : NSObject
@end
@implementation TestObj
- (id)init {
self = [super init];
NSLog(@"%@ constructed", self);
return self;
}
- (void)dealloc {
NSLog(@"%@ deallocated", self);
}
@end
@interface MainViewController () {
NSMutableSet *weakedSet;
NSMutableSet *usualSet;
}
@end
@implementation MainViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
weakedSet = AllocNotRetainedMutableSet();
usualSet = [NSMutableSet new];
}
return self;
}
- (IBAction)addObject:(id)sender {
TestObj *obj = [TestObj new];
[weakedSet addObject:obj]; // store unsafe unretained ref
[usualSet addObject:obj]; // store strong ref
NSLog(@"%@ addet to set", obj);
obj = nil;
if ([usualSet count] == 3) {
[usualSet removeAllObjects]; // deallocate all objects and get old fashioned crash, as it was required.
[weakedSet enumerateObjectsUsingBlock:^(TestObj *invalidObj, BOOL *stop) {
NSLog(@"%@ must crash here", invalidObj);
}];
}
}
@end
输出:
2013-06-30 00:59:10.266 not_retained_collection_test [28997:907] 构建2013-06-30 00:59:10.267 not_retained_collection_test [28997:907] addet to 设定2013-06-30 00:59:10.581 not_retained_collection_test [28997:907] 构建2013-06-30 00:59:10.582 not_retained_collection_test [28997:907] addet to 设定2013-06-30 00:59:10.881 not_retained_collection_test [28997:907] 构建2013-06-30 00:59:10.882 not_retained_collection_test [28997:907] addet to 设定2013-06-30 00:59:10.883 not_retained_collection_test [28997:907] deallocated 2013-06-30 00:59:10.883 not_retained_collection_test [28997:907] deallocated 2013-06-30 00:59:10.884 not_retained_collection_test [28997:907] deallocated 2013-06-30 00:59:10.885 not_retained_collection_test [28997:907] * - [TestObj respondsToSelector:]:发送到解除分配的实例0x1f03c8c0的消息
使用iOS版本4.3,5.1,6.2进行检查。 希望它对某人有用。
答案 9 :(得分:1)
如果您需要归零弱引用,请参阅this answer以获取可用于包装类的代码。
that question的其他答案建议使用基于块的包装器,以及从集合中自动删除归零元素的方法。
答案 10 :(得分:1)
如果您使用了很多这个比例,它会指向您自己的NSMutableArray类(NSMutableArray的子类),它不会增加保留计数。
你应该有这样的东西:
-(void)addObject:(NSObject *)object {
[self.collection addObject:[NSValue valueWithNonretainedObject:object]];
}
-(NSObject*) getObject:(NSUInteger)index {
NSValue *value = [self.collection objectAtIndex:index];
if (value.nonretainedObjectValue != nil) {
return value.nonretainedObjectValue;
}
//it's nice to clean the array if the referenced object was deallocated
[self.collection removeObjectAtIndex:index];
return nil;
}
答案 11 :(得分:-2)
我认为优雅的解决方案是Erik Ralston先生在他的Github存储库中提出的建议
https://gist.github.com/eralston/8010285
这是必不可少的步骤:
为NSArray和NSMutableArray创建一个类别
在实现中创建一个具有弱属性的便捷类。您的类别会将对象分配给此弱属性。
·H
#import <Foundation/Foundation.h>
@interface NSArray(WeakArray)
- (__weak id)weakObjectForIndex:(NSUInteger)index;
-(id<NSFastEnumeration>)weakObjectsEnumerator;
@end
@interface NSMutableArray (FRSWeakArray)
-(void)addWeakObject:(id)object;
-(void)removeWeakObject:(id)object;
-(void)cleanWeakObjects;
@end
的.m
#import "NSArray+WeakArray.h"
@interface WAArrayWeakPointer : NSObject
@property (nonatomic, weak) NSObject *object;
@end
@implementation WAArrayWeakPointer
@end
@implementation NSArray (WeakArray)
-(__weak id)weakObjectForIndex:(NSUInteger)index
{
WAArrayWeakPointer *ptr = [self objectAtIndex:index];
return ptr.object;
}
-(WAArrayWeakPointer *)weakPointerForObject:(id)object
{
for (WAArrayWeakPointer *ptr in self) {
if(ptr) {
if(ptr.object == object) {
return ptr;
}
}
}
return nil;
}
-(id<NSFastEnumeration>)weakObjectsEnumerator
{
NSMutableArray *enumerator = [[NSMutableArray alloc] init];
for (WAArrayWeakPointer *ptr in self) {
if(ptr && ptr.object) {
[enumerator addObject:ptr.object];
}
}
return enumerator;
}
@end
@implementation NSMutableArray (FRSWeakArray)
-(void)addWeakObject:(id)object
{
if(!object)
return;
WAArrayWeakPointer *ptr = [[WAArrayWeakPointer alloc] init];
ptr.object = object;
[self addObject:ptr];
[self cleanWeakObjects];
}
-(void)removeWeakObject:(id)object
{
if(!object)
return;
WAArrayWeakPointer *ptr = [self weakPointerForObject:object];
if(ptr) {
[self removeObject:ptr];
[self cleanWeakObjects];
}
}
-(void)cleanWeakObjects
{
NSMutableArray *toBeRemoved = [[NSMutableArray alloc] init];
for (WAArrayWeakPointer *ptr in self) {
if(ptr && !ptr.object) {
[toBeRemoved addObject:ptr];
}
}
for(WAArrayWeakPointer *ptr in toBeRemoved) {
[self removeObject:ptr];
}
}
@end