NSArray #property backed by a NSMutableArray - objective-c

I've defined a class where I'd like a public property to appear as though it is backed by an NSArray. That is simple enough, but in my case the actual backing ivar is an NSMutableArray:
#interface Foo
{
NSMutableArray* array;
}
#property (nonatomic, retain) NSArray* array;
#end
In my implementation file (*.m) I #synthesize the property but I immediately run into warnings because using self.words is the same as trying to modifying an NSArray.
What is the correct way to do this?
Thanks!

I would declare a readonly NSArray in your header and override the getter for that array to return a copy of a private NSMutableArray declared in your implementation. Consider the following.
Foo.h
#interface Foo
#property (nonatomic, retain, readonly) NSArray *array;
#end
Foo.m
#interface Foo ()
#property (nonatomic, retain) NSMutableArray *mutableArray
#end
#pragma mark -
#implementation Foo
#synthesize mutableArray;
- (NSArray *)array
{
return [[self.mutableArray copy] autorelease];
}
#end

Basically, put the NSArray property in a category in your header file and the NSMutableArray property in the class extension in your implementation file. Like so...
Foo.h:
#interface Foo
#end
#interface Foo (Collections)
#property (nonatomic, readonly, strong) NSArray *someArray;
#end
Foo.m
#interface Foo ()
#property (nonatomic, readwrite, strong) NSMutableArray *someArray;
#end

Simple:
1) Don't use a property when it ain't one.
2) Code simplifies to:
- (NSArray *)currentArray {
return [NSArray arraywithArray:mutableArray]; // need the arrayWithArray - otherwise the caller could be in for surprise when the supposedly unchanging array changes while he is using it.
}
- (void)setArray:(NSArray *)array {
[mutableArray setArray:array];
}
When the object is alloced create the array, when it dies, dealloc the array.
When large effects happen at the mere use of a '.' operator, its easy to overlook hugely inefficient code. Accessors are just that. Also - if someone calls aFoo.array - the contract is to get access to foo's array members - but really its just a copy at the time of the call. The difference is real enough that it caused bugs in the other implentations posted here.

Update: this answer is not valid anymore. Use one of suggested solutions below.
These days you can do the following:
Foo.m:
#implementation Foo {
NSMutableArray* _array;
}
#end
Foo.h:
#interface Foo
#property (readonly, strong) NSArray* array;
#end
You can still address mutable _array by ivar from the inside of implementation and outside it will be accessible via immutable property. Unfortunately this doesn't guarantee that others can't cast it to NSMutableArray and modify. For better protection from idiots you must define accessor method and return immutable copy, however that might be very expensive in some cases.
I would actually agree with one of the comments above that it's better to use simple accessor methods if you need to return some read-only data, it's definitely less ambiguous.

That's because your property must match the actual ivar's class type.
A possible solution/workaround:
//Foo.h:
#interface Foo
{
NSMutableArray* mutableArray;
}
#property (readwrite, nonatomic, retain) NSArray* array;
//or manual accessor declarations, in case you're picky about wrapper-properties.
#end
//Foo.m:
#interface Foo ()
#property (readwrite, nonatomic, retain) NSMutableArray* mutableArray;
#end
#implementation
#synthesize mutableArray;
#dynamic array;
- (NSArray *)array {
return [NSArray arrayWithArray:self.mutableArray];
}
- (void)setArray:(NSArray *)array {
self.mutableArray = [NSMutableArray arrayWithArray:array];
}
#end
You're adding a private mutableArray property in a class extension and making the public array simply forward to your private mutable one.
With the most recent language extensions of ObjC I tend to remove the
{
NSMutableArray* mutableArray;
}
ivar block entirely, if possible.
And define the ivar thru the synthesization, as such:
#synthesize mutableArray = _mutableArray;
which will generate a NSMutableArray *_mutableArray; instance for you.

Simplest answer: your property type (NSArray) doesn't match your instance variable type (NSMutableArray).
This is yet another good reason that you shouldn't define your own backing variables. Let #synthesize set up your instance variables; don't do it by hand.

Related

How to reference member variable in the implementation file

I am learning Objective-C and I am trying to split the class definition from the implementation as shown below.
Now in the code I want to reference the both of:
NSString *CarMotorCode;
NSString *CarChassisCode;
In the implementation file. I attempted to use:
self.CarMotorCode;
self.CarChassisCode;
But it does not work. Would you please let me know how to reference it.
Note: please let me know what is the right naming convention for the variables enclosed inside the brackets in the implementation section? Are they member variables?
Car2.m:
#import <Foundation/Foundation.h>
#import "Car2.h"
#implementation Car2
-(id) initWithMotorValue:(NSString *)motorCode andChassingValue:(NSInteger)ChassisCode {
self
}
#end
Car2.h
#ifndef Car2_h
#define Car2_h
#interface Car2 : NSObject {
NSString *CarMotorCode;
NSString *CarChassisCode;
}
-(id) initWithMotorValue: (NSString *) motorCode andChassingValue: (NSInteger) ChassisCode;
-(void) startCar;
-(void) stopCrar;
#end
#endif /* Car2_h */
You have declared instance variables (ivars). To get the “dot syntax”, you need to declare properties. The “dot syntax” is syntactic sugar that makes use of the “accessor methods” that are synthesized for you when you declare a property. (FWIW, it’s advised to not declare ivars manually, anyway, and rather to declare properties and let the compiler synthesize the necessary ivars. See Programming with Objective-C: Properties Control Access to an Object’s Values and Practical Memory Management: Use Accessor Methods to Make Memory Management Easier.)
Thus:
#interface Car2: NSObject
#property (nonatomic, copy) NSString *motorCode;
#property (nonatomic, copy) NSString *chassisCode;
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSString *)chassisCode;
#end
And your init method might look like:
#implementation Car2
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSString *)chassisCode {
if ((self = [super init])) {
_motorCode = [motorCode copy];
_chassisCode = [chassisCode copy];
}
return self;
}
#end
That will synthesize ivars _motorCode and _chassisCode for you behind the scenes, but you generally wouldn’t interact directly with them (except in init method, in which case you should avoid accessing properties). But in the rest of your instance methods, you could just use the properties self.motorCode and self.chassisCode.
A few unrelated notes:
I dropped the car prefix in your property names. It seems redundant to include that prefix when dealing with a car object.
I start my property names with lowercase letter as a matter of convention.
I changed the init method signature to better mirror the property names (e.g. not initWithMotorValue but rather initWithMotorCode).
Alternatively, you might use the strong memory qualifier rather than copy. E.g.
#interface Car2: NSObject
#property (nonatomic, strong) NSString *motorCode;
#property (nonatomic, strong) NSString *chassisCode;
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSString *)chassisCode;
#end
And
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSString *)chassisCode {
if ((self = [super init])) {
_motorCode = motorCode;
_chassisCode = chassisCode;
}
return self;
}
But we often use copy to protect us against someone passing a NSMutableString as one of these properties and then mutating it behind our back. But this is up to you.
You defined chassisCode to be a string in your ivar declaration, but as an NSInteger in your init method signature. Obviously, if it’s an NSInteger, change both accordingly:
#interface Car2: NSObject
#property (nonatomic, copy) NSString *motorCode;
#property (nonatomic) NSInteger chassisCode;
- (id) initWithMotorCode:(NSString *)motorCode chassisCode:(NSInteger)chassisCode;
#end
and
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSInteger)chassisCode {
if ((self = [super init])) {
_motorCode = [motorCode copy];
_chassisCode = chassisCode;
}
return self;
}
If you’re wondering why I didn’t use the property accessor methods in the init method, please see Practical Memory Management: Don’t Use Accessor Methods in Initializer Methods and dealloc.

Hiding privately mutable properties behind immutable interfaces in Objective-C

So, what I basically want to ask is whether the following code is safe (not whether it works, because it does). I.e, will the public getter override the synthesized getter of the actionLog property [which is of a different type]?
.h file:
#interface SomeClass : NSObject
- (NSArray*) actionLog;
#end
.m file:
#interface SomeClass ()
#property (strong, nonatomic) NSMutableArray* actionLog;
#end
#implementation SomeClass
...
#end
This is not only OK, it is exactly why class extensions were created in the first place!
Yes, there will be a single automatically synthesized ivar and pair of getter/setter methods generated as expected.
Sorry -- missed the NSArray vs. NSMutableArray part. No, you can't do that; the types must be the same.
However, you don't want to return your mutable array anyway. First, the caller might modify it (a bug). But, more importantly, the caller will assume that the contents are immutable as implied by the API) and, thus, when that array's contents change out from under the caller, it may cause issue (example; caller can reasonably assume that the result of count will be stable and can be cached).
By backing the property with a mutable ivar, like this:
.h file:
#interface SomeClass : NSObject
#property (nonatomic, strong) NSArray *actionLog;
#end
.m file:
#implementation SomeClass{
NSMutableArray* _actionLog;
}
-(void)insertAction:(Action *)action{
if(!_actionLog){
_actionLog = [[NSMutableArray alloc] init];
}
[_actionLog addObject:action];
}
#end

Confusing Objective-C class structure

Here's a (reduced) class declaration from an example on apple's developer:
#interface myController : UITableViewController {
NSArray *samples;
}
#property (nonatomic, retain) NSArray *samples
What is the purpose of declaring
{
NSArray *samples;
}
when you declare it again as a property? If you leave out:
{
NSArray *samples;
}
you can still use #synthesize in your .m and get a reference to it!
I'm a little confused as to the purpose of the first declaration.
Thanks
Properties are just a handy way to declare accessors to you data. It usually leads to some member variable but not necessarily. And that member var can have different name:
#interface myController : UITableViewController {
NSArray *mSamples;
}
#property (nonatomic, retain) NSArray *samples
#end
#implementation
#synthesize samples = mSamples;
#end
Or you can use properties without vars at all:
#interface myController : UITableViewController {
}
#property (nonatomic, retain) NSArray *samples
#end
#implementation
-(NSArray*) samples {
//you can for example read some array from file and return it
}
-(void) setSamples:(NSArray*) arr {
//write that array to file or whatever you want
}
#end
With new compiler you can use properties without ivars at all, compiler will generate them for you implicitly.
With a property declaration, there is no purpose or benefit in explicitly declaring the backing instance variable. It's just leftovers from habit.
Edit: For iOS or Mac 64-bit Intel, explicitly declaring ivars was never needed for properties. But they were needed for other Mac work — hence the examples.
Also, I did find a difference. When an ivar is explicitly declared, unless you state otherwise, it is a protected ivar, available to subclasses. But when an ivar is implicitly created for a property, subclasses don't have access to the ivar.

Objective-C property, expose only superclass

I am declaring a property in my class in header file;
#property (readonly) NSArray *pages
That's how I want it to be exposed publicly. Internally though, I am going to allocating it as NSMutableArray so I can add/remove stuff from it. But to do that, I will have to type cast every time. Is there a better way to do this?
Thanks
Your approach is really bad. If you insist on exposing a mutable array with dynamic content, then modify your getter to return an immutable copy, otherwise you are going to get weird side effects and exceptions for mutations during fast enumeration.
There isn't a solution for this. You have to cast every time, or use different properties. Here is a sample for the second approach:
#interface MyClass : NSObject
#property (nonatomic, strong, readonly) NSArray *pages;
-(void)addObject:(id)obj;
#end
#interface MyClass()
#property (nonatomic, strong, readwrite) NSMutableArray *mPages;
#end
#implementation MyClass
-(id) init {
self = [super init]
if (self){
_mPages = [NSMutableArray array];
}
return self;
}
-(NSArray*)pages {
return [NSArray arrayWithArray:self.mPages];
}
-(void)addObject:(id)obj {
[self.mPages addObject:obj];
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
MyClass *m = [MyClass new];
[m addObject:#"x"]; // the collection is mutable
NSLog(#"%#",[m pages]); // but only accessible as an immutable copy
}
}
This will be expensive if you access the collection frequently, and may be out of sync with the internal mutable collection (which may be mutated while you iterate on the copy).
Copying can be avoided returning the internal mutable instance (NSMutableArray) disguised as an immutable class (NSArray), but that incurs the following risks:
The client could cast to mutable and change it.
The internal copy could be mutated. This will crash the application if you are iterating, or may cause an index out of range exception.
Note that the following idiom doesn't solve the problem:
#interface MyClass : NSObject
#property (nonatomic, strong, readonly) NSArray *pages;
#end
#interface MyClass()
#property (nonatomic, strong, readwrite) NSMutableArray *pages;
#end
This lets you set the variable, but not use it as a different class than the one declared in the interface. In other words, it forces you to cast on every use:
[(NSMutableArray*)pages addObject:#"x"];

Trouble with shadowing #property in objective-c

Why isn't a setter synthesized for myString in the example below? The basic assignment below results in myString being nil. Trying to use the setter [self setMyString:s]; results in an unrecognized selector exception.
// in .h file
#interface MyClass
#property (nonatomic, readonly) NSString *myString;
#end
// in .m file
#interface MyClass (MyCategory)
#property (nonatomic, copy) NSString *myString;
#end
#implementation MyClass
#synthensize myString;
- (void) someMethod:(NSString *) s {
myString = [s copy];
// why is myString nil here?
}
#end
Edit: the problem was with gdb. po myString printed Can't print description of a NIL object.. However NSLog(#"myString: %#", myString); printed the expected value.
The other two answers are correct, but I think they miss your intention. It's common to declare a property as read-only in the .h file, so that code outside the class implementation can't write it. Inside the .m file, you want it to be readwrite. This kind of redefinition is explicitly supported. However, you need to put the redeclaration as readwrite in a class-extension:
// In your .h file:
#interface MyClass : NSObject
#property (nonatomic, copy, readonly) NSString *myString;
#end
// In your .m file:
#interface MyClass () // Note the empty parentheses
#property (nonatomic, copy, readwrite) NSString *myString;
#end
You do still need to use self.myString = aString or [self setMyString:aString], instead of writing to the ivar directly as you're doing right now.
It looks like you're trying to declare a publicly readonly, privately writable property. You should do that in a class extension rather than a category. Syntactically, a class extension looks like a category with no name:
#interface MyClass ()
#property (nonatomic, copy) NSString *myString;
#end
Declaring a property with the same name in both your MyClass interface and your MyCategory category seems like a bad idea. Remove the declaration in the category and I expect all will be well.