弱引用的NSArray(__unsafe_unretained)到ARC下的对象

我需要将弱引用存储到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 ),请检查出这个问题,而不是: ARC

正如Jason所说,你不能让NSArray存储弱引用。 实现Emilebuild议将对象封装在另一个对象中的最简单方法是存储对象的弱引用,如下所示:

 NSValue *value = [NSValue valueWithNonretainedObject:myObj]; [array addObject:value]; 

另一种select:使NSMutableArray可选地存储弱引用的类别 。

请注意,这些是“不安全的未保存的”引用,而不是自我调零的弱引用。 如果在释放对象之后数组仍然在附近,那么将会有一堆垃圾指针。

使用NSValue帮助程序或创build集合(数组,集合,字典)对象并禁用其保留/释放callback的解决scheme都不是使用ARC的100%故障安全解决scheme。

对这些build议的各种评论指出,这样的对象引用不会像真正的弱引用一样工作:

由ARC支持的“适当”弱财产有两种行为:

  1. 对目标对象没有很强的参照。 这意味着如果对象没有强引用指向它,对象将被释放。
  2. 如果ref'd对象被释放,弱引用将变为零。

现在,虽然上述解决scheme将符合行为#1,他们没有展示#2。

要获得行为#2,你必须声明自己的助手类。 它只有一个弱的财产,以供参考。 然后,您将此帮助对象添加到集合。

哦,还有一件事:iOS6和OSX 10.8应该提供了一个更好的解决scheme:

 [NSHashTable weakObjectsHashTable] [NSPointerArray weakObjectsPointerArray] [NSPointerArray pointerArrayWithOptions:] 

这些应该给你的容器,举行弱引用(但请注意下面的马特的评论)。

在C ++写了20年之后,我是新的Objective-C。

在我看来,objective-C在松散耦合的消息传递方面非常出色,但对于数据pipe理来说却很糟糕。

想象一下,我发现xcode 4.3支持objective-c ++是多么的高兴!

所以,现在我将所有的.m文件重命名为.mm(编译为objective-c ++),并使用c ++标准容器进行数据pipe理。

因此,“弱指针数组”问题成为__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 } } 

概念validation(根据汤米对重新分配载体的关注):

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) 

如果你不需要特定的顺序,你可以使用NSMapTable和特殊的键/值选项

NSPointerFunctionsWeakMemory

使用适合于ARC或GC的弱读写障碍。 使用NSPointerFunctionsWeakMemory对象引用将在最后一个版本中变为NULL。

我相信最好的解决scheme是使用NSHashTable或NSMapTable。 关键或/和价值可能很弱。 你可以在这里阅读更多关于它的信息: http : //nshipster.com/nshashtable-and-nsmaptable/

要将弱自引用添加到NSMutableArray,请使用下面给出的弱属性创build自定义类。

 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 ,则可以从数组中删除该对象

该属性将为零,并且引用将在适当的时候被释放,与这个数组引用无关

最简单的解决scheme:

 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。

不,这是不正确的。 那些实际上并不是很弱的参考。 您现在还不能真正将弱引用存储在数组中。 你需要有一个可变数组,当你完成它们的时候删除引用,或者在完成时删除整个数组,或者滚动你自己的支持它的数据结构。

希望这是他们将在不久的将来解决的( NSArray一个弱版本)。

我刚刚面对同样的问题,发现我之前的ARC解决scheme工作后,按照devise的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, &notRetainedCallbacks); 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]要设置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]要设置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 set 2013-06-30 00:59:10.883 not_retained_collection_test [28997:907]解除分配2013-06-30 00:59:10.883 not_retained_collection_test [28997:907]解除分配2013-06-30 00:59 :10.884 not_retained_collection_test [28997:907]解除分配2013-06-30 00:59:10.885 not_retained_collection_test [28997:907] * – [TestObjrespondsToSelector:]:发送到释放实例0x1f03c8c0的消息

检查iOS版本4.3,5.1,6.2。 希望对别人有用。

如果您需要调弱引用,请参阅此答案以了解可用于包装类的代码。

对这个问题的其他答案build议一个基于块的包装,以及从集合中自动删除零元素的方法。

如果你使用了很多这个套件,它会被指示给你自己的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; } 

我认为一个优雅的解决scheme就是Erik Ralston先生在他的Github存储库上提出的build议

https://gist.github.com/eralston/8010285

这是必不可less的步骤:

为NSArray和NSMutableArray创build一个类别

在实施中创build一个弱财产的便利类。 您的类别将分配对象到这个弱财产。

。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