NSArray and NSMutable Array. Type of property and type of ivar are not the same - objective-c

I'm working through the big nerd ranch guide for Objective-C programming.
There is an example and challenge in chapter 21 that I've been stumped on. (I actually finished the challenge but I used previous code from an example) I'd like to actually understand what I did.
It has to do with declaring a property of type NSArray and then declaring the setter method to be of type NSMutableArray. Can anyone tell me what is going on here? What happens "behind the scenes" with this statement.
#property (nonatomic copy) NSArray *assets
and what is happening in these two files? Please explain at the lowest-level possible. Thanks!
BNREmployee.h
#interface BNREmployee: BNRPerson
{
NSMutableArray *_assets
}
#property (nonatomic copy) NSArray *assets
#end
BNREmployee.m
#implementation BNREmployee
- (void)setAssets:(NSArray *)a
{
_assets = [a mutableCopy]; //What did this actually do?
}
- (NSArray *)assets
{
return [_assets copy];
}

An NSMutableArray IS an NSArray (it is a subclass), so it just gets assigned normally. Nothing special happens behind the scenes.
An example I can give looking at your models would be assigning a BNREmployee object to a BNRPerson pointer which will work fine.
BNREmployee *employee = [BNREmployee new]; // or whatever initializer
BNRPerson *person = employee; // works fine, an employee IS a person
You can always assign an NSMutableArray to an NSArray pointer, but not the other way around.
Edit
"The property has type NSArray, which tells other classes, If you ask
for my assets, you are going to get something that is not mutable.
However, behind the scenes, the assets array is actually an instance
of NSMutableArray so that you can add and remove items in BNREmployee"
By exposing the public property as an immutable type you are letting any potential callers know that they can't modify the property without explicitly calling the mutator on the object. It also lets callers know once they get the collection, its contents will never be changed out from under them. This is the standard contract when exposing an immutable property. As pointed out in one of the comments below, there can be cases where a return type is immutable but the underlying object is actually mutable internally and can change, so good practice would be to make a copy when receiving the object if you are not already receiving an actual immutable copy, such as in the case of [NSView subviews].
Making the actual underlying variable a mutable type acts as a convenience to allow the owning class to modify it internally easily. Functionally, you could accomplish the same thing by making it a regular NSArray and constantly recreating it whenever you want to change its contents (such as array = [array arrayByAddingObject:object]). This will of course be slower than just modifying a mutable instance.

Related

Items disappearing from array within array

I create a NSMutableArray that I need as long as my app lives, lets call it suseranArray, just after the #implementation of my main class. This Array will hold several objects of a class called Vassal. A Vassal is simply:
1) A NSMutableString
2) Another NSMutableString
3) A NSMutableArray
4) Another NSMutable Array
Each Vassal created is also needed for the life of the app, and they never change.
These objects are made as (retain) properties in an .h file, synthesized in the .m file, and each given an alloc+init whenever the object Vassal is created during the init function. Each vassal has data filled in and stored in the suzerain Array. the 3rd item always has several elements, and after a bug appeared, I put a line to check if it is ever empty, but it never is, and life is good.
Now, later on when a certain Vassal object is needed, we try to access its 3rd property to fetch the data in there, and sometimes that array empty... I checked to see if it disappeared somehow, but it is always there on the debug, carrying a nice address like 0x2319f8a0 which makes sense since the NSMutableString just above it is at address 0x2319fb40 - (I was expecting 00000000 after a lot of headache). What is happening? I my head, I am creating an RETAINed objects, which retains data put in by default, and that object is put inside another, but somehow the data inside the array vanishes. What possible scenario could lead to this? Thank you for your time :)
Note: the last array currently just holds one item at this stage of development, and curiously enough, that one item is never missing, despite the two arrays being 'brothers'
Vassal.h
#interface Vassal : NSObject
#property (retain) NSMutableString *wordBody;
#property (retain) NSMutableString *wordCode;
#property (retain) NSMutableArray *wordRelations;
#property (retain) NSMutableArray *wordLinks;
#end
Vassal.m
#implementation Vassal:NSObject
#synthesize wordBody;
#synthesize wordCode;
#synthesize wordRelations;
#synthesize wordLinks;
-(NSObject*) init
{
if(self=[super init])
{
wordBody=[[NSMutableString alloc] init];
wordCode=[[NSMutableString alloc] init];
wordRelations=[[NSMutableArray alloc] init];
wordLinks=[[NSMutableArray alloc] init];
}
return self;
}
//Somewhere in Suseran:
-(void)fillStuff
{
...
Vassal *vassal=[Vassal new];
for (int i=0;i<[originalDataString length];i++)
{
...
[vassal.wordRelations addObject:anItem];
...
}
int errorTest=[vassal.wordRelations count];
if (errorTest==0)
{
//breakpoint here. Program NEVER comes here
}
[bigArrayOfVassals addObject:vassal];
}
//these arrays are never touched again but here:
-(void) getVassalstuff:(NSMutableString*)codeOfDesiredVassal
{
Vassal *aVassal;
for (int i=0;i<[bigArrayOfVassals count];i++)
{
aVassal=bigArrayOfVassals[i];
if ([codeOfDesiredVassal isEqualToString:aVassal.wordCode)
{
int errorTest=[aVassal.wordRelations count];
if (errorTest==0)
{
//yay! this breakpoint sometimes is hit, sometimes not,
//depending on code's mood. Why is this happening to me? :,(
}
}
}
}
I see that that you have properties that are mutable (which is itself a bad idea except for specific cases) and that you are retaining them.
Mutability means that if you have set the array as a property based on some other array, and if that original array is changed, the array in your property is also changed. It may be, and I don't know because you haven't shown any code, that you are emptying the original array, and thus changing the array you have as a property
Solutions:
My preferred solution is to use the immutable versions of these classes for your properties; NSString, NSArray and instead of retain use copy
A second solution is to leave the properties as mutable, but write a custom setter for each of them that stores a mutableCopy of the object that you pass in.
In both of these cases, your property will be a copy of the object used to set the property, so that if the object is changed outside of your class it will not affect your class's properties.
edited to add, after a comment
If you declare your property as
#property (copy) NSArray wordRelations;
Then simply writing
vassal wordArray = tempArray;
will do the same thing and is cleaner and more readable..

Objective-C Why use init?

I'm reading my first book on Objective-C [Programming in Objective-C 4th Edition], I'm midway through the book but one thing that bugs me, is that it didn't explain WHY we initialize objects.
I tried playing around with the with objects, for example allocating their memory but not initiating them and everything in the program works the same as before.
I'd appreciate some example explaining this, also.
The code within an init method is class specific - it performs whatever initialisation is required for that specific class. There are cases where a class does not need to perform any initialisation and thus removing this method call would have no effect.
However, by convention, you should always use init - what if someone were to add some required initialisation code to a class in the future?
See also:
alloc and init what do they actually do
To address you point of "everything works", the interesting thing about Objective-C is that alloc sets all instance variables to nil, and sending messages to nil doesn't do anything, it just returns nil, so in most of the cases you will not see a problem until you would try to do something illegal, consider a class like this
#interface MyObject : NSObject
#property (nonatomic, strong) NSString *string;
#end
#implementation MyObject
#end
Now if we'd just alloc it as:
MyObject *m = [MyObject alloc];
the instance variable _string, or property string would be nil, we could send different messages to it, like [string length] without any harm, since message to nil equals nil.
But say we then want to add this string to array, like
#[m.string]
Now you would get a exception, because NSArray cannot contain nil, only full blown objects. You can easily fix this by initializing your value inside MyObject.init.
Pretty contrived example, but hopefully shows the point of why everything doesn't break when you don't init :)
One of the main reasons why you should never use alloc's return value directly instead of using [[Class alloc] init]'s return value is that init might return a different object than alloc.
Apple's documentation mentions this:
Note: It’s possible for init to return a different object than was
created by alloc, so it’s best practice to nest the calls as shown.
Never initialize an object without reassigning any pointer to that
object. As an example, don’t do this:
NSObject *someObject = [NSObject alloc];
[someObject init];
If the call to init returns some other object, you’ll be left with a
pointer to the object that was originally allocated but never
initialized.
Source: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html

Objective C: Differentiating iVars and Accessors

#interface RandomObject : NSObject
{
NSString* someObject; // I know I don't have to explicitly declare. Just to clarify my point.
}
#property (nonatomic, strong) NSString *someObject;
#end
#implementation RandomObject
#synthesize someObject;
#end
Given the code above and Xcode 4.3 is used (hence, no auto-synthesizing), here is my question.
The property/synthesize will create accessors for someObject, namely getter and setter. So if I want to assign a value to someObject, I can do this.
self.someObject = #"Tomato"; // [self setSomeObject: #"Tomato"];
If my understanding is correct, self will send #"Tomato" to setSomeObject method. But what if you do this?
someObject = #"Tomato"; // or maybe _someObject = #"Tomato" if you are doing auto-synthesizing
Directly accessing an iVar object seems like a bad idea, but since someObject is a private variable, within the same class you have access to that, right?
I understand why you would need to use self.someOject if you want to manipulate someObject from another class. But why is it that you'd need to do the same even though you are still in the same class. Why is it that it's a bad idea to directly access iVar.
Generally speaking accessors have more pros than cons and I use them everywhere I can.
The main issue is that every place you reference the ivar directly is another potential place your code will need to change.
For example imagine you have referenced someObject in multiple places throughout your class. Then the requirements change and now you decide that when the value of someObject is assigned you need to so some other work. Due to the fact that you have accessed the ivar directly throughout the class you now have to either duplicate this new code everywhere you assign someObject or refactor. If you was using an accessor you just have one piece of code to change
- (void)setSomeObject:(id)anObject
{
if (anObject != someObject) {
someObject = anObject;
[self doSomeWork];
}
}
You can have the same issue with the getter - imagine you store an array of objects in someObjects - this works great but then later down the line you decide that you don't actually need to store someObjects as it can be dynamically computed from other values. If you have directly accessed the ivar everywhere then this becomes a big chore. If you stick to abstracting someObject behind a getter then all you now have to do is
- (NSArray *)someObjects
{
return [self calculateSomeObjects];
}
This is exactly the idea with non-ARC code, which puts the memory management of the ivar in one place (behind accessors) so that you do not have to litter your code with repetitive code.
The property does more than just assigning an object to the ivar.
If you don't use ARC, the property will auto-generate retain/release code to handle memory management. Just calling someObject = #"Tomato" creates a memory leak (if someObject is assigned)
If your property is atomic, the property will provide thread safety, while accessing the ivar would not be thread safe.
See https://stackoverflow.com/a/589348/1597531 for examples of auto-generated property code.

What is the rule of thumb for using #property(copy) vs. #property(retain)?

I wonder if there is a rule of thumb you follow, when deciding whether or not a given property in ObjectiveC should be a retain or copy?
How do you decide which it should be?
Typically you use copy for safety with classes which have mutable variants, like NSString, NSArray, the other collection classes, etc. To see why, consider what happens here...
Once upon a time,
#interface MyClass : NSObject
#property (retain) NSString *happyString;
- (void)rejoice;
#end
Then one day,
- (void)bigBadMethod {
MyClass *myObject = [[[MyClass alloc] init] autorelease];
NSMutableString *theString = [NSMutableString stringWithString:#"I'm happy!"];
myObject.happyString = theString; // this is allowed because NSMutableString inherits from NSString
[myObject rejoice]; // prints "I'm happy!"
when suddenly...
[theString setString:#"BRAAAAIIINNNSSSSS"];
[myObject rejoice]; // prints "BRAAAAIIINNNSSSSS"
}
And you wouldn't want that, would you? So use #property (copy) if you don't want to get mutated while you're not looking!
In a nutshell, assign vs retain vs copy determines how the synthesized accessors interact with the Objective-C memory management scheme:
assign is the default and simply performs a variable assignment
retain specifies the new value should be sent -retain on assignment and the old value sent release
copy specifies the new value should be sent -copy on assignment and the old value sent release.
Remember that retain is done on the created object (it increases the reference count) whereas copy creates a new object. The difference, then, is whether you want to add another retain to the object or create an entirely new object.

objective-c "mutating method sent to immutable object" error

I'm pretty new to objective-c and try to create a small app for the iphone.
I'm nearly done beside this little error here. Actually, I've searched hours with google to find a proper solution but unfortunately I'm not able to find a solution which works.
I'm using this tutorial here to build up an UITableView: UITableView Tutorial
The full error message looks like this:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '* -[NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
This is the Data Controller Header:
MyLinksDataController.h
#interface MyLinksDataController : NSObject {
NSMutableArray *tableList; //<---important part
}
- (unsigned)countOfList;
- (id)objectInListAtIndex:(unsigned)theIndex;
- (void)addData:(NSString *)data; //<---important part
- (void)removeDataAtIndex:(unsigned)theIndex;
#property (nonatomic, copy, readwrite) NSMutableArray *tableList; //<---important part
.....
And the Data Controller Method:
MyLinksDataController.m
#import "MyLinksDataController.h"
#implementation MyLinksDataController
#synthesize tableList;
- (id)init {
if (self = [super init]) {
NSLog(#"Initilizing DataController");
//Instantiate list
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.tableList = [localList copy];
[localList release];
//Add initial Data
[self addData:#"AAAAAAAAAAAAAA"];
[self addData:#"BBBBBBBBBBBBBB"];
}
return self;
}
-------------------------------later on in the source code---------------------------------
- (void)addData:(NSString*)data; {
[tableList addObject:data]; //<---- here the app crashes
}
I would pretty much appreciate any help.
Thank you for your support in advance.
Daniel
Sending the copy message to an NSMutableArray -- as in the following statement in init -- returns an immutable copy.
self.tableList = [localList copy];
Cocoa documentation uses the word immutable to refer to read-only, can't-be-changed-after-initialization objects. Hence the subsequenct call to addObject: fails with an error message.
Note how the assignment statement above doesn't trigger any compiler warning. copy returns an id, which fits comfortably -- as far as the compiler is concerned -- in the NSMutableArray* tableList. There's no runtime error here either, as no messages get passed around; an NSArray pointer is just placed in an NSMutableArray pointer variable.
To obtain a mutable copy, use mutableCopy instead.
Note that both copy and mutableCopy create a new array and copy the content of the original to it. A change in the copy will not be reflected in the original. If you just need another reference to the original array, use retain instead.
You can find more detail in the discussion section of the copyWithZone reference and in the NSMutableCopying protocol reference.
You're running into, basically, the memory management rules of Cocoa (specifically, these details). If there is an object with an immutable version and a mutable version, then sending -copy to an object will return an immutable object.
Let's step through the relevant part.
NSMutableArray *localList = [[NSMutableArray alloc] init];
This creates a new, empty mutable array that you own. Fine.
self.tableList = [localList copy];
This creates an immutable copy of the empty array. Furthermore, you own this freshly created copy. That's two objects you own at the moment.
This also assigns your copied object to the tableList property. Let's look at the property declaration:
#property (nonatomic, copy, readwrite) NSMutableArray *tableList;
This property is declared with the copy attribute, so whenever a new value is assigned to it, another -copy method is sent to it. This third copy, however, is not owned by you—it's owned by the object.
[localList release];
That releases the original empty mutable array. Fine, but there's still the one you made in the second line floating around, unreleased. That's a memory leak.
If you actually need a mutable copy of something, you want the -mutableCopy method. (The documentation for these methods is found under NSCopying and NSMutableCopying.) However, you're never going to get a mutable version of something into a property with the copy attribute, since it will send -copy to whatever it is assigned. Your property should use the retain attribute instead of the copy attribute, and the code to initialize it should look something like this:
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.tableList = localList;
[localList release];
Or, a shorter version:
self.tableList = [NSMutableArray array];
There's no need to copy anything in this situation, you're just creating a fresh object.
If u are assigning localList from another object may be that is not Mutable in that case it can through this kind of error.
I hope it will be helpful.
self.tableList = [localList mutableCopy];
Hi instead of mutableCopy i believe "strong" can also be used to tackle this problem. I had similar problem in my code as well because of using "copy" instead of "strong." So the below line:
#property (copy, nonatomic) NSMutableArray *computers;
It should be:
#property (strong, nonatomic) NSMutableArray *computers;
Hope it will be of immense help for beginners making mistakes like me.
This will resolve the issue:
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.localList = [[NSMutableArray alloc]initWithArray:localList];