NSDictionary of uninitialized objects? - objective-c

How can I store an uninitialized object in an NSDictionary?
I think I would do it like this, but I’m not certain that it’s a good approach:
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
[MyObject1 alloc], #"myObject1",
[MyObject2 alloc], #"myObject2"
, nil];
MyObject1 *object = [dict objectForKey:#"myObject1"];
[object init];
Any help would be appreciated.

What you need is to store mutable objects inside the dictionary. Doing this you will be able to modify them after the insertion, because an immutable dictionary doesn't allow to insert a new object.
If for "uninitialized" you mean that the object has only been created with alloc, without init, that's deprecable because init may return a different object from the one returned with alloc. So just store them like you're doing it, and when you need to modify them call the accessors:
NSDictionary *dict = #{ #"myObject1" : [MyObject1 new] , #"myObject2" : [MyObject2 new] };
dict[#"myObject1"].someProperty= someValue;
If your MyObject1 class is immutable, then you have to use a mutable dictionary.

Related

Is a NSMutableDictionary in a NSDictionary still mutable?

The question is as simple as the title:
Is a NSMutableDictionary in a NSDictionary still mutable? Is the mdict mutable below?
NSMutableDictionary *mdict = [[NSMutableDictionary alloc] init];
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:mdict, #"key", nil];
And, is a NSDictionary in a NSMutableDictionary still immutable?
Further, what if it's array/set instead of dictionary?
Absolutely! Mutability of an object does not change when you place it into a container.
When you place a mutable dictionary into another collection, mutable or immutable, that collection adds a reference to the mutable dictionary object, but it does not change it in any other way. Same goes for placing immutable objects into collections: collections reference these objects without changing their nature.
This remains true while your object is in memory. If you serialize it and then deserialize it back, the process of deserialization may remove mutability. For example, if you save NSMutableDictionary into NSUserDefaults and then read it back, you would get back an immutable dictionary.
Yes. Objects generally don't know when they're placed into a collection, so they can't change their behavior based on that. NSDictionary does copy its keys (precisely so you can change the original object without affecting the dictionary), but it just stores a normal reference to the value.
As long as you access your variables like so
NSMutableDictionary * tempDict = [mdict objectForKey: #"Key"];
NSMutableDictionary * tempDict2 = [arrayVar objectAtIndex: index];
The temp variables retain all the functionality as before

Copying a dictionary changes NSMutableArray into NSArray?

So I have this NSMutableDictionary object:
pdata=[[NSMutableDictionary alloc] initWithObjectsAndKeys:
#"",#"pid",
#"",#"pname",
[[NSMutableArray alloc] initWithCapacity:1],#"ilist",
nil];
And then I copy this object into another object like this:
NSMutableDictionary *pdataCopy=[[NSMutableDictionary alloc] initWithDictionary:pdata copyItems:TRUE];
But once Ive done this, pdataCopy.ilist is now an NSArray instead of NSMutableArray.
How can I copy a dictionary object whilst maintaining the mutability of the properies inside it?
Actually you can't. You can get a mutable array by
NSMutableArray *mutableArray = [pdataCopy.ilist mutableCopy]
You have three options:
Don't specify copyItems:YES
Scan through the dictionary after copying and replace NSArrays with NSMutableArrays (using mutableCopy) as desired.
Create your own subclass of NSMutableArray that responds to copyWithZone by producing a mutable copy of itself (and use objects of that class in your dictionary).

NSArray unwanted deallocation

I am new to collection programming , so i faced this problem . Any help will be thanked ...
I Initialized a NSArray instance in a class initialize ( i already define that as a property of that class in header file so i a must can access it any where in that class implementation ) and assign some object to it with
+(NSArray*) arrayWithObjects: ; . every thing in init method is fine but ,...
When i want to work with that array in update method app crashs , because that array DEALLOCATED .
Why that array became deallocated ? How can i prevent it ?
Thanks .
If you are not using ARC, then you should init it with initWithObjects, then retain it.
If you are using ARC, probably it's just a weak reference.Make it be a strong reference when you declare the property.
if you are initializing array like this,
objects = [NSArray arrayWithObjects: buttonOne, buttonTwo,
textField, nil];
its a conventional method, It just auto-release the object after one time the object access. Rather you can retain the object or you can allocate and initialize it like
objects = [NSArray alloc] initWithObjects: buttonOne, buttonTwo,
textField, nil];
You should alloc NSArray and use -initWithObjects method instead.
NSArray* tempArray = [[NSArray alloc] initWithObjects:(id), ..., nil]

Objective-C identifier from NSString

I want to pass an NSString to a method and have that particular NSString name a new NSSMutableArray. Confusing? Programmatically looks like this:
+ (void)newMutableArrayWithName:(NSString*)theArrayName
{
NSLog(#"Creating an array that is named: %#",theArrayName);
NSMutableArray* theArrayName = [[NSMutableArray alloc] init];
}
Unfortunately, "theArrayName" is not affiliated with the argument passed to the method. Is there any way this is achievable?
The name of a variable is used by the compiler, and is set at compile-time, not at run time.
If you need to be able to associate a label with an array, I suggest that you use an NSDictionary to do something like this
NSString *theArrayName = #"My Cool Array";
NSMutableArray *theArray = [NSMutableArray array];
NSDictionary *theDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
theArray, theArrayName, nil];
You could have multiple "named" arrays in the dictionary, if you wanted, and could access them by the names that you gave them
[theDictionary objectForKey:#"My Cool Array"];
Look into key-value coding for setting the values of existing properties by the property's name, but it appears it can't create a new property. For that, you should just use a dictionary.

Assigning values to Instance variables in Objective C

The function I'm looking at:
-(void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"statedictionary" ofType:#"plist"];
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.statesZips = dictionary;
[dictionary release];
NSArray *components = [self.stateZips allKeys];
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
self.States = sorted;
NSString *selectedState = [self.states objectAtIndex:0];
NSArray *array = [stateZips objectForKey: selectedState];
self.zips = array;
}
Why is an NSDictionary allocated, then assigned to a pointer called *dictionary, and then assigned to the instance variable stateZips? Why not allocate it and assign it directly to the instance variable and save memory of creating and releasing another NSDictionary? The same methodology is always followed, including later in this function with the NSArray...
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.statesZips = dictionary;
[dictionary release];
Also, this sorting puts the keys from a hash table (dictionary) in alphabetical order. I'm not sure I understand this line:
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
No one seems to have addressed the fact that the line
self.statesZips = dictionary;
is not directly an instance variable assignment. stateZips is a property, and so that line of code calls the setStateZips: method. That method retains or copies the dictionary, so unless the viewDidLoad method intends to use it again for some purpose, it's not needed any longer. That makes it OK to release it.
The previous line:
[[NSDictionary alloc] initWithContentsOfFile:plistPath];
allocates an object. That makes it your responsibility to release it once you don't need it any more. After assigning it to the statesZips property, it's no longer needed, so it's released and you shouldn't use dictionary any more. You'll notice that later code only refers to self.stateZips, not dictionary.
In the case of the NSArray later in the method, viewDidLoad does not allocate the object, so that method is not responsible for calling release on it. The rule of thumb is that if you alloc it, you're responsible for making sure it gets released. Otherwise, it's not your problem.
Sorting the array uses the sortedArrayUsingSelector: method. A selector identifies a method in Objective-C. And the #selector is the literal syntax for selectors (kind of like how #"" is the literal syntax for NSString objects). So, what that code says, is "give me an array where the objects in components are sorted, and use the compare: method to compare each object when you do the sort. When it sorts the array, it will call compare: on the objects in the array to determine how to put them in order.
The statesZips property is probably retained, that's the reasoning.
When the NSDictionary is first allocated, its retain count is 1. When it's assigned to statesZips, the retain count becomes 2. When it's released, the retain count drops to 1, which is usually the desired outcome.
Note that the code below would have produced (almost) the same result:
self.statesZips = [NSDictionary dictionaryWithContentsOfFile:plistPath];
because dictionaryWithContentsOfFile returns an autoreleased object.
As a convention, class methods like [NSDictionary dictionary] return autoreleased objects (which automatically get released after some time), while the usual alloc-init method (as in [[NSDictionary alloc] init]) return retained objects.
I suggest you read the Memory Management Programming Guide for Cocoa for further information.
EDIT: I must have missed the last part of your question when I first read it, but Barry has already answered that part.
This code uses reference-counted memory management (not the automatic garbage collection memory management available in Objective-C 2.0 on OS X). When any object (in this case, the NSDictionary and the NSArray) are alloc'd, the caller is responsible for calling -release on that instance. Failing to call release causes a memory leak. The code could have been written as
self.statesZips = [[[NSDictionary alloc] initWithContentsOfFile:plistPath] autorelease];
but at the expense of less explicit memory management (relying on NSAutoreleasePool to release the alloc'd instance at the end of the event loop iteration.
the call
[components sortedArrayUsingSelector:#selector(compare:)];
returns an array of whose elements come from components but according to the return value of calling [elem1 compare:elem2] to compare two array elements.