i'm a newbie of the world of objective-c.
what i'd like to know is how to check whether a mutable array object exists.
here is an exmaple.
if(![appDelegate.answerList objectAtIndex:3])
{
answer = [[NSMutableArray alloc] init];
}
else
{
answer = [[NSMutableArray alloc] initWithArray:[appDelegate.answerList objectAtIndex:3]];
}
above this code, 'answer' object is local, and 'answerList' object is on appDelegate class.
both are NSMutableArray objects.
i don't know whether answerList's third object is allocated or not.
If it already has an object, i wanna just copy an object from answerList's third object.
But that code doesn't work.
I'm not familiar with objective-c's methods.
please show me the way.
You can check if an array is empty by comparing it's count property to a number (in this case, 4, because arrays are 0 based). And because arrays do not accept objects that are nil and send a -retain message to all of their objects, you would technically only need to compare count instead of object existence. However, you can nest your current if...else... blocks in this as well for much more accurate (if redundant) results.
if([appDelegate.answerList count] <= 4) //object exists, and the array contains a valid index.
Related
What happens if I call [alloc] init] on an object which already was initialized and alloc'ed?
In my particular case I have an NSMutableArray which I initialize in superclass Parent using NSMutableArray* someArray = [NSMutableArray alloc] init];
In subclass Child I need to insert an object in someArray but at a specific index, for example 3.
So if the array has no items, or if it has less items than the index I'm trying to insert at (array has 4 items, and I want to insert at index 10) it will crash.
What would happen if I initialized someArray again in Child class? Would the pointer stored in someArray be replaced with the new one I'm initializing and the "old" one would just leak?
EDIT:
Sorry, my terminology was a bit off. I don't mean doing [someObject alloc], but doing someObject = [SomeClass alloc] init]; where someObject had previoulsy been initialized with an instance of SomeClass
Just for clarity when you say "What happens if I call [alloc] init] on an object..." your terminology is wrong.
The following line:
NSMutableArray* someArray = [[NSMutableArray alloc] init];
Reads in English:
"Send the alloc message to the NSMutableArray class object, then send the init message to the object returned from the first message, then store the object returned from init into the pointer variable named someArray."
I say that to emphasize the fact that you're not "calling alloc/init" on an existing object, you're making a new object, and storing a reference to this new object over the reference you had to the previous object. Since you no longer have a reference to that previous object, you've lost the ability to properly release its memory, so yes, you'll leak it.
correct, it will leak. Use NSMutableArray insertObject:atIndex:
There are a couple of ways that come to mind to do what I think you want. A sort of clumsy one is to put as many [NSNull null] objects into the array as you need so that it's filled up to the spot where you need to add the new object. Then you would replace an existing NSNull if you were storing your own object.
Probably a better approach is to use a dictionary instead of an array and turn your index value into a key.
I would like to use an array of pointers to instances of objects, but only want to create instances of those objects when required (i.e. lazily). The array corresponds to a table in the UI, so each array index corresponds to a table row.
I would like to use an NSMutableArray to hold pointers to the object instances as they are created (which occurs when the user selects the corresponding row in the UI).
If a row in the table is selected, the corresponding array entry is checked. If the pointer value is nil, the instance hasn't yet been created, and so it is created at that point, and the object pointer is stored in the corresponding indexed array entry.
Obviously, this requires that I initially start with an array of nil pointers, but objC won't let me put a nil pointer in an NSArray.
I can't just add objects to the array as they are created, as the array index would not correspond to the table row.
What's the best objC solution here?
The idiomatic solution in Objective C is to use NSNull:
The NSNull class defines a singleton object used to represent null values in collection objects (which don’t allow nil values).
Create your NSMutableArray, and fill it up with [NSNull null] objects:
NSMutableArray *array = [NSMutableArray arrayWithCapacity:N];
for (int i = 0 ; i != 10 ; [a addObject:[NSNull null]], i++);
When you check for the presence or absence of an object in your NSMutableArray, compare the object at index to [NSNull null]: if they are the same, replace with a real object; otherwise, the real object is already there.
if ([array objectAtIndex:index] == [NSNull null]) {
MyRealObject realObject = [[MyRealObject alloc] init];
[array replaceObjectAtIndex:index withObject:realObject];
}
** edit summary ** edited to initialize the array using a loop (thanks bbum).
NSMutableArray doesn't support sparse arrays. Thus, you could pre-seed the array with NSNull instances (or some other "no object" marker). Something like:
a = [NSMutableArray array];
for(int i = 0; i<numberNeeded; i++) [a addObject:[NSNull null]];
Or, if your array is going to be truly sparse, consider the use of some kind of map instead. NSMutableDictionary will work, but requires objects for keys and all that boxing/un-boxing is painful in some cases. Alternatively, a CFDictionary can easily be configured to use integer keys with object values.
While a dictionary is obviously slower for lookup-by-index, that performance difference is unlikely to cause a problem in most cases (but not all).
What about [NSMutableArray arrayWithCapacity:numberOfRows] ?
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html
I wrote a class, which acts as a filter. I pass three objects:
An NSArray, which holds objects to filter (these objects have a timestamp property)
An NSMutableArray (which will hold the section names for a tableView, the periods based on timestamps). I need this array, because I have to sort the periods.
An NSMutableDictionary, in which the keys will be the section names, the values are NSMutableArrays, which hold the items for a given period.
In the class from which I pass these objects, there is a tableView, in which I display the items.
This class has it own NSMutableArray and NSMutableDictionary, I not initialize them, only retain the corresponding return values of the filter class. In the delloc method I release them. There is a method in the filter class:
+ (void)insertItem:(id)item forPeriod:(NSString *)period toContainer:(NSMutableDictionary *)container {
if ( ![[container allKeys] containsObject:period] ) {
// the period isn't stored, create and store it
NSMutableArray *periodArray = [[NSMutableArray alloc] init];
[container setObject:periodArray forKey:period];
[periodArray release];
periodArray = nil;
}
// store the item
NSMutableArray *arrayForPeriod = [container objectForKey:period];
[arrayForPeriod addObject:item];
arrayForPeriod = nil;
}
The instruments shows me leak when I set the newly allocated array as an object of the dictionary. At this point this is definitely true, because the dictionary retains again the array, so after the release, it retain count remains 1. But I think in the caller class when I release the dictionary, the array will be released too. Am I wrong?
Yes it is considered as a leak because your var is a local variable. Then you still have an object in memory but no reference to it. Remember the init makes a retain + the retain made by the dictionary = 2 retains. Just create your array using
NSMutableArray *periodArray = [[[NSMutableArray alloc] init]
autorelease]
Is it clear ?
You could switch to ARC. Alternatively, check what the static analyser thinks of your code. It is pretty good at finding memory leaks, better than most humans.
Once you have a few hundred objects in your dictionary, you waste an awful lot of time and memory. A dictionary doesn't have an array of all keys stashed away somewhere, it has to create it every time you call your method. That's copying a few hundred pointers (cheap) and retaining them (expensive). containsObject for an array compares the object with every object in the array calling isEqual: That's expensive. It's an NSString compare each time. The array is autoreleased, and when it finally goes away, all the keys in it get released. Again expensive.
NSDictionary uses a hash table, so [objectForKey ] will immediately go to the right object. One operation instead of possibly hundreds.
Im having the following problem:
I've made a NSMutableArray "array" that is going to contain objects of a class named "Class". At the start that array should be empty and it must be filled during the program's execution.
As I never actually told the compiler that my NSMutableArray will be holding elements of the class Class, when I try to write the appropriate methods the compiler wont let me do it.
This is my first experience on Objective-C and iPhone development. I used to code in C/C++ where I declared my arrays in the following way:
Class array[NUMBEROFELEMENTS];
Is there any way to do this in Objective-C?
Thanks!
The truth is that is doesn't matter to the NSMutableArray what type of object it is. NSMutableArray simply stores pointers to all the objects they contain, or reference.
The trick is when you pull the object back out of the array you need to create a new pointer based on the appropriate type:
MyObject *myObject = [myArray objectAtIndex:0];
Then you can use the object however you like:
[myObject doThatThingWithThisValue:10];
Or whatever you need.
Arrays in Objective-C Cocoa are objects (as well as other collections, sets, dictionaries). Arrays can contain references to objects of any type, so the type for the array is simply NSArray, NSMutableArray, etc...
Since they are objects, you can send them messages to manipulate their content.
I suggest you take a look at Apple's excellent Collections Programming Topics, which explain the rudiments of collections.
Here is a quick example :
// two objects of different types
NSNumber *n = [NSNumber numberWithInteger:10];
NSString *s = #"foo";
// alloc/init a new mutable array
NSMutableArray *a = [NSMutableArray arrayWithCapacity:10];
// add an object
[a addObject:n];
[a addObject:s];
// array a now contains a NSNumber and a NSString
Well, you can still have C-style arrays in Objective-C.
However, the characteristics of Objective-C (some people will call it strength, other will call it weakness) is that it has dynamic typing of objects and dynamic dispatch.
It has NSArray and NSMutableArray which are not specialized for the certain class. It can store objects of non-compatible classes.
You can use the following idiom: [obj isMemberOfClass: [Class type]] to make sure an array element is of the desired type and then cast to Class*.
You can also use for-each loop (aka Fast Enumeration):
NSMutableArray* array = //... initialize your array
for (Class* elm in array) {
elm.your_property = 10;
}
I thought that NSArray/NSDictionary/NSSet and their mutable subclasses just added the pointer to the object, and not the object it self.
So if set my "simple" object to nil after I added it to the container, why isn't the reference nil also in the Array (container)?
Here is the code:
NSMutableArray *array = [[NSMutableArray alloc] init];
Simple *simple = [[Simple alloc] init];
[array addObject:simple];
//Array sends retain, lets release
[simple release], simple = nil;
NSLog(#"Simple = \"<Simple: %p>", simple);
NSLog(#"Array: %#", array);
[array release], array = nil;
Here is the output:
2011-02-16 20:00:03.149 Allocations[5433:207] Simple = <Simple: 0x0>
2011-02-16 20:00:03.150 Allocations[5433:207] Array: (
<Simple: 0x4d3d4e0>
)
NSArray adds a pointer to the object. In order to track changes to variable, the array would have to add a pointer to the variable itself (remember, you're just setting the variable to nil, not the object). There can be many variables all pointing to the same object, and reassigning them won't change any others.
Remember: Pointers aren't magic. They're just ordinary variables whose value is a memory address — in this case, the memory address of an object. Two pointers to the same object aren't "linked" any more than two ints with the value 5. Changing the pointer doesn't affect the object; in order to affect the object, you have to either send it a message that causes it to change (e.g. [object setValue:6]) or dereference the pointer to access the object's members directly (e.g. object->value = 6).
PS: Don't access an object's members directly. It's bad and fragile and very prone to bugs. I just mentioned it here to explain how pointers work.
Setting simple = nil just makes that pointer point to nothing. It doesn't delete the object that the array still has a pointer to. At the point of your NSLog statements, the retainCount of the Simple instance that simple pointed to would be one.
Create simple
simple => (Simple instance: retain count 1)
Add to array
simple => (Simple instance: retain count 2)
[array objectAtIndex:0] => (Simple instance: retain count 2)
Release simple
simple => (Simple instance: retain count 1)
[array objectAtIndex:0] => (Simple instance: retain count 1)
Set simple = nil
simple => nil
[array objectAtIndex:0] => (Simple instance: retain count 1)
Release array
(Simple instance: retain count 0, subsequently destroyed)
NSArray does contain only a pointer to the object that is added, but that's ok -- it's not pointing to the simple pointer itself, but rather to the Simple object that simple pointed to. Thus in your example, after you change what simple points to, the array is still pointing at the original Simple object.