My problem is when I copy an array with objects, it seems that if i change the copied array, the original array changes as well. Below is a simplified version of my code.
I have an array of objects
#interface TimesViewController (){
NSMutableArray *route1;
}
I fill these objects up in my ViewDidLoad method
route1 = [[NSMutableArray alloc] init];
while (sqlite3_step(statement) == SQLITE_ROW) {
StopsOnRoutes *stopOnRoutes = [[StopsOnRoutes alloc] init];
[stopOnRoutes setStart_time:p_time];
[stopOnRoutes setStart_route_id:p_route];
[stopOnRoutes setStart_stop_id:p_stop];
[stopOnRoutes setStop_time:c_time];
[stopOnRoutes setStop_route_id:c_route];
[stopOnRoutes setStop_stop_id:c_stop];
[stopOnRoutes calc];
[route1 addObject:stopOnRoutes];
}
BUT when I try to copy route1 and make a few changes, they change both in route1 and amTimes
NSMutableArray *amTimes = [[NSMutableArray alloc] init];
[amTimes addObjectsFromArray:route1];
for(int i = 0; i<amTimes.count; i++){
[[amTimes objectAtIndex:i] setStop_time:[[amTimes objectAtIndex:i] stop_time]-86400];
[[amTimes objectAtIndex:i] setStart_time:[[amTimes objectAtIndex:i] start_time]-86400];
}
How can I copy route1 to amTimes, so if I change an object in AM times, it won't change in route1.
The way arrays work they simply hold references (pointers) to the objects in the array. Therefore when you add objects, it does not create a new object and point to it, it simply points to that very same object. What you want to do is referred to as a deep copy array, which involves coping each object inside the array.
Which is what happens when you use
- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag
Each object in the array is send a copyWithZone, you can implement copyWithZone in each of the objects you want to add to your array, and then do
[myArray addObject[myObject copy]];
Or you can also use
[[NSArray alloc] initWithArray:otherArray copyItems:YES] //You need to implement copyWithZone in the items you want to copy.
This will give you the result that you want.
You need to have your StopOnRoutes class implement the NSCopying protocol and the copyWithZone: method. This method needs to create a copy of self.
Then you can do this:
NSMutableArray *amTimes = [[NSMutableArray alloc] init];
for (StopOnRoutes *obj in route1) {
[amTimes addObject:[obj copy]];
}
Container objects don't contain copies of the objects that are added to them, they contain pointers to the objects. If you add the same object to 2 arrays, it is a member of both arrays.
If you want to create an array of copies, you need to do several things:
You need to implement the NSCopying protocol for the objects in your array so you can copy them.
Then you'd need to create a new mutable array with room for the same number of elements as the first array, loop through the first array, copy each item, and add it to the second array.
If your array contains only standard system objects that support NSCopying then your work is simpler. If your array contains different kinds of custom objects or complex custom objects then you might have more work to do.
Try using setArray: i.e.[amTimes setArray:route1];
Related
I have an NSMutableArray 'myArray' which contained some customObject 'A', Now I want to convert 'myArray' to tempArray which contained some object which subclass 'A' named as 'B'.
As a property just like follow:
NSMutableArray <B *> tempArray;
tempArray = [myArray mutableCopy];
But the object in tempArray always is kind of 'A'. I want convert 'A' to 'B', Any suggestion?
'B' has more property which i want to use.
How do you expect this to work? B is a child of A, it contains more information (the property you wish to use etc.). Where would the data for that property come from?
You need a way to construct a new instance of B using (the properties of) an instance of A and supplying the extra data a B needs. B might already have a suitable init method, or you may need to write your own code.
Once you have a way to produce a new B you can just iterate over your array building a new one, building a new B instance for each A instance.
HTH
If you have an immutable array (NSArray) that means you can't add or remove objects, or replace objects with other objects. [myArray mutableCopy] creates an NSMutableArray. It will contain exactly the same objects as the immutable array, but you are now free to add or remove or replace objects.
Your declaration NSMutableArray * tempArray does nothing but lie to the compiler. If myArray contained objects of type A*, then tempArray contains the same objects of type A*. You are just lying, so the compiler believes they are objects of type B*, but they are not.
There is no way on earth how a compiler could automatically convert an object of some class to an object of a subclass. If the subclass has additional members, for example, how is the compiler going to fill those members? This just cannot work.
If you want to create new objects of type B*, based on objects of type A*, you have to do that by hand, likely using an initialiser like
- (instancetype)initWithA:(A*)a;
in your B interface. Your code could be for example
NSMutableArray <B*> *tempArray = [myArray mutableCopy];
for (NSUInteger i = 0; i < tempArray.count; ++i) {
tempArray [i] = [[B alloc] initWithA:tempArray [i]];
}
or possibly
NSMutableArray <B*> *tempArray = [[NSMutableArray alloc] init];
for (A* a in myArray)
[tempArray addObject:[[B alloc] initWithA:a]];
I've made a NSMutableArray a property on my view controller, which holds some core data objects for users.
When the user presses a button , I clear out the contents of the mutable array with a while loop,
while (self.mArray.count != 0){
[context deleteObject:self.mArray[0]];
[self.mArray removeObjectAtIndex:0];
}
After the while loop, I reinit the array:
self.mArray = [[NSMutableArray alloc] init];
I know it isn't necessary since there should be zero objects left in the array, but regardless, when I reinitialize the array, and then check the class of the array in the debugger, I get __NSArrayI, which is corroborated by an exception thrown when I attempt to add an object into self.mArray right afterwards.
I've looked for any other references to my array, but I've always passed around [self.mArray mutableCopy] as arguments to other methods, and I never cast it as an NSArray. I just don't understand how calling [[NSMutableArray alloc] init] would initialize the array as an immutable array.
What am I missing?
It's likely that you have declared mArray as copy in the property declaration. Change that to strong.
I am trying to check if the NSMutableArray has a specific object, before adding the object to it, if exists then don't add.
i looked over many posts explaining how to do this, managed to implement it like this, but it always gives me that the object "doesn't exist", though i already added it !
//get row details into FieldLables Object
AllItemsFieldNames *FieldLabels = feedItems[row];
// object to hold single row detailes
AllItemsFieldNames *SelectedRowDetails = [[AllItemsFieldNames alloc] init];
SelectedRowDetails.item_name = FieldLabels.item_name;
//SelectedRowDetails.item_img = FieldLabels.item_img;
SelectedRowDetails.item_price = FieldLabels.item_price;
//NSLog(#"item has been added %#", SelectedRowDetails.item_name);
//NSLog(#"shopcartLength %lu", (unsigned long)SelectedFieldsNames.count);
if([SelectedFieldsNames containsObject:SelectedRowDetails])
{
NSLog(#"Already Exists!");
}
else
{
NSLog(#"Doesn't Exist!");
[SelectedFieldsNames addObject:SelectedRowDetails];
}
I can display all object from the NSMutableArray into a table, what i need to do in the above code is stop the addition of duplicate objects.
The first method listed on the NSArray documentation under the section "querying an array" is containsObject:. If it's not working, that suggests that your implementation of isEqual: is not correct. Make sure you follow the note in the documentation:
If two objects are equal, they must have the same hash value. This
last point is particularly important if you define isEqual: in a
subclass and intend to put instances of that subclass into a
collection. Make sure you also define hash in your subclass.
You might also consider using an NSSet since you can't add duplicates to that. Of course, this would also require a working version of isEqual:.
Sets are composed of unique elements, so this serves as a convenient way to remove all duplicates in an array.
here some sample,
NSMutableArray*array=[[NSMutableArray alloc]initWithObjects:#"1",#"2",#"3",#"4", nil];
[array addObject:#"4"];
NSMutableSet*chk=[[NSMutableSet alloc ]initWithArray:array]; //finally initialize NSMutableArray to NSMutableSet
array= [[NSMutableArray alloc] initWithArray:[[chk allObjects] sortedArrayUsingSelector:#selector(compare:)]]; //after assign NSMutableSet to your NSMutableArray and sort your array,because sets are unordered.
NSLog(#"%#",array);//1,2,3,4
I have a NSMutableArray that i define in the header file as:
#property (strong, nonatomic) NSMutableArray *tempPhotosArray;
Then i allocate as:
_tempPhotosArray = [[NSMutableArray alloc] init];
What i'd like to know is if i then go to replaceObjectAtIndex the program will complain on an out of bounds. I want to keep only a set number of items in that array, so is it possible to do a insert or replace? i.e. if at index 0 it is empty do an insert, if there is an object already replace it?
Thanks
i think i agree with Hani Ibrahim. Since you said you only want to keep a set number of objects in the array. So how many you want?
// add these code when you initialize the array
int aSetNumber = 5;
_tempPhotosArray = [[NSMutableArray alloc] init];
for (int i = 0; i < aSetNumber; i++)
{
[_tempPhotosArray addobject: [NSNull null]];
}
i guess then you can do whatever you want, i don't know what exactly you want to do in this case, but i would check if the object in that position is NSNUll, if so, replace that, if not, i don't know what you want them
//use these code when you trying to insert the real object
if([[_tempPhotoArray objectAtIndex:anIndex] isKindOfClass: [NSNull class]])
{
//replace it here
}
As to why you are getting an error, what everyone else wrote is accurate, but....
The description of what you want doesn't match what an NSArray is. It sounds like you want a list of up to 5 items and never more than 5. It might be that if you try to add a 6th item the "oldest" goes away. Like a "recently opened" file history. You can make this type of functionality with an NSArray, but that's not what it is out of the box.
I would suggest making your own object class. I'm not going to write all the code for you, because this sounds suspiciously like programming homework, but I will point you in the correct direction.
FivePack <-- our class
NSArray *storage; <-- where we house the data
// a public method which lets you add things.
- (void)addItem:(id)item {
int indexOfLastItemInArrayToSave = 4;
if (storage.length < 4)
indexOfLastItemInArrayToSave = length-1;
NSRange range = NSMakeRange(0, indexOfLastItemInArrayToSave);
NSArray *temp = [storage subArrayWithRange:range];
// now create a new array with the first item being "item" that
// was passed in and the rest of the array being the contents of temp.
// Then save that to storage.
}
What you want to do with the data and writing something to get it from your new object is up to you, because I'm not sure how you want to do it.
There are no objects in the array when you initially created it, so there is nothing to replace.
Like this?
if([_tempPhotosArray count] > 0)
//replace object
else
//add object to array
I'm trying the following code to create an instance, assign properties, add to array.
Then, assigning new properties and adding again.
However array will contain 2 identical objects (equal to the second one added). The class Message simply has several (nonatomic, retain) NSStrings/Integer properties.
This probably has something to do with my understanding of pointer, can someone explain?
self.messages=[[NSMutableArray alloc]init];
Message *m=[[Message alloc]init];
m.cb=#"2402";
m.ck=1001;
m.msg=#"as";
[self.messages addObject:m];
m.cb=#"2422";
m.ck=1002;
m.msg=#"aadfsdsdfdssdklsdflkh";
[self.messages addObject:m];
NSLog(#"%#",self.messages);
When you add an object to an array, it does not add a copy of the object to the array, but instead just a reference to it. If you want two different objects, then you need to create two different objects instead of re-using the same one (or, as #Brendon points out, create a copy when you add it to your array).
To fix your example, the most common technique would be to add the following line right before you start modifying the properties for the second object:
m=[[Message alloc]init];
Or, use a second pointer and object instead of reusing m.
EDIT:
To add a copy, change [self.messages addObject:m]; to [self.messages addObject:[m copy]];, assuming that the Message class conforms to the NSCopying protocol.
Yes, after executing the posted code self.messages contains the Message object twice, at indexes 0 and 1. That's not a problem, though. Arrays can contain any object, even themselves.
It seems that you want two distict objects, so you would just create a second Message.
You can either implement the NSCopy protocol — as mentioned by lnafziger — or just create new instances quite easily in a for loop.
«Two or more, use a for»
— Edsger W. Dijkstra
self.messages=[[NSMutableArray alloc] init];
NSArray *dataArray = #[ #{#"cb": #"2402", #"ck": #(1001), #"msg": #"as"},
#{#"cb": #"2422", #"ck": #(1002), #"msg": #"aadfsdsdfdssdklsdflkh"}
];
for(NSDictionary *data in dataArray) {
Message *m=[[Message alloc] init];
m.cb = data[#"cb"];
m.ck = [data[#"ck"] integerValue];
m.msg = data[#"msg"];
[self.messages addObject:m];
}