1

Suppose that I have a class that holds a mutable array. I want to make sure that if other classes ask for the array they will get a non-mutable type, but in the owning class, it is actually an instance of NSMutableArray, so that I can add and remove items.

#import "Person.h"

@class Asset;

@interface Employee : Person

{
    NSMutableArray *_assets;
}

@property (nonatomic,copy) NSArray *assets;

-(void)addAssets:(Asset *)a;

The question is, do I have to modify the accessor methods into something like this, or will it automatically behave like I want?

#import "Employee.h"
#import "Asset.h"

@implementation Employee

/* Accessors for assets properties
-(NSArray *)assets
{
    return [_assets copy];
}

-(void)setAssets:(NSArray *)assets
{
    _assets = [assets mutableCopy ];

}
*/

-(void)addAssets:(Asset *)a
{
    //is assets nil?

    if (!_assets) {
        //Create Array
        _assets = [[NSMutableArray alloc]init];
    }
    [_assets addObject:a];

}
jscs
  • 63,694
  • 13
  • 151
  • 195

2 Answers2

3

ppalancica's answer is incorrect. The copy attribute means only that the setter will take a copy when the property is set. The synthesized getter will not return a copy. You must implement that behavior yourself:

- (NSArray *)assets
{
    return [_assets copy];
}

You might want to make an internal-only accessor that doesn't make a copy. You could also redeclare the property privately; client code will then be contracted to treat the array it requests as immutable.

This code demonstrates that the synthesized getter returns the uncopied object:

#import <Foundation/Foundation.h>

@interface ArrayReturner : NSObject

@property (copy, nonatomic) NSArray * array;

@end

@implementation ArrayReturner
{
    NSMutableArray * _array;
}

- (BOOL)myArrayIsIdenticalTo:(NSArray *)otherArray
{
    return _array == otherArray;
}

@end

int main(int argc, const char *argv[])
{
    @autoreleasepool {

        ArrayReturner * a = [ArrayReturner new];
        [a setArray:@[@1, @2]];
        NSArray * returnedArray = [a array];
        // Does not throw
        NSCAssert([a myArrayIsIdenticalTo:returnedArray],
                  @"Returned array is a separate instance.");
    }

    return 0;
}
Community
  • 1
  • 1
jscs
  • 63,694
  • 13
  • 151
  • 195
  • what should I do to make synthesized getter to return a copy? well, I tried to modify your example code by modifying getter to return a copy. but still myArrayIsIdentical results in identical match. – user3213703 Nov 06 '14 at 14:40
  • What class are you using for your test? Immutable classes often won't actually return a new object when you copy them, to save memory. – jscs Nov 06 '14 at 20:06
  • I replicated your ArrayReturner class and changed it into Mutable one (mutable array), well it turns out I have to implement both setter and getter methods in mutable class in case I want to get new copied object (array) from MutableArrayReturner Class. – user3213703 Nov 07 '14 at 01:10
0

Because you already specified the attribute "copy" for the array property, there is no need to override the getter and setter. The compiler will do all the heavy work for you.

If you specify "strong" instead, the getter and setter would look like:

-(NSArray *)assets
{
    return _assets;
}

-(void)setAssets:(NSArray *)assets
{
    _assets = assets;
}

And that may be a problem.

There is actually a WWDC conference that explains all these details. For NSString properties it is more recommended to use copy, and you can see it a lot like that in the iOS SDK frameworks.

ppalancica
  • 4,236
  • 4
  • 27
  • 42
  • Guessing you mean a WWDC session -- the entire conference covers a great many topics. It'd be helpful to specify which session you're talking about, maybe even provide a link to the video. – Caleb Aug 04 '14 at 04:22
  • Yes, session, sorry. An useful link may be: https://developer.apple.com/videos/wwdc/2012/ Sessions 405, 413, or any other related to memory management and effective Objective-C – ppalancica Aug 04 '14 at 04:57
  • `copy` won't make the getter return a copy; the property specifier only affects the setter. – jscs Aug 04 '14 at 09:17
  • This answer is incorrect; the synthesized getter will **not** return a copy: https://gist.github.com/woolsweater/38ad03759e6245bf6733 You must write your own if you want that behavior. – jscs Aug 15 '14 at 18:33