Kahsn
Kahsn

Reputation: 1045

Conflict between declaring instance variable and property

I am studying Objective-C. I asked a question about this code earlier but I came up with further questions. The below code is trying to make NSArray externally but really makes NSMutableArray internally so I can add pointers or remove in NSMutableArray

I face two questions. 1) What is the purpose of doing like this? Is there a specific reason you make NSArray externally? Why can't I just declare a property of NSMutableArray?

2)I learn that instance variable (_assets) is made when I declare a property of NSArray *assets. And I also declared NSMutableArray *_assets under the interface. I think those two _assets conflict each other even though they have different types. Am I thinking this in a wrong way?

@interface BNREmployee : BNRPerson
    {
        NSMutableArray *_assets;
    }

@property (nonatomic) unsigned int employeeID;
@property (nonatomic) unsigned int officeAlarmCode;
@property (nonatomic) NSDate *hireDate;
@property (nonatomic, copy) NSArray *assets;

Upvotes: 1

Views: 101

Answers (5)

Aviel Gross
Aviel Gross

Reputation: 9965

Another approach might be to make the property only writable to the implementation of you class.

To do that you declare your property as readonly in the header:

//BNREmployee.h
@property (nonatomic, readonly) NSMutableArray *assets;

Than declare it as readwrite inside an inner interface in your implementation:

//BNREmployee.m
@interface BNREmployee()
@property (nonatomic, readwrite) NSMutableArray *assets;
@end

@implementation
...

Upvotes: 0

Tim Johnsen
Tim Johnsen

Reputation: 1491

The reason you'd internally keep an NSMutableArray, but expose an NSArray externally is so that users of your API won't abuse it and mutate its data. Keeping it visible as immutable makes people less prone to mess with it.

Another approach you could take to this is to not use a property at all, but simply have a getter and a mutable property in a class extension. For example, in your .h:

@interface BNREmployee : BNRPerson

- (NSArray *)assets;

@end

In your .m

@interface BNREmployee ()

// Inside of the class manipulate this property
@property (nonatomic, strong) NSMutableArray *mutableAssets;

@end

@implementation BNREmployee

// Clients of your class rely on this
- (NSArray *)assets
{
    // copy makes the result immutable
    return [self.mutableAssets copy];
}

@end

Upvotes: 0

EmilioPelaez
EmilioPelaez

Reputation: 19884

Here's something you can do if you want _assets to be a mutable array, but you don't want other classes to modify it, implement the setter and getter of the assets property so they look like this (implementing the getter and the setter will cause the property to not be synthesised, which means the NSArray *_assets will not be created automatically):

-(NSArray *)assets{
  return [_assets copy]; // Copy creates an immutable copy
}
-(void)setAssets:(NSArray *)assets{
  _assets = [NSMutableArray arrayWithArray:assets];
}

Keep in mind that if you access the assets array a LOT, it might be slow since you're creating an immutable copy every time, so you can create an NSArray whenever your _assets array is modified and return that in the -(NSArray *)assets method

Upvotes: 1

Ganesh Somani
Ganesh Somani

Reputation: 2360

I'll try put your answers the way you have asked them. Let hope they clear your doubts. By now I guess you would be knowing that NSArray once initialised with data you wont be able to add or delete the data inside it which is different from NSMutableArray.

The benefit here no one else can change your externally visible data. Also when you try to sort or iterate the array you are sure that no other data would be removed or added. Also if you use NSMutableArray for such cases the application would crash if you add data while you iterate the array.

Like @KirkSpaziani Explained

@synthesize assets = _assets;

would create an instance variable for your property. However you are actually supposed to use this _assets only in getter and setter. Else places you should be using self.assets.

You can also synthesize your other array NSMutableArray *_assets as follows

@synthesize _assets = __assets; Which would have double underscore, but frankly we shouldn't be using the underscore for a starting variable name. Plus would be great if you have different names altogether.

Also with advances in Objective C you dont require to synthesize these variables at all. Just use the self.variableName and you can access it.

Hope it clears some of your queries.

Upvotes: 1

KirkSpaziani
KirkSpaziani

Reputation: 1972

Put { NSMutableArray *_assets; } in the @implementation block

@implementation {
   NSMutableArray *_assets;
}

Putting the NSMutableArray in the implementation block hides the fact that it is mutable from consumers (it is no longer in the header file).

Follow it with:

@synthesize assets = _assets;

This might not be necessary actually, but makes things clearer. When you declare a property an ivar will be automatically created (unless you @dynamic the property). However an explicitly declared ivar of the same name will override the automatically created one - so long as the type is the same or a subclass.

The reason to make it an NSArray publicly visible is so that no one else can mutate your data structure. You will have control of it. If it is an NSMutableArray internally then you can add and remove items without exposing that functionality to consumers.

You can declare your property to be readonly or readwrite - a readwrite NSArray means you can replace the whole array with a property set, but you can't add or remove items. If internally you are adding and removing items, this can make things messy. Try to stick with readonly when having a mutable internal version.

Upvotes: 1

Related Questions