How to copy NSArray memory semantics to a subclass

Question

In my ARC project, I have a class that manages objects called LazyMutableArray. Some of the objects are actually zero, but users in my collection will never know about it; so I made it a subclass NSMutableArray, and it tries to do the same. "In particular, objects are saved when added .

Now let's look at the memory behavior of other methods. It turns out that the destruction methods documented by Apple are an exception to this rule, in which they release, not the auto-implemented object.NSArray

There is some debate that Apple cannot automatically destroy the addObject:+ objectAtIndex:+ combination of destroying the array, or it just happens in the tested examples, and in the example that includes Apple.

How can I create in my subclass a method with the same memory semantics?


Last update

After some thought, I decided that the implementation based on is NSMutableArraymore suitable in this case compared to NSPointerArray. The new class, I should note, has the same pair retain/ autoreleaseas the previous implementation.

Thanks to Rob Napier, I see that no modification of my method objectAtIndex:will change this behavior, which answers my initial question about this method.

, retain/autorelease ; , , . .

( NSMutableArray) GitHub: , , test ( -testLazyMutableMemorySemantics). .


NSMutableArray:

, , . ( , OData), . , , NSArray. .

OData " ", NSArray . , 1000 () 20, . , .


,

I unit test , , .. . , , .

? , lazy, , (* . *). weakSingleton, , 1. :

XCTAssertEqual(weakSingleton, lazy[0], @"Correct element storage"); // line B

, 2. , -retainCount ,

lazy[0] = nil; // yep, does the right thing
XCTAssertNil(weakSingleton, @"Dropped by lazy array"); // line C <-- FAIL

, , weakSingleton .

, , , , β€” @autorelease B weakSingleton. , , , NSPointerArray -addPointer: (, , ARC [[object retain] autorelease]). !

, overriding, NSMutableArray -objectAtIndex: `, ; , , , , Apple. , : A, /. , :)

1. ARC , ARC Objective-C. ARC.

2 ? , , , B, unit test C.

, [LazyMutableArray -objectAtIndex] , , 0, , , .

3 , , ; , , .


    @implementation LazyMutableArray {
        NSPointerArray *_objects; 
        // Created lazily, only on -setCount:, insert/add object.
    }

    - (id)objectAtIndex:(NSUInteger)index {
        @synchronized(self) {
            if (index >= self.count) {
                return nil;
            }
            __weak id object = [_objects pointerAtIndex:index];
                if (object) {
                    return object;
                }
            }

        // otherwise do something else to compute a return value
        // but this branch is never called in this test
        [self.delegate array:self missingObjectAtIndex:index];

        @synchronized(self) {
            if (index >= self.count) {
                return nil;
            }
            __weak id object = [_objects pointerAtIndex:index];
            if (object) {
                return object;
            }
        }
        @throw([NSException exceptionWithName:NSObjectNotAvailableException
                               reason:@"Delegate was not able to provide a non-nil element to a lazy array"
                             userInfo:nil]);

    }

    - (void)createObjects {
        if (!_objects) {
            _objects = [NSPointerArray strongObjectsPointerArray];
        }
    }

    - (void)addObject:(id)anObject {
       [self createObjects];
       [_objects addPointer:(__bridge void*)anObject];
    }

:

// Insert two objects into lazy array, one held weakly, one held strongly.

NSMutableArray * lazy = [LazyMutableArray new];
id singleton = [NSMutableArray new];
[lazy addObject:singleton];

__weak id weakSingleton = singleton;
singleton = [NSMutableDictionary new];
[lazy addObject:singleton];

XCTAssertNotNil(weakSingleton, @"Held by lazy array");
XCTAssertTrue(lazy.count == 2, @"Cleaning and adding objects");

//    @autoreleasepool {
XCTAssertEqual(weakSingleton, lazy[0], @"Correct element storage");
XCTAssertEqual(singleton, lazy[1], @"Correct element storage");
//    }

lazy = nil;

XCTAssertNotNil(singleton, @"Not dropped by lazy array");
XCTAssertNil(weakSingleton, @"Dropped by lazy array");

, lazy = [NSMutableArray new] , @autoreleasepool.

+4
2

-, . , NSPointerArray. , NSArray , . , [NSArray arrayWithArray:lazyMutableArray], lazyMutableArray NULL? , , NSArray NULL, , . , , CFArray NSArray; , ( ). , , (LSP).

, NSObject <NSFastEnumeration>. , NSPointerArray NSArray. . , , Apple.

, , , , - @autorelease B . , A ARC [[ ] autorelease]. !

. , . , , .

, , , , , .

, . , NSPointerArray:

__weak id weakobject;
@autoreleasepool
{
  NSPointerArray *parray = [NSPointerArray strongObjectsPointerArray];
  {
    id object = [NSObject new];
    [parray addPointer:(__bridge void*)object];
    weakobject = object;
  }
  parray = nil;
}
NSAssert(!weakobject, @"weakobject still exists");

(, ) , , .

autoreleasepool . / addPointer:, ARC, .

addObject:, . , , .

+2

, " ". , , , , .

NSMutableArray . - . , , , , . , .

, .

@interface LazyMutableArray : NSMutableArray
- (id)initWithCreator:(id(^)(int))creator;
@end

@interface LazyMutableArray ( ) 
@property (nonatomic, copy) id (^creator)(int);
@property (nonatomic, assign) NSUInteger highestSet;
@end

@implementation LazyMutableArray

- (id)initWithCreator:(id(^)(int))creator
{
    self = [super init];
    if (self) {
        self.highestSet = NSNotFound;
        self.creator = creator;
    }
    return self;
}

- (id)objectAtIndex:(NSUInteger)index
{
    id obj = nil;
    if ((index < self.highestSet) && (self.highestSet != NSNotFound)) {
        obj = [super objectAtIndex:index];
        if ([obj isKindOfClass:[NSNull class]]) {
            obj = self.creator(index);
            [super replaceObjectAtIndex:index withObject:obj];
        }
    } else {
        if (self.highestSet == NSNotFound) {
            self.highestSet = 0;
        }
        while (self.highestSet < index) {
            [super add:[NSNull null]];
            self.highestSet += 1;
        }
        obj = self.creator(index);
        [super add:obj];
        self.highestSet += 1;
    }
    return obj;
}

: . , , , , . , add:, count, removeObjectAtIndex:, insertObject:atIndex: , , replaceObjectAtIndex:withObject:. , , , .

+1

All Articles