Question

In objective C, its common practice to instantiate internal class arrays (and the like) in a lazy manner.

So if you call on the getter, it first checks if the array isn't nil, and allocates memory for it if needed.

But what about the setter? If you are trying to insert some value into one of the array cells, since we did not allocate memory for it yet - where does it go?

I'm missing something here, clearly. Would be happy for a clarification.

Was it helpful?

Solution

I'm not sure I understand your question, but if you do this:

@property (nonatomic, strong) NSMutableArray* myArray;
...

- (NSMutableArray *) myArray {
    if(!_myArray) {
        NSLog(@"created");
        _myArray = [[NSMutableArray alloc] init];
    }

    return _myArray;
}

...
[self.myArray addObject:@"test"];

The getter is actually getting called when you call addObject:, so you'll see "created" being logged.

OTHER TIPS

So @property declarations are syntactic sugar for declaring, in the case of objects, pointers to instance variables. The "nonatomic" refers to the type of getter and setter automatically created (in this case "non thread safe.") And the "strong" is an indicator to ARC to increase the retain count of the variable.

So when you declare:

@property (nonatomic, strong) NSMutableArray* myArray;

This is what really gets created in your class - just a pointer to your hidden instance variable.

@implementation MyClass {
    NSMutableArray *_myArray;
}

As you can see in the getter, you are initializing the _myArray pointer to point to a new NSMutableArray:

- (NSMutableArray *) myArray {
    if(!_myArray) {
        NSLog(@"created");
        _myArray = [[NSMutableArray alloc] init];
    }

    return _myArray;
}

However in the setter, you are just updating the pointer to a variable you have already created.

self.myArray = [[NSMutableArray alloc] init];

This sends your class the following message:

- (void) myArray: (NSMutableArray *) myArray {
    _myArray = myArray;
}

As you can, see the setter doesn't need any special initialization most of the time. The only time you want to create a custom setter is when you want to validate the incoming object has special properties. A contrived example is checking that the NSMutableArray is no larger than 10 objects:

- (void) myArray: (NSMutableArray *) myArray {
    if (myArray.count < 10) {
        _myArray = myArray;
    }
}

Finally, I would like to point out that you can actually lazy instantiate objects using the short ternary operator and parenthetical return values. For example, the following statement:

- (NSMutableArray *) myArray {
    return (_myArray = _myArray ?: @{}.mutableCopy);
}

Is equal to:

- (NSMutableArray *) myArray {
    if(!_myArray) {
        _myArray = [[NSMutableArray alloc] init];
    }
    return _myArray;
}

You can even macro this pattern into (WSM is my class prefix):

#define WSM_LAZY(object, assignment) (object = object ?: assignment)

So you can write statements like this:

- (NSMutableArray *) myArray {
    return WSM_LAZY(_myArray, @{}.mutableCopy);
}

Or even use compound statement syntax to rewrite the original setter you presented as an example:

- (NSMutableArray *) myArray {
    return WSM_LAZY(_myArray, ({
        NSLog(@"created");
        @{}.mutableCopy;
    }));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top