Passing a simple NSMutableArray change original - objective-c

I am having hard time with a simple array that i want to pass .
I have a class with some NSMutableArray that i pass to another class(the array is global from singleton)
[mgzm saveWithArray:[Globals sharedGlobals].allImages];
To this function :
-(void)saveWithArray:(NSMutableArray*)currentArray
{
dataToSave=[[NSMutableArray alloc] init]; //local
dataToSave=[currentArray mutableCopy]; //copy
Than i saw that is is changing the original array which i don't want .
So i did this :
dataToSave=[[NSMutableArray alloc] initWithArray:currentArray copyItems:YES];
Which result in a situation that i can't change the dataToSave array get get a crash when trying to.(it needs to be changed).
Than i did this :
for(NSMutableDictionary *dic in currentArray)
[dataToSave addObject:dic];
Which again if i change dataToSave it change also the original array (?! )
Is there a way in this language to COPY array without changing the original one ????

When you make a copy of a mutable array, changing the array copy does not change the original array, not the objects inside the array. Here is what happens when you call [currentArray mutableCopy]:
The two arrays are pointing to the same objects. If you remove an object from the copy, the original array would still have it. However, if you modify the object itself (say, change the name of A to X) the change will reflect on the object in the original array, because it is the same object.
Here is what you want to happen:
Now the two arrays are completely independent of each other. This is the effect that you achieve when you call
dataToSave=[[NSMutableArray alloc] initWithArray:currentArray copyItems:YES];
However, there is a catch: in order for this to work, the objects inside the array must conform to NSCopying protocol, otherwise the code is going to crash.
To fix this, make sure that the objects inside NSMutableArray implement NSCopying. Here is an answer that explains how it is done.

The problem is although you are creating a new array the NSDictionary objects within the new array are still the same ones. So you need to make copies of the NSDictionary objects, you we're close but you need to do something like this...
-(void)saveWithArray:(NSMutableArray*)currentArray
{
dataToSave=[[NSMutableArray alloc] init]; //local
for(NSMutableDictionary *dic in currentArray)
[dataToSave addObject:[NSDictionary dictionaryWithDictionary:dic];
}

Related

Updating values on NSArray

I'v run into such problem. I need to update the values in my NSArray. And don't know a way to do it. Here's my array
NSArray *arrayWithInfo = [[NSArray alloc] initWithObjects:AMLocalizedString(#"Status", nil),AMLocalizedString(#"Call", nil),AMLocalizedString(#"Location", nil),AMLocalizedString(#"Control", nil),AMLocalizedString(#"Sim", nil),AMLocalizedString(#"Object", nil),AMLocalizedString(#"Info", nil),nil];
self.dataArray = arrayWithInfo;
[arrayWithInfo release];
To be more specific I have tableview initialized with this array. There is a possibility for user to use different localized strings, so I have to update it. By using [tableview reloadData]; i'v got the table to update, but the values in NSArray stay the same as they were initialized in first place.
So how to make array look up at the strings once again and get their new values?
Use NSMutableArray instead of NSArray
NSMutableArray (and all other classes with Mutable in the name) can be modified.
You should be using an NSMutableArray. Doing so will allow you to change its values after instantiation.
Your array doesn't need to be mutable here as the array seems to be all or nothing. You dont mention the requirement to delete some objects and not others. NSMutableArray isn't needed. You want to write a lazy loading getter method for the array which reinstantiates it if the array doesnt exist.
-(NSArray *)dataArray{
if (_dataArray){
return _dataArray;
}
_dataArray = NSArray *arrayWithInfo = [[NSArray alloc] initWithObjects:AMLocalizedString(#"Status", nil),AMLocalizedString(#"Call", nil),AMLocalizedString(#"Location", nil),AMLocalizedString(#"Control", nil),AMLocalizedString(#"Sim", nil),AMLocalizedString(#"Object", nil),AMLocalizedString(#"Info", nil),nil];
return _dataArray;
}
Then when you want to reload the tableView
self.dataArray = nil;
[tableView reloadData];
this destroys the old array, forcing it to be remade but with the new localisation.
EDIT:
The issue is the array isn't storing the statement AMLocalizedString(#"Status", nil) its storing the result of that statement, which is the localised string itself. There is no way to make the array re-evaluate that statement without either re-creating the whole array again or using an NSMutableArray and changing all the objects. The lazy loading getter method is more in the objective-c style.
You need to use the NSMutableArray. The NSArray is immutable.

NSKeyedArchiver cannot archive arrays

I am trying to develop a Cydia tweak, but now I have some problems.
I want to copy an NSMutableArray, so I use:
myArray = %orig;
NSMutableArray* myArray2;
myArray2 = myArray;
NSData* data = [NSKeyedArchiver archivedDataWithRootObject: myArray2];
(The original function returned an NSMutableArray, so I use myArray = %orig; to get it)
However, seems the archivedDataWithRootObject doesn't work, it always causes my device crash.
So, is there any limit for archivedDataWithRootObject ? I don't know whether the original NSMutableArray is encoded or not. Can I use archivedDataWithRootObject for a encoded NSMutableArra?
Thanks for any suggestions and replies
I don't understand this line of code in your code :
myArray = %orig;
And I can't see why you are doing this :
NSMutableArray* myArray2;
myArray2 = myArray;
If you got an NSArray or an NSMutableArray you will be able to encode it directly with the that line of code :
NSData* data = [NSKeyedArchiver archivedDataWithRootObject: myArray2];
BUT (there is always as but...)
What is store in your NSArray?
If you got custom object they must implement the NSCoding protocol to be able to participate in the Archiving dance.
AND finally you are saying :
I want to copy an NSMutableArray, so I use:
If you just want to copy the NSMutableArray you can do this :
[[NSMutableArray alloc] initWithArray:anArray];
I still don't get the % sign there.
If you need to add new object to it, why not just go simple, like create new object. If it's your own object you have 2 choice:
1st you implement the NSCopying protocol on those object an make them behave like you want, or
2nd choice : if the array contain one type of object you can create a Class method for those object that is like this : newObjectWithArray:(* NSArray)theArray and in that method you alloc init object that you initialize to the value of the receiving objet.

Can setValuesForKeysWithDictionary be used with a nested dictionary

So I have an NSDictionary where one of the keys is an array of dictionaries. The class I'm mapping to has matching key names and setters. Can setValuesForKeysWithDictionary fill the sub-dictionaries for me? When I tried it, it seemed like it filled the objects with pointer junk or something, but I'm a newbe, so maybe I'm doing something wrong. Does that function work like that?
I realized that there was no way for setValuesForKeysWithDictionary to know what kind of object to fill the NSMutableArray with. I ended up making a custom setter for the array property that manually loops the elements of the array (of NSDictionaries) you pass in and calls setValuesForKeysWithDictionary for each one.
Here's the code:
There is a property called itemList of type NSMutableArray that I want filled it objects of type Item. The setItemList setter loops through the array of mystery objects, converting each NSDictionary to my Item type and adds them to a new array. Any comments on how to simplify the code would be welcome.
I also want to add some logic here to handle a situation where the array already contains Item objects instead of dictionaries. In actionscript you can check for null after you try to cast something to see if it worked, not sure what the equivalent process would be here. [item isMemberOfClass [Item class]] always evaluates to YES, even if item is an NSDictionary. I can't understand why...
- (void) setItemList:(NSMutableArray*)input{
[itemList autorelease];
itemList = [[NSMutableArray alloc] initWithCapacity:input.count];
//loop through the array, creating an Item for for each object in the array
for(int i=0;i<input.count;i++){
Item* item = [Item new];
[item setValuesForKeysWithDictionary:(NSDictionary*)[input objectAtIndex:i]];
[itemList insertObject:item atIndex:i];
}
}
- (NSMutableArray*) itemList{
return itemList;
}

My NSMutableArray doesn't work

sorry for my stupid question (beginner)
I got the demo program Accelerometergraph the apple site and would like to use
NSMutableArray in the values of acceleration x.
but my NSMutableArray contains only one object, there being several passage
NSMutableArray routine and should contain the same number of objects that the counter
show, how code below
if(!isPaused)
{
array = [[NSMutableArray alloc] init];
[filter addAcceleration:acceleration];
[unfiltered addX:acceleration.x y:acceleration.y z:acceleration.z];
NSNumber *number = [[NSNumber alloc] initWithDouble:acceleration.x];
[array addObject:number];
++a;
if (a == 30) // only check the # objs of mutablearray
{
sleep(2);
}
[filtered addX:filter.x y:filter.y z:filter.z];
}
It looks like you're missing a loop of some kind. The code you list above:
Makes sure something isn't paused.
Creates a new (empty) mutable array.
Adds a value to the new array.
And does some other work.
My guess is that this whole if{} block sits inside some kind of loop. You need to alloc and init the mutable array outside of the loop instead.
You create a new array each time the if block is entered, so the addObject: will only add the object to the most recently created array.
Furthermore, you are leaking the array and number objects. Each time you allocate an object, you are responsible for releasing it. Make sure you're familiar with the guidelines set out in the memory management programming guide.

Table view not updating according to bindings

This is a very newbie question, and this is something I have done many times before, but there's something I'm missing this time.
In my AppDelegate.h file I declare an NSArray and set it as a property:
#interface AppDelegate : NSObject {
NSArray *lines;
}
#property(readwrite, retain) NSArray *lines;
#end
And then in the AppDelegate.m file in the awakeFromNib method I alloc it:
lines = [[NSArray alloc] init];
Then I have a method that sets the lines array:
NSString *fileContents = [NSString stringWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Desktop/sometextfile.txt"] encoding:NSUTF8StringEncoding error:NULL];
lines = [fileContents componentsSeparatedByString:#"\n"];
I have an array controller thats bound to AppDelegate.self.lines then I have a table column bound to Array Controller.arrangedObjects. I can confirm that the array is being updated (tested using NSLog) however the contents of the table are not being update (it remains blank).
Is there something obvious I'm missing here?
You don't need a data source if you're using Bindings. One or the other.
I have an array controller thats bound to "AppDelegate.self.lines" …
Why self?
#property(readwrite, retain) NSArray *lines;
No, use copy here. Otherwise, you'll find yourself retaining someone else's mutable array, which they will then mutate. Then “your” array will have changed without you knowing it.
Then I have a method that sets the lines array:
lines = [fileContents componentsSeparatedByString:#"\n"];
This is why the table doesn't show anything. You're not going through the property, you're accessing the instance variable directly. Direct instance variable accesses do not cause KVO notifications, so the array controller never finds out about the change.
Even worse, you're leaking the old array (since you simply assign over it without releasing it) and under-retaining this new array. Because you're not retaining the new array, that instance variable will hold a dead object shortly. The automatic retaining is done by the setLines: method, which only gets called when you call it.
You need to go through the property:
self.lines = [fileContents componentsSeparatedByString:#"\n"];
A property access is an implicit accessor message, so this both retains the array (or copies it, once you correct the #property as I suggested above) and posts KVO notifications.
When you say you have a arrangedObjects bound to the column do you mean you set the tablview datasource? If not you to set the tableview datasource to the lines array
You might want to read through this, it's got some good diagrams and explanations. What ennuikiller is saying is correct, I think it's a problem with your datasource. This is done by calling
[aTable setDelegate:aDelegate];