NSArray of weak references (__unsafe_unretained) to objects under ARC

前端 未结 12 672
南旧
南旧 2020-11-27 11:26

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?



        
相关标签:
12条回答
  • 2020-11-27 12:15

    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.

    0 讨论(0)
  • 2020-11-27 12:19

    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;
    }
    
    0 讨论(0)
  • 2020-11-27 12:24

    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.

    0 讨论(0)
  • 2020-11-27 12:27

    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

    0 讨论(0)
  • 2020-11-27 12:30

    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/

    0 讨论(0)
  • 2020-11-27 12:31

    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
    
    0 讨论(0)
提交回复
热议问题