Difference between the following allocations types? - iphone

I have a simple code:
NSMutableArray *arrayCheckList = [[NSMutableArray alloc] init];
[arrayCheckList addObject:[NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"2011-03-14 10:25:59 +0000",#"Exercise at least 30mins/day",#"1",nil] forKeys:[NSArray arrayWithObjects:#"date",#"checkListData",#"status",nil]] ];
[arrayCheckList addObject:[NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"2011-03-14 10:25:59 +0000",#"Take regular insulin shots",#"1",nil] forKeys:[NSArray arrayWithObjects:#"date",#"checkListData",#"status",nil]]];
Now I want to add a specific index of above array to a dictionary. Below are two way, which one is better and why? What are the specific drawbacks of the latter?
NSDictionary *tempDict = [[NSDictionary alloc] initWithDictionary:[arrayCheckList objectAtIndex:1]];
OR
NSDictionary *tempDict = [arrayCheckList objectAtIndex:1];
What would the impact on the latter since I am not doing any alloc/init in it?

1:
NSDictionary *tempDict = [[NSDictionary alloc] initWithDictionary:[arrayCheckList objectAtIndex:1]];
Creates a new immutable dictionary object as a copy of the original one. If you add objects to the mutable dictionary in your arrayCheckList they will not be added to your copied reference.
2:
NSDictionary *tempDict = [arrayCheckList objectAtIndex:1];
This directly pulls the mutable dictionary from your array and not a copy. The following two lines will be equivalent:
[[arrayCheckList objectAtIndex:1] addObject:something];
[tempDict addObject:something];

The first one potentially copies the dictionary a index 1 of the array. (It should, since you're creating an immutable dictionary but the one in the array is mutable.) The second only gets a reference to the dictionary in the array -- there's no chance of creating a new object.

Related

Setting flags for elements in NSMutableArray

I have an NSMutableArray of elements and I want to be able to conditionally set custom flags for some of the elements. For example an error count for certain elements if they return an error. If the count is more than 3, I would like to delete this element from an array.
What would be the best way to implement such behaviour?
A few options:
Have a separate array holding your counter for each object. When deleting one from your original array, remember to delete it's corresponding counter object.
Create a small class that contains an int value and whatever other object you are storing in the array, and populate your NSMutableArray with that object. You will then have your object and the error counter on the same place
Edit: The second option is the most scalable one, if you ever want to add more flags or whatever to it.
You would be better off creating a mutable array filled with mutable dictionaries. This would allow you have two keys corresponding to each index in the array:
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"some text, or what ever you want to store",#"body",
[NSNumber numberWithUnsignedInteger:0],#"errorCount",
nil];
[myMutableArray addObject:mutableDictionary];
And then here is a basic example of how to increment the error count for a specific item in the array:
- (void)errorInArray:(NSUInteger)idx
{
if ([[[myMutableArray objectAtIndex:idx] objectForKey:#"errorCount"] unsignedIntegerValue] == 2) {
[myMutableArray removeObjectAtIndex:idx];
}else{
NSUInteger temp = [[[myMutableArray objectAtIndex:idx] objectForKey:#"errorCount"] unsignedIntegerValue];
temp ++;
[[myMutableArray objectAtIndex:idx] setObject:[NSNumber numberWithUnsignedInteger:temp] forKey:#"errorCount"];
}
}
As alluded above, no need for custom object creation necessarily:
Creating a mutable array, creating a dictionary with objects/keys and adding said dictionary to the array:
NSMutableArray *myArray = [[NSMutableArray alloc] init] autorelease];
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"John Doe", #"elementName",
[NSNumber numberWithInt:0], #"errorCount",
nil];
[myArray addObject:myDictionary];

How to use setValue:forKey: function with NSArray

I'm try to use the functions -setValue:forKey: and get the value using -valueForKey:
Example:
NSArray *rootarr = [[NSArray alloc] init];
NSArray *array = [[NSArray alloc] initWithObjects:#"name", #"address", #"title", nil];
[rootarr setValue:array forKey:#"n"];
NSArray *getArr = [rootarr valueForKey:#"n"];
But the getArr array I got is not equal the array I set (array).
Could you please tell me what's wrong I met. And what's the way to use these functions?
NSArray's setValue:forKey: method is used for Key Value Coding, not for using an array as an associative container. You need to use NSMutableDictionary.
NSMutableDictionary *rootDict = [NSMutableDictionary dictionary];
NSArray *array = [[NSArray alloc] initWithObjects:#"name", #"address", #"title", nil];
[rootDict setValue:array forKey:#"n"];
NSArray *getArr = [rootDict valueForKey:#"n"];
An array isn't a key-value store, which you appear to want to use it as. I think you want an NSDictionary instead (or more precisely NSMutableDictionary if you want to modify it after its created).
According to the Apple documentation setValue:forKey:
Invokes setValue:forKey: on each of the array's items using the specified value and key.
Practical uses are when you want to set the same value to each element of the array
UILabel *label1 = [[UILabel alloc]init];
UILabel *label2 = [[UILabel alloc]init];
NSArray *arr = #[label1, label2];
[arr setValue:#"bye" forKey:#"text"];
NSLog(#"%# %#",label1.text, label2.text); // bye bye
in your example "getArr" is an empty array because your "rootarr" doesn't have elements, otherwise you receive a setValue:forUndefinedKey: into the contained objects that are not compliant for the assigned key
You can't add objects to an NSArray after it is created. You have to use NSMutableArray in order to do that.

iPhone: How to access NSMutableArray two dimensional setting?

I am using insertObject for NSMutableArray for storing values into two dimensional array like below for example.
[mutableArrayPtr insertObject:[NSMutableArray arrayWithObjects:firstData, secondData,nil] atIndex:index];
I think, it is correct way of storing values in two dimensional array in Obj C.
I want to access it at later point of time. How can i do that?
Thanks!
The way you are doing it is perfectly fine. NSArray's don't have native two dimensional syntax like primitive arrays (int[][], double[][], etc.) in C do. So instead, you must nest them using an array of arrays. Here's an example of how to do that:
NSString *hello = #"Hello World";
NSMutableArray *insideArray = [[NSMutableArray alloc] initWithObjects:hello,nil];
NSMutableArray *outsideArray = [[NSMutableArray alloc] init];
[outsideArray addObject:insideArray];
// Then access it by:
NSString *retrieveString = [[outsideArray objectAtIndex:0] objectAtIndex:0];
To access your array at a later point in time, you would do something like:
NSArray* innerArray = [mutableArrayPtr objectAtIndex:0];
NSObject* someObject = [innerArray objectAtIndex:0];
Of course, change 0 to whatever index you actually need to retrieve.
EDIT:
Q: Is it "initWithObjects" (or) "insertObject:[NSMutableArray arrayWithObjects:imagePtrData,urlInStr,nil]" for two dimension?
A: initWithObjects and insertObject would both allow you to create two dimensional arrays. For example you could do:
NSMutableArray* arrayOne = [[NSMutableArray alloc] initWithObjects:[NSArray initWithObjects:#"One", #"Two", #"Three", nil], [NSArray initWithObjects:[#"Four", #"Five", #"Six", nil], nil];
// NOTE: You need to release the above array somewhere in your code to prevent a memory leak.
or:
NSMutableArray* arrayTwo = [NSMutableArray array];
[arrayTwo insertObject:[NSArray arrayWithObjects:#"One", #"Two", #"Three", nil] atIndex:0];

Creating a Two-Dimensional Array with Concrete Positions

I need to create a custom array:
In php I would define as follows:
$myarray[100][80] = 1;
But I don't know how to do it in objective-c...
I don't need an array [0][0],[0][1],[0][2], ... I only need concrete positions in this array [80][12], [147][444], [46][9823746],...
The content of these positions always will be = 1;
for this you would use a dictionary rather than an array as they are always 0,1,2 keyed so something along the lines of:
NSNumber *one = [NSNumber numberWithInt:1];
NSString *key = #"80,12";
NSDictionary *items = [NSDictionary dictionaryWithObject:one forKey:key];
Then to pull them out again you would use the objectForKey: method.
You cannot put ints directly into arrays or dictionaries that's why it is wrapped in the NSNumber object. To access the int after getting the NSNumber out of the dictionary you would use something like:
NSNumber tempNum = [items objectForKey:key];
int i = tempNum.intValue;
See the docs here for a full explanation of the NSDictionary class. Hope this helps...
I an not a PHP master but I believe in php arrays are not real arrays they are hash tables right?
Anyway, I think you are looking for NSDictionary or NSMutableDictionary class.
That looks more like a bitset than an array.
Allocating so many cells for that seems useless, so maybe you could revert the problem, and store the positions in an array.
Well in objective c we can use NSMutableArray to define 2-D arrays.
See the following code, it might help you
NSMutableArray *row = [[NSMutableArray alloc] initWithObjects:#"1", #"2", nil];
NSMutableArray *col = [[NSMutableArray alloc] init];
[col addObject:row];
NSString *obj = [[col objectAtIndex:0] objectAtIndex:0];
NSLog(#"%#", obj);

Create a dictionary property list programmatically

I want to programatically create a dictionary which feeds data to my UITableView but I'm having a hard time with it. I want to create a dictionary that resembles this property list (image) give or take a couple of items.
I've looked at "Property List Programming Guide: Creating Property Lists Programmatically" and I came up with a small sample of my own:
//keys
NSArray *Childs = [NSArray arrayWithObjects:#"testerbet", nil];
NSArray *Children = [NSArray arrayWithObjects:#"Children", nil];
NSArray *Keys = [NSArray arrayWithObjects:#"Rows", nil];
NSArray *Title = [NSArray arrayWithObjects:#"Title", nil];
//strings
NSString *Titles = #"mmm training";
//dictionary
NSDictionary *item1 = [NSDictionary dictionaryWithObject:Childs, Titles forKey:Children , Title];
NSDictionary *item2 = [NSDictionary dictionaryWithObject:Childs, Titles forKey:Children , Title];
NSDictionary *item3 = [NSDictionary dictionaryWithObject:Childs, Titles forKey:Children , Title];
NSArray *Rows = [NSArray arrayWithObjects: item1, item2, item3, nil];
NSDictionary *Root = [NSDictionary dictionaryWithObject:Rows forKey:Keys];
// NSDictionary *tempDict = [[NSDictionary alloc] //initWithContentsOfFile:DataPath];
NSDictionary *tempDict = [[NSDictionary alloc] initWithDictionary: Root];
I'm trying to use this data of hierachy for my table views.
So I was wondering how can I can create my property list (dictionary) programmatically so that I can fill it with my own arrays.
I'm still new with iPhone development so bear with me. ;)
This is a situation where "teach a man to fish" is a vastly more helpful approach than "give a man a fish". Once you understand the basic principles and the NSDictionary API, it becomes much easier to craft your own custom solution. Here are a few observations and learning points:
The method +dictionaryWithObject:forKey: is used to create an NSDictionary with a single key-value pair. It will not accept comma-separated arguments after each colon (:) in the method call, just one. To create a dictionary with multiple key-value pairs, use one of 2 related methods: +dictionaryWithObjects:forKeys: which accepts two NSArray objects containing values and keys, or +dictionaryWithObjectsAndKeys: which alternates (object, key, object, key) with a terminating nil argument.
Simplify the creation code. You don't need a million local variables just to create the thing. Use inline arguments where it makes sense. One way to do this it to build up your dictionary in code as an NSMutableDictionary, then (if necessary) make an immutable copy of it by calling -copy on it. (Remember that a copy method returns a new object that you must release to avoid memory leaks.) That way you don't have to have a variable for every single value so you can do a "one shot" creation of the structure(s) at each level.
Use +arrayWithObject: for creating an NSArray with a single object.
(Style suggestions) Never use an uppercase letter to begin the name of a variable, method, or function. (Notice that SO highlights leading-caps variables like class names.) It will certainly help others who read your code from being confused about your intent by your naming scheme.
Just to give you a flavor of what creating the dictionary in the linked image might look like in code... (I'm ignoring your chosen variable names and using both different approaches for completeness.)
NSDictionary *item1 = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"Screen J",[NSNumber numberWithInt:3],nil]
forKeys:[NSArray arrayWithObjects:#"Title",#"View",nil]];
NSDictionary *item2 = [NSDictionary dictionaryWithObjectsAndKeys:
#"Screen I", #"Title",
[NSArray arrayWithObject:item1], #"Children",
nil];
...
I am not sure I understand the basic objective here.
It seems like at runtime, you are constructing a deep dictionary with many child nodes. But you are constructing this all with static code... why can you not simply make a plist (like the one you had an image of) and read that into an NSDictionary? Both NSDictionary and NSArray have methods that let you simply read in a file and get a whole filled out object. Then it is WAY easier to edit and to understand. That method is dictionaryWithContentsOfFile.
If all of the data is truly created at runtime before it is put into the dictionary, then it seems like you would want a very different, recursive, style of code rather than the flat examples given.
Lastly, I personally dislike the dictionaryWithObjects:forKeys: method in NSDictionary for building a dictionary. If you have to do things that way I greatly prefer the alternate method dictionaryWithObjectsAndKeys: which does the same thing but keeps the keys with the objects:
NSDictionary *item1 = [NSDictionary dictionaryWithObjectsAndKeys:
#"Screen J",
#"Title",
[NSNumber numberWithInt:3],
#"View"];
NSMutableDictionary *topLevel = [NSMutableDictionary dictionary];
NSMutableDictionary *item1 = [NSMutableDictionary dictionary];
NSString *item1title = [NSString stringWithString:#"Title 1"];
NSMutableDictionary *item1children = [NSMutableDictionary dictionary];
// create children
NSString *item1child1 = [NSString stringWithString:#"item 1, child 1"];
NSMutableDictionary *item1child2 = [NSMutableDictionary dictionary];
NSString *item1child2title = [NSString stringWithString:#"Title 1-2"];
NSMutableDictionary *item1child2children = [NSMutableDictionary dictionary];
NSString *item1child2child1 = [NSString stringWithString:#"item 1, child 2, child 1"];
NSString *item1child2child2 = [NSString stringWithString:#"item 1, child 2, child 2"];
[item1child2 setObject:item1child2title forKey:#"Title"];
[item1child2children setObject:item1child2child1 forKey:#"item 1 child2 child 1"];
[item1child2children setObject:item1child2child2 forKey:#"item 1 child2 child 2"];
[item1child2 setObject:item1child2children forKey:#"children"];
// add children to dictionary
[item1children setObject:item1child1 forKey:#"item1 child1"];
[item1children setObject:item1child2 forKey:#"item1 child2"];
// add to item 1 dict
[item1 setObject:item1title forKey:#"Title"];
[item1 setObject:item1children forKey:#"children"];
NSMutableDictionary *item2 = [NSMutableDictionary dictionary];
NSString *item2title = [NSString stringWithString:#"Title"];
NSMutableDictionary *item2children = [NSMutableDictionary dictionary];
NSString *item2child1 = [NSString stringWithString:#"item 2, child 1"];
NSString *item2child2 = [NSString stringWithString:#"item 2, child 2"];
NSString *item2child3 = [NSString stringWithString:#"item 2, child 3"];
// add children to dictionary
[item2children setObject:item2child1 forKey:#"item2 child1"];
[item2children setObject:item2child2 forKey:#"item2 child2"];
[item2children setObject:item2child3 forKey:#"item2 child3"];
// add to item 2 dict
[item2 setObject:item2title forKey:#"Title"];
[item2 setObject:item2children forKey:#"children"];
[topLevel setObject:item1 forKey:#"Item 1"];
[topLevel setObject:item2 forKey:#"Item 2"];
first of .. super! thanks .. I really appreciate the explanation and code snippet.
Since you gave me such a good explanation I hope you don't mind me asking a couple more questions.
First, I did as you suggested and this is what I came up with :
(I used my original property list instead of the example this time so this is where the drilldown table gets his( or needs to get his ) treestructure).
http://img509.imageshack.us/img509/7523/picture2lsg.png
NSDictionary *root = [NSMutableDictionary dictionary];
NSDictionary *item1 = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:#"VirtuaGym Online"] forKey:[NSArray arrayWithObjects:#"Title"]];
NSDictionary *item2 = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:#"Do the training"] forKey:[NSArray arrayWithObjects:#"Title"]];
NSDictionary *item3 = ...
[root setObject:item1 forKey:#"Item 1"];
[root setObject:item2 forKey:#"Item 2"];
Also did some research and tried something else with some other input..
NSMutableArray *Rows = [NSMutableArray arrayWithCapacity: 1];
for (int i = 0; i < 4; ++i) {
NSMutableArray *theChildren = [NSMutableArray arrayWithCapacity: 1];
[theChildren addObject: [NSString stringWithFormat: #"tester %d", i]];
NSString *aTitle = [NSString stringWithFormat: #"Item %d", i];
NSDictionary *anItem = [NSDictionary dictionaryWithObjectsAndKeys: aTitle, #"Title", theChildren, #"Children"];
[Rows addObject: anItem];
}
NSDictionary *Root = [NSDictionary withObject: Rows andKey: #"Rows"];
I decided to just test both of these however it does what I want. It gives me a EXC_BAD_ACCESS error.
I was also wondering since I saw you using number in your code snippet, couldn't you also use NSString since that's what the plist uses.. could be totally of here of course
NSDictionary *item1 = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"Screen J",[NSNumber numberWithInt:3],nil]
forKeys:[NSArray arrayWithObjects:#"Title",#"View",nil]];
and third a question is about my possible approach to my app.
I have an xml parser which saves certain information in different arrays.
I want to use this information for my drilldown UITableviews (infoFirstScreen[] infoSecondScreen[] infoThirdScreen[]).
The information provided has to be connected like the tree I showed you above.
This is the reason I wanted to build the dictionary in code so I can take the info from my arrays and insert it here.
My question do you think my approach is correct, wrong or is there a faster way?
again really appreciate the explanation ;)