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

发布于 2025-01-06 08:29:43 字数 624 浏览 3 评论 0原文

我需要在 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下清零弱引用集合

I need to store weak references to objects in an NSArray, in order to prevent retain cycles. I'm not sure of the proper syntax to use. Is this the correct way?

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];

Note that I need to support iOS 4.x, thus the __unsafe_unretained instead of __weak.


EDIT (2015-02-18):

For those wanting to use true __weak pointers (not __unsafe_unretained), please check out this question instead: Collections of zeroing weak references under ARC

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

一袭白衣梦中忆 2025-01-13 08:29:43

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

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

另一个选项:类别 使 NSMutableArray 可以选择性地存储弱引用。

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

As Jason said, you can't make NSArray store weak references. The easiest way to implement Emile's suggestion of wrapping an object inside another object that stores a weak reference to it is the following:

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

Another option: a category that makes NSMutableArray optionally store weak references.

Note that these are "unsafe unretained" references, not self-zeroing weak references. If the array is still around after the objects are deallocated, you'll have a bunch of junk pointers.

哀由 2025-01-13 08:29:43

使用 NSValue 帮助器或创建集合(数组、集合、字典)对象并禁用其的解决方案对于使用 ARC,保留/释放回调都不是 100% 的故障安全解决方案。

正如对这些建议的各种评论指出的那样,此类对象引用不会像真正的弱引用那样工作:

ARC 支持的“正确”弱属性有两种行为:

  1. 不保留对目标对象的强引用。这意味着如果该对象没有指向它的强引用,则该对象将被释放。
  2. 如果 ref'd 对象被释放,弱引用将变为 nil。

现在,虽然上述解决方案符合行为#1,但它们不表现出行为#2。

为了也获得行为#2,您必须声明您自己的辅助类。它只有一个弱属性来保存您的参考。然后将此辅助对象添加到集合中。

哦,还有一件事:iOS6 和 OSX 10.8 据说提供了更好的解决方案:

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

这些应该为您提供保存弱引用的容器(但请注意下面 Matt 的评论)。

示例(更新于 2022 年 2 月 2 日)

#import <Foundation/Foundation.h>

static BOOL didDealloc = NO;

@interface TestClass : NSObject
@end

@implementation TestClass
-(void)dealloc {
    didDealloc = YES;
}
@end

int main(int argc, const char * argv[]) {
    NSPointerArray *pa = [NSPointerArray weakObjectsPointerArray];
    @autoreleasepool {
        TestClass *obj = TestClass.new;
        [pa addPointer:(__bridge void * _Nullable)(obj)]; // stores obj as a weak ref
        assert([pa pointerAtIndex:0] != nil);
        assert(!didDealloc);
    } // at this point the TestClass obj will be deallocated
    assert(didDealloc);
    assert([pa pointerAtIndex:0] == nil); // verify that the weak ref is null now
    return 0;
}

如果您运行此示例,您会发现将 TestClass 对象添加到指针数组 pa 中,然后再次释放该对象后,指针(内部是一个弱对象引用)现在根据需要设置为 null。

但是,请注意,最后调用 [pa Compact] 不会像我预期的那样删除 nil 指针。

The solutions to use a NSValue helper or to create a collection (array, set, dict) object and disable its Retain/Release callbacks are both not 100% failsafe solutions with regard to using ARC.

As various comments to these suggestions point out, such object references will not work like true weak refs:

A "proper" weak property, as supported by ARC, has two behaviors:

  1. Doesn't hold a strong ref to the target object. That means that if the object has no strong references pointing to it, the object will be deallocated.
  2. If the ref'd object is deallocated, the weak reference will become nil.

Now, while the above solutions will comply with behavior #1, they do not exhibit #2.

To get behavior #2 as well, you have to declare your own helper class. It has just one weak property for holding your reference. You then add this helper object to the collection.

Oh, and one more thing: iOS6 and OSX 10.8 supposedly offer a better solution:

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

These should give you containers that hold weak references (but note matt's comments below).

An example (updated 2 Feb 2022)

#import <Foundation/Foundation.h>

static BOOL didDealloc = NO;

@interface TestClass : NSObject
@end

@implementation TestClass
-(void)dealloc {
    didDealloc = YES;
}
@end

int main(int argc, const char * argv[]) {
    NSPointerArray *pa = [NSPointerArray weakObjectsPointerArray];
    @autoreleasepool {
        TestClass *obj = TestClass.new;
        [pa addPointer:(__bridge void * _Nullable)(obj)]; // stores obj as a weak ref
        assert([pa pointerAtIndex:0] != nil);
        assert(!didDealloc);
    } // at this point the TestClass obj will be deallocated
    assert(didDealloc);
    assert([pa pointerAtIndex:0] == nil); // verify that the weak ref is null now
    return 0;
}

If you run this you'll find that after adding the TestClass object to the pointer array pa, then releasing that object again, the pointer (which is internally a weak object ref) is now set to null as desired.

However, note that calling [pa compact] at the end will not remove the nil pointer as I'd have expected.

酒几许 2025-01-13 08:29:43

在编写 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)

I am new to objective-C, after 20 years of writing c++.

In my view, objective-C is excellent at loosely-coupled messaging, but horrible for data management.

Imagine how happy I was to discover that xcode 4.3 supports objective-c++!

So now I rename all my .m files to .mm (compiles as objective-c++) and use c++ standard containers for data management.

Thus the "array of weak pointers" problem becomes a std::vector of __weak object pointers:

#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
  }
}

Which is equivalent to the c++ idiom:

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
  }
}

Proof of concept (in the light of Tommy's concerns about vector reallocation):

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]);
}

example log output:

2016-09-21 18:11:13.150 foo2[42745:5048189] Thing
2016-09-21 18:11:13.152 foo2[42745:5048189] (null)
静赏你的温柔 2025-01-13 08:29:43

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

NSPointerFunctionsWeakMemory

使用适合 ARC 或 GC 的弱读写屏障。使用 NSPointerFunctionsWeakMemory 对象引用将在上次版本中变为 NULL。

If you do not require a specific order you could use NSMapTable with special key/value options

NSPointerFunctionsWeakMemory

Uses weak read and write barriers appropriate for ARC or GC. Using NSPointerFunctionsWeakMemory object references will turn to NULL on last release.

雄赳赳气昂昂 2025-01-13 08:29:43

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

I believe the best solution for this is to use NSHashTable or NSMapTable. the Key or/and the Value can be weak. You can read more about it here: http://nshipster.com/nshashtable-and-nsmaptable/

放血 2025-01-13 08:29:43

要将弱自引用添加到 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,并且引用将在适当的时间被释放,独立于该数组引用

To add weak self reference to NSMutableArray, create a custom class with a weak property as given below.

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];

}

Step 3: later on, if the property delegateWeakReference == nil, the object can be removed from the array

The property will be nil, and the references will be deallocated at proper time independent of this array references

看海 2025-01-13 08:29:43

最简单的解决方案:

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。

The simplest solution:

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);

Note: And this works on iOS 4.x too.

携余温的黄昏 2025-01-13 08:29:43

不,那是不正确的。这些实际上并不是弱引用。您现在无法真正将弱引用存储在数组中。您需要有一个可变数组,并在使用完它们后删除引用,或者在使用完后删除整个数组,或者滚动您自己的支持它的数据结构。

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

No, that's not correct. Those aren't actually weak references. You can't really store weak references in an array right now. You need to have a mutable array and remove the references when you're done with them or remove the whole array when you're done with it, or roll your own data structure that supports it.

Hopefully this is something that they'll address in the near future (a weak version of NSArray).

醉城メ夜风 2025-01-13 08:29:43

我刚刚遇到了同样的问题,发现我的 before-ARC 解决方案在按照设计使用 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] 添加到
设置 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] 添加到
设置 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] * -[TestObj
respondsToSelector:]: 消息发送到已释放的实例 0x1f03c8c0

使用 iOS 版本 4.3、5.1、6.2 检查。
希望它对某人有用。

I've just faced with same problem and found that my before-ARC solution works after converting with ARC as designed.

// 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

Output:

2013-06-30 00:59:10.266 not_retained_collection_test[28997:907]
constructed 2013-06-30 00:59:10.267
not_retained_collection_test[28997:907] addet to
set 2013-06-30 00:59:10.581 not_retained_collection_test[28997:907]
constructed 2013-06-30 00:59:10.582
not_retained_collection_test[28997:907] addet to
set 2013-06-30 00:59:10.881 not_retained_collection_test[28997:907]
constructed 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]
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:]: message sent to deallocated instance 0x1f03c8c0

Checked with iOS versions 4.3, 5.1, 6.2.
Hope it will be useful to somebody.

揽清风入怀 2025-01-13 08:29:43

如果您需要归零弱引用,请参阅此答案以获取可用于包装类的代码。

该问题的其他答案建议使用基于块的包装器,以及方法自动从集合中删除归零的元素。

If you need zeroing weak references, see this answer for code you can use for a wrapper class.

Other answers to that question suggest a block-based wrapper, and ways to automatically remove zeroed elements from the collection.

小红帽 2025-01-13 08:29:43

如果你经常使用这个行为,它会指示你自己的 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;
}

If you use a lot this comportment it's indicated to your own NSMutableArray class (subclass of NSMutableArray) which doesn't increase the retain count.

You should have something like this:

-(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;
}
半世晨晓 2025-01-13 08:29:43

我认为 Erik Ralston 先生在他的 Github 存储库 https://gist.github 上提出了一个优雅的解决方案

。 com/eralston/8010285

这是基本步骤:

为 NSArray 和 NSMutableArray 创建一个类别,

在实现中 创建一个具有弱属性的便利类。您的类别会将对象分配给这个弱属性。

.h.m

 #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

#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

I think an elegant solution is what Mr. Erik Ralston propose on his Github repository

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

this are the essential steps:

create a category for NSArray and NSMutableArray

in the implementation create a convenience class with a weak property. Your category will assign the objects to this weak property.

.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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文