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
Related
Hey there I'm new to objective c is there any way to grab and objects properties once it's in an NSMutable array.
like this:
so in c++ it would look like this
for (int i = 0; i < length; i++)
{
someArray[i].someClass.somePropertyInTheClass;
}
is there any possible way to do that with an NSMutableArray in OBJECTIVE -C
Thanks in advance all you pro objective c-ers!
There are two main ways to loop through an array in Objective-C. First, using an index:
NSInteger count = [array count];
for (int i; i < count; i++)
{
id object = [array objectAtIndex:i];
NSLog(#"%#", [object somePropertyInTheClass]);
}
Note that the the count is stored in a variable rather than in the for loop. This avoids having to recalculate the count each time through the loop.
A more concise method is with fast enumeration:
for (id object in array)
{
NSLog(#"%#", [object somePropertyInTheClass]);
}
Both versions are semantically equivalient, they both log each object's somePropertyInTheClass property in the array (assuming each object has a somePropertyInTheClass property). In both versions, id can, and should, be replaced with a specific class, eg NSDictionary * based on what you store in the array.
While fast enumeration is more concise, the approach with the counter is useful if you need to use the index for something other than accessing one array. Also, fast enumeration does not allow you to modify the array, even in another method, while looping. In most cases though, when iterating through an array, fast enumeration can be used.
Hey there I'm new to objective c is there any way to grab and objects
properties once it's in an NSMutable array.
The mere fact of being included in an array or other container doesn't prevent you from accessing the properties of an object. If the object you want is at index i, you can get the object simply:
Person *person = [arrayOfPeople objectAtIndex:i];
or even simpler:
Person *person = arrayOfPeople[i];
You can then of course get the property like this:
NSString *name = [person valueForKey:#"name"];
or just:
NSString *name = person.name;
However, your example shows a loop, suggesting that you might want to get the same property of all the objects in an array. Objective-C containers have the very nice property that they respond to -valueForKey: by getting the value for the given key from each contained object and returning a container with just those values. So, you can say:
NSArray *names = [arrayOfPeople valueForKey:#"name"];
and you get an array of names in names, with one name for each object in the array arrayOfPeople.
You can, of course, also iterate over the array:
for (Person *person in arrayOfPeople) {
NSString *name = person.name;
NSLog(#"The name is %#", name);
}
Finally, you should know that mutable arrays work just the same way as non-mutable arrays when it comes to accessing objects. The only difference between a mutable array and a non-mutable array is that you can add or remove objects from a mutable array after you create it. The mutability of the objects contained in the array isn't affected by the array's own mutability. That is, if you have an array arrayOfPeople, and if the Person objects stored in it can be modified, then you're free to change the name, age, etc. of any object stored in the array regardless of whether arrayOfPeople is mutable or non-mutable.
How is NSMutableArray implemented?
If there are a number of NSMutableArray references to a mutable array e.g. Source Array and each of these pointing arrays sort the objects differently, are the objects copied over into each array or are the object pointers just internally rearranged?
If the source array is updated with new objects, will the NSMutableArray pointers automatically add an the entry to their internal reference.
It just stores pointers.This means that if the NSMutableArray stores immutable objects, then you are sure that they'll not change their state, but if it has a mutable object the state of any referenced object may change at any time.
Exactly I don't know how it's implemented, but I would say a pointer to id.It allocates memory and then update the id at the index that needs:
id* pointers;
< Allocation>
pointers[i]= newObjectPointer;
It's a little hard to parse exactly the scenario you're asking about, but NS(Mutable)Array holds a list of object references. It does not copy the objects you add to it, just keeps a pointer. If those objects can change, and you change them from outside the array, that will be reflected in the things you read back from the array.
NSMutableArray * arr = [NSMutableArray array];
NSMutableArray * child = [NSMutableArray array];
[child addObject:#5];
[arr addObject:child];
[child addObject:#6];
NSLog(#"%#", arr[0]);
> [5, 6]
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.
I have a NSMUtableArray which has elements, for example:
a,b,c,e
And I want to add an object d to behind c and before e.
In other words, I'd like to insert an object to a sorted array.(The object can be a custom object, too)
I'd like to know : besides using for to find the position, is there any other method to implement it? It is better to use the iOS api.
Thanks.
You can use -[NSArray indexOfObject:inSortedRange:options:usingComparator:] to ask an NSArray for the index where an object should be inserted given an array range that’s currently sorted.
For example, assuming the entire array is sorted::
NSMutableArray *array = …;
id newObject = …;
NSComparator comparator = …;
NSUInteger newIndex = [array indexOfObject:newObject
inSortedRange:(NSRange){0, [array count]}
options:NSBinarySearchingInsertionIndex
usingComparator:comparator];
[array insertObject:newObject atIndex:newIndex];
Since this method uses binary search, it is more efficient than iterating over all elements in the array.
The comparator is a block object that receives two objects of type id and returns an NSComparisonResult value.
To inject element to known index (position) use
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html
And to find position of object previously placed into NSMutableArray use
- (int)indexOfObject:(id)anObject
NSMutableArray - Get Arrays Index Integer By Searching With A String
Section Finding Objects in an Array
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html
I'd just add the new object at either end and sort the array again. If the array you're adding to is already sorted, the re-sort that moves one object is going to be about as quick as anything you'd implement yourself.
NSMutableArray *things; // populated
id newObject;
...
[things addObject:newObject atIndex:0];
[things sortUsingSelector:#selector(compare:)];
Ok a pretty simple question.. in c++ it seems to work but in objective-c i seem to struggle with it :S ..
If you want to compare two arrays it should be something like this right
for ( int i = 0; i < [appdelegate.nicearray count]; i++ )
{
if ( appdelegate.nicearray[i] == appdelegate.exercarray[i] )
{
NSLog(#"the same elements in this selection");
}
}
what's the problem exactly ?
These are Cocoa array objects (instances of NSArray), not C arrays or C++ vectors, and remember that Objective-C does not have operator overloading. The only things you can do with an object are pass it around, store it in variables, and send messages to it.
So the array-subscript operator is wrong with Objective-C objects. I don't think it's even linguistically valid to dereference a pointer to an Objective-C object, so this code should be giving you a compiler error. I may be misremembering, though. If it does make it to runtime, that code will crash sooner or later, since you're accessing memory beyond the ends of the array objects.
(EDIT from the year 2013: Objective-C now supports subscripting of objects. This ultimately translates into the appropriate objectAtIndex: or replaceObjectAtIndex:withObject: message. So, the code in the question would actually work now, although it's still not the proper way to simply walk an array, much less to compare two arrays.)
The proper way to retrieve an object from an NSArray object by its index is not to use the array-subscript operator, but to send the array object the objectAtIndex: message:
[myArray objectAtIndex:i]
The proper way to iterate on the elements of an array object, assuming you don't really need the index for something else (such as replacing objects in a mutable array), is to loop on it directly (this is called “fast enumeration”):
for (MyObject *myObject in myArray) {
…
}
NSArray also responds to objectEnumerator and reverseObjectEnumerator, which return a similarly-iterable object. Of the two, reverseObjectEnumerator is the more useful in new code, since you can just iterate on the array directly to iterate forward. Both of them were most useful before fast enumeration existed; that code looked like this:
NSEnumerator *myArrayEnum = [myArray objectEnumerator];
MyObject *myObject;
while ((myObject = [myArrayEnum nextObject])) {
…
}
(Yes, that's an assignment in the condition. Deliberately, hence the extra (). We coded boldly back then, didn't we?)
For what you're doing, though, you more likely want to send one of the arrays an isEqualToArray: message, as Williham Totland suggested:
BOOL theyAreEqual = [myFirstArray isEqualToArray:mySecondArray];
This will make sure both arrays have the same length, then walk them both in lock-step, sending isEqual: to each pair of objects. It'll return YES if every isEqual: message returned YES; NO otherwise. The arrays may contain different objects, but as long as each pair is equal, the arrays themselves are equal.
That assumes you want object equality. Two separate objects are equal if one of them responds with YES when you send it an isEqual: message and pass the other object. If you meant to compare the identities of the objects, then you do need to do the lock-step loop yourself and use ==:
BOOL arraysContainTheSameObjects = YES;
NSEnumerator *otherEnum = [otherArray objectEnumerator];
for (MyObject *myObject in myArray) {
if (myObject != [otherEnum nextObject]) {
//We have found a pair of two different objects.
arraysContainTheSameObjects = NO;
break;
}
}
But that's unlikely. Most of the time, I have wanted to test the objects' equality, not identities, so isEqualToArray: is what I wanted.
You want the isEqualToArray: method. As in:
if ([arrayOne isEqualToArray:arrayTwo]) {
// Do something
}
This will recursively compare the two arrays, while having the advantage of not being needlessly circuitous and not requiring a loop.
Try telling us the result you're getting when you run this code. The approach is correct, but try this one:
for (int i =0; i< appdelegate.nicearray.count; i++)
{
if ([[appdelegate objectAtIndex:i] isEqual: [appdelegate.exercarray objectAtIndex:i]])
{
NSLog(#"the same");
}
}
Here's a little one I put together based on the top ranked example. This merely checks that the arrays contains the same values, irrespective of order and if there are any duplicates. I mainly use this to compare keys of two dictionaries (which often return their allKeys arrays in various sort orders) to see if they contain the same objects. Thanks Peter Hosley for providing the example I adapted this from.
#pragma mark - Arrays
// Check to see if arrays contain the same elements, not necessarily in the same order
// This is different from [array isEqualToArray:responseKeys] which demands the same order in both arrays
// ## Does not compensate for duplicate entries in an array
+ (BOOL)doArraysContainTheSameObjects:(NSArray *)firstArray withArray:(NSArray *)secondArray {
BOOL arraysContainTheSameObjects = YES;
for (id myObject in firstArray) {
if (![secondArray containsObject:myObject]) {
// We have found an object that is not in the other array.
arraysContainTheSameObjects = NO;
break;
}
}
return arraysContainTheSameObjects;
}
I do the following when comparing arrays:
Check to see if any of the arrays are nil when the other is not
Check to see if the lengths are the same
Iterate (using a for loop like you have) over each element checking the matching element in the other array.
To compare elements you need to define what you want to regard as being "equal". Are they equal only if the pointers in the array are equal or can they be equal if the content is equal too.
For the pointer case, you can use ==.
For the deep comparison you might need to use CompareTo or something similar.