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]
Related
I need to sort a mutable array, but in this specific case when it comes time to sort, I don't need it to be mutable anymore. The sortedArrayUsingSelector: method returns an NSArray * even when called by an NSMutableArray * object. I have 3 options:
1) I can make a mutableCopy of the returned NSArray * and store it in the var I already have
NSMutableArray *mutableArray = [NSMutableArray array];
// add a bunch of stuff to the array
mutableArray = [[mutableArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] mutableCopy];
2) I can make a new NSArray * var to hold the returned NSArray *
NSMutableArray *mutableArray = [NSMutableArray array];
// add a bunch of stuff to the array
NSArray *array = [mutableArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
3) I can ignore Xcode's warnings and store the returned NSArray * object in an NSMutableArray * var
NSMutableArray *mutableArray = [NSMutableArray array];
// add a bunch of stuff to the array
mutableArray = [mutableArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
I realize that for most cases the difference is negligible, but considering available resources, efficiency, and safety, which would be the overall "best" option?
EDIT: I hadn't considered that the best option might be to create a non-mutable array from the mutable one before sorting it. Not sure if this would be, in fact, the best option, but something I figured I'd mention.
Since you have a mutable array already, just use the following method on NSMutableArray:
- (void)sortUsingSelector:(SEL)comparator
That way you're not recreating anything. This is likely more efficient than creating a new, sorted array from the original and then creating a mutable copy of that. Part of the point of having a mutable array in the first place is that you can change stuff around without needing to recreate it each time, so sorting is a very obvious thing to have here.
The array you get back from sortedArrayUsingSelector won't be a deep copy - it will contain the same pointers as the original array, just in a different order. These array methods in foundation will be well optimized, so I wouldn't worry about it too much.
Instead, just decide what you want. If you want a sorted NSArray * that won't look like an NSMutableArray *, just use sortedArrayUsingSelector, which returns an NSArray *
If you don't need the original unsorted array anymore, just sort the mutable array, like #Gavin suggests.
Given the following code:
NSMutableArray *a = [NSMutableArray array];
NSMutableArray *b = [NSMutableArray array];
[a addObject:b];
[b addObject:a];
What are the options to make the array objects deallocate when I set a and b to NIL?
Ive tried few things (Like weak references) but it doesn't seem to work.. (Probably because I don't understand enough - new to objective c ).
would love to get some assistance.
thank you
you can make an entire array not retain its elements with: CFArrayCreate()
this can be problematic in ARC if you no longer use the elements then iterate through the array later.
It's explained here: Non-retaining array for delegates.
You can use
+ (NSValue *)valueWithNonretainedObject:(id)anObject
And put the NSValue in the array.
Or do that:
NSMutableArray* NICreateNonRetainingMutableArray(void) {
return (NSMutableArray *)CFArrayCreateMutable(nil, 0, nil);
}
NSMutableDictionary* NICreateNonRetainingMutableDictionary(void) {
return (NSMutableDictionary *)CFDictionaryCreateMutable(nil, 0, nil, nil);
}
NSMutableSet* NICreateNonRetainingMutableSet(void) {
return (NSMutableSet *)CFSetCreateMutable(nil, 0, nil);
}
I'm not sure how you tried to add weak references to an array, but if it was something like this:
NSMutableArray *myArray = [NSMutableArray new]
__weak id weakObject = blah;
[myArray addObject:weakObject]
Then the fact that weakObject is declared weak makes no difference. It only makes the local reference to your object from the stack weak. In your situation, the array needs to hold a weak reference to the object, and NSMutableArray always holds strong references to its members.
There are several ways of doing this. CFArray allows you to specify how it should retain its members when you create it. Or you could 'box' the reference inside an NSValue, which although retained itself by the array, does not retain its contents.
If you're targeting Mac or iOS 6.0+, the best solution is to use a weak NSPointerArray (created using [NSPointerArray weakObjectsPointerArray]).
This is better than using CFArray or an NSValue inside a normal NSMutableArray because when the referenced object is deallocated, the array element will be automatically set to nil.
One solution to this kind of thing is to wrap your objects in a weak proxy object. Here is an example implementation of such an object:
https://github.com/j-h-a/jalib-core/blob/master/JALibCore/Helpers/JAWeakProxy.h
https://github.com/j-h-a/jalib-core/blob/master/JALibCore/Helpers/JAWeakProxy.m
Then you can do this:
NSMutableArray* a = [NSMutableArray array];
NSMutableArray* b = [NSMutableArray array];
[a addObject:[JAWeakProxy weakProxyWithTarget:b]];
[b addObject:[JAWeakProxy weakProxyWithTarget:a]];
Now both a and b are weakly referenced within the array. The good thing about using weak proxy objects is that you don't need to 'unbox' the real object inside them - you can just send messages (call methods) directly on the weak proxy object as if it were the target and it will pass the message along. For example:
[a[0] addObject:#(0)];
Notice how a[0] actually returns the weak proxy object holding b as its target, but I can sill send addObject directly to this weak proxy representing b (which is an NSMutableArray) and the implementation of the weak proxy will ensure that the message is forwarded to b.
However, you do lose some compile-time type-checking, so this technique is best used to help with the internal implementation of some class, which will have well-typed methods to access and enumerate the contents.
When I use such things I usually put in some auto-clean-up code into the array enumerators. That is I hide the arrays inside a utility class and provide block-based enumeration methods and I also keep a toRemove array. When iterating I skip any objects that the target has auto-zeroed to nil and add them to the toRemove array (the weak proxy object still exists even though its target is gone), then afterwards I iterate through the toRemove array and remove the proxy objects. You can also check if the target is nil and add it to the toRemove array in any accessor helpers.
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;
}