In Matlab, I have a class named "point" (which is not a handle class).
I use a loop that creates a cell array of points, making it grow at each iteration without preallocation:
for k=1:npoints
newpoint=point(*some parameters that depend on k*);
pointcell{k}=newpoint; % pointcell grows at each iteration
end
In this example, the cell array "pointcell" grows at each iteration, which may not be optimal in terms of performance.
Is it useful to preallocate this cell array, and if yes how can this be done ?
Just using pointcell=cell(npoints) doesn't seem to work..
Also consider to use array of objects, instead of cell array of objects.
When you allocate a cell array, you aren't really helping Matlab to manage the memory. That is because each cell can be of any size, thus the only thing that can be preallocated is an array of pointers. Check out this for more info on cell arrays.
And then you can allocate an array of objects by doing repmat
points = repmat(point(defaultVal1,defaultVal2,...),1,npoints);
for k=1:npoints
newpoint=point(*some parameters that depend on k*);
points(k)=newpoint; % pointcell grows at each iteration
end
The recommended way to pre-allocate a cell array is one of:
pointcell = cell(npoints);
or
pointcell = {};
pointcell{1, npoints} = [];
pointcell = cell(npoints, 1);
I recommend sticking with your cell-array approach. One of the other answers mentioned using an array of objects; I do NOT recommend doing that! Assignment into a pre-allocated cell array is fast, where assignment into an array of objects is comparatively very slow. I think you have the right approach already.
Related
I was looking around and couldn't find anything, and I'm starting to think it's not possible with objective-c.
I have a NSMutableArray *myMutableArray and the size varies depending on what csv file is loaded. Since I do not set a size of myMutableArray I can't do:
if (c == 5){
myMutableArray[q] = [[NSNumber numberWithFloat:myOtherArray] stringValue];
q = q + 1;
c = 0;
}
Else {
c = c + 1;
}
Since myMutableArray is technically of size nil I guess I can't add objects to it.
In cases, q can be between 1500 and 2500.
My question is, how do I make `myMutableArray' change size on every loop.
If this isn't possible, I guess I will have to make myMutableArray very large - but I need the values in myMutableArray for a graph. If I do myMutableArray = [[NSMutableArray alloc] initWithCapacity:5000]; and don't use the 5000 memory locations, will these locations be nil, or 0? (sorry if the technical words are wrong, by memory locations I mean the memory given to myMutableArray)
Thank you, if there is anything else I can add to this please feel free to let me know.
EDIT: What I'm trying to achieve is adding data to the array, and with that data create a graph
You can't have a sporadically populated array. Creating an array with capacity 5000 is just a hint as to how much data you might be going to store into the array. Consider using an array of dictionaries where the dictionary contains the q value (presumably one coordinate) and the associated value. You can then sort the array based on the q values if you need to (for plotting). Then you can just add the dictionaries to the array as usual (addObject:).
The NSMutableArray class declares the programmatic interface to objects that manage a modifiable array of objects. This class adds insertion and deletion operations to the basic array-handling behavior inherited from NSArray.
If you
arrayWithCapacity:
Creates and returns an NSMutableArray object with enough allocated memory to initially hold a given number of objects.
Mutable arrays expand as needed. When declaring them, we can init them like this:
+ (instancetype)arrayWithCapacity:(NSUInteger)numItems
Here numItems simply establishes the object’s initial capacity.
Later to add more data, i.e. to expand mutable array, use this
addObject:
What it does is, it inserts a given object at the end of the mutable array.
- (void)addObject:(id)anObject
It's important to note that:
The object to add to the end of the array's content. This value must not be nil. It raises an NSInvalidArgumentException if anObject is nil.
Say I have a collection of "node" instances. An integer property call zIndex will be used to group them.
What are the pros/cons for storing them in :
1) An array of arrays
2) A dictionary of arrays
In pseudo code, I would describe the expected result like this:
zBuffer[100] = [node1, node 2];
zBuffer[105] = [playerNode, collectable1];
zBuffer[110] = [foreground1, foreground2];
And I'm wondering about what zBuffers should be; Must NSArrays only be used for sequential read/write? Like not using non-continuous indexes?
I tried with an NSMutableArray:
[zBuffer objectAtIndex:zOrder]
But it fails if the array contains no data for that index (like out-of-bound exception).
Thanks for your advices!
J
As far as I can see, one of your requirements is that the indexes you use to access zBuffer be not contiguous (100, 105, 100). In this case, I would not use an array for that, since the indexes you can use with an array must be less than the count of elements of the array (if you have 3 elements, then indexes range from 0 to 2).
Instead I would use NSMutableDictionary, where you can use the zIndex key as a "name" for groups of objects you are looking for.
This suggestion does not take into account any other requirements that you might have, especially concerning complexity and the kind of operations you are going to carry through on your collection of nodes (beyond accessing them through zIndex).
You could actually provide both. It looks like what you want to have is a sparse array: so you look up objects by index, but it's permissible for there not to be an object at a certain index. So you could make that.
I'd do that by creating an NSMutableArray subclass that implements the primitive methods documented. Internally, your subclass would use an NSMutableDictionary for storage, with numbers (the "filled" indices) as keys. -objectAtIndex: returns either the object with that number as its key or nil if the array is empty at that point.
There are some ambiguities in this use of the array contract that it's up to you to decide how to address:
does count return 1+(highest index in use), or the number of objects in the array?
the enumerator and fast enumeration patterns never expect to see nil, so you need to come up with an enumerator that always returns an object (but lets me see what index it's at) if you want users of your class to enumerator over the array.
you won't be able to initialise it with the +arrayWithObjects: (id) firstObject,... pattern of initialisers because they use nil as a sentinel.
I'm sorry if this is a bit of a C-noob question: I know I need to swot up on my pointers. Unfortunately I'm on a deadline so don't have time to work through a whole book chapter, so I'm hoping for a bit more targeted advice.
I want to store some objective-C objects in a C array. I'm using ARC. If I were on the Mac I'd be able to use NSPointerArray instead, but I'm on iOS and that's not available.
I'll be storing a three-dimensional C array: conceptually my dimensions are day, height, and cacheNumber. Each element will either be a pointer to an objective-C object, or NULL.
The number of caches (i.e. the size of the cacheNumber dimension) is known at compile time, but the other two are not known. Also, the array could be very large, so I need to dynamically allocate memory for it.
Regarding ownership semantics, I need strong references to the objects.
I would like the whole three-dimensional array to be an instance variable on an objective-C object.
I plan to have a method that is - tableForCacheNumber:(int)num days:(int*)days height:(int*)height. That method should return a two-dimensional array, that is one specific cache number. (It also passes back by reference the size of the array it is returning.)
My questions:
What order should I put my dimensions so that I can easily return a pointer to the subarray for one specific cache number? (I think it should be first, but I'm not 100%.)
What should the return type of my method be, so that ARC doesn't complain? I don't mind if the returned array has an increased reference count or not, as long as I know which it's doing.
What type should my instance variable that holds the three dimensional array be? I think it should just be a pointer, since that ivar just represents the pointer to the first item that's in my array. Correct? If so, how do I specify that?
When I create the three-dimensional array (for my ivar), I guess I do something like calloc(X * Y * Z, sizeof(id)), and cast the result to the type for my ivar?
When accessing items from the three-dimensional array in the ivar, I believe I have to dereference the pointer each time, with something like (*myArray)[4][7][2]. Correct?
Will the two-dimensional array I return from the method be similarly accessed?
Do I need to tag the returned two-dimensional array with objc_returns_inner_pointer?
I'm sorry once again that this is a bit of a bad Stack Overflow question (it's too long and with too many parts). I hope the SO citizens will forgive me. To improve my interweb karma, maybe I'll write it up as a blog post when this project has shipped.
First off: while you don't have NSPointerArray, you do have CFMutableArrayRef and you can pass any callbacks you want for retain/release/description, including NULL. It may be easier (and performance is something you can measure later) to try that first.
Taking your points in order:
you should define your dimensions as [cacheNumber][days][height], as you expect. Then cache[cacheNumber] is a two-dimensional array of type id *[][]. As you've said performance is important, be aware that the fastest way to iterate this beast is:
for (/* cacheNumber loop */) {
for (/* days loop */) {
for (/* height loop */) {
//...
}
}
}
it should be of type __strong id ***: that's a pointer to a pointer to a pointer to id, which is the same as array of (array of (pointer to id)).
your ivar needs to be __strong id **** (!), because it's an array of the above things.
you guess incorrectly regarding allocating the array.. If you're using a multidimensional array, you need to do this (one dimension elided for brevity):
- (__strong id * * *)someArray {
__strong id * * *cache = (__strong id * * *)malloc(x*y*sizeof(void *));
id hello = #"Hello";
cache[0] = (__strong id * *)malloc(sizeof(void *)); //same for cache[1..x-1]
cache[0][0] = &hello; // for all cache[x][y]
return (__strong id * * *)cache;
}
correct, that is how you use such a pointer.
yeah, the two-D array works in the same way, sans the first dimension.
I don't think so, you're handing out __strong object pointers so you should be grand. That said, we're at about the limit of my ability with this stuff now so I could well be wrong.
Answering my own question because this web page gave me the missing bit of info I needed. I've also upvoted Graham's answer, since he was very helpful in getting my head round some of the syntax.
The trick I was missing is knowing that if I want to refer to items in the array via the array[1][5][2] syntax, and that I don't know the sizes of my array at compile time, I can't just calloc() a single block of data for it.
The easiest to read (although least efficient) method of doing that is just with a loop:
__strong Item ****cacheItems;
cacheItems = (__strong Item ****)calloc(kMaxZooms, sizeof(Item ***));
for (int k = 0; k < kMaxZooms; k++)
{
cacheItems[k] = (__strong Item ***)calloc((size_t)daysOnTimeline, sizeof(Item **));
for (int j = 0; j < daysOnTimeline; j++)
{
cacheItems[k][j] = (__strong Item **)calloc((size_t)kMaxHeight, sizeof(Item *));
}
}
I'm allocating a three dimensional array of Item *s, Item being an objective-C class. (I have of course left out the error handling code in this snippet.)
Once I've done that, I can refer to my array using the square brackets syntax:
cacheItems[zoom][day][heightToUse] = item;
The web page I linked to above also describes a second method for performing the memory allocations, that uses only one call to calloc() per dimension. I haven't tried that method yet, as the one I've just described is working well enough at the moment.
I would think of a different implementation. Unless it is a demonstrable (i.e. you have measured and quantified it) performance issue, trying to store Objective-C objects in plain C arrays is often a code smell.
It seems to me that you need an intermediate container object which we will call a Cache for now. One instance will exist for each cache number, and your object will hold an NS(Mutable)Array of them. Cache objects will have properties for the maximum days and height.
The Cache object would most easily be implemented with an NSArray of the objects in it, using simple arithmetic to simulate two dimensions. Your cache object would have a method -objectAtDay:Height: to access the object by its coordinates.
This way, there is no need at all to worry about memory management, ARC does it for you.
Edit
Given that performance is an issue, I would use a 1D array and roll my own arithmetic to calculate offsets. The type of your instance variable would be:
__strong id* myArray;
You can only use C multilevel subscripts (array[i][j][k]) if you know the range of all the dimensions (except the first one). This is because the actual offset is calculated as
(i * (max_j * max_k) + j * max_k + k) * sizeof(element type)
If the compiler doesn't know max_j and max_k, it can't do it. That's precisely the situation you are in.
Given that you have to use a 1D array and calculate the offsets manually, the Apple example will work fine for you.
I've been programming for a while in objective-c, but I've unfortunately never delved very deeply into C and memory pointers, although I do have a rudimentary understanding of them. I'm working with an array of CLLocationCoordinate2D structures, and I'm trying to figure out how to append to the array. First of all, I get the
NSString *aString; //a bunch of coordinates
CLLocationCoordinate2d *coordinates;
int length;
doSomethingCool(aString, &coordinates, &length);
after I do something cool, I want to preserve it in a class variable. If I simply do something like
points = newPoints
points contains the appropriate contents. However, if I try to do something like this:
points = malloc(sizeof(CLLocationCoordinate2D) * length);
points[0] = *newPoints;
points ends up with contents different from newPoints.
Ultimately my goal is to be able to append to points based on length, but I'm not going to be able to do that if I can't get the above code to work. What am I doing wrong?
Your code simply copies the first value of newPoints into the first value of points (*newPoints is equivalent to newPoints[0]).
One situation is to make a new array, copy all values, switch the arrays, and free() the old one. For example:
int* newvals = malloc(sizeof(int) * newcount);
memcpy(newvals, vals, sizeof(int) * oldcount);
free(vals);
vals = newvals;
You can also use realloc - its behavior is similar to the above (though it can fail!), but at times may be more efficient.
Note that you simply can't change the underlying pointer's size in a safe and portable fashion. You will need to update your instance ("class") variable with the new pointer.
The idea would be to copy all of the array into a temporary array, resize the original, and then copy them back. However, managing this could get hairy. You'd be better off using an std::vector and just appending it.
EDIT: I just realized you're using C, not C++. Disregard the second half of this.
I have an undetermined size for a dataset based on unique integer keys.
I would like to use an NSMutableArray for fast lookup since all my keys are integer based.
I want to do this.
NSMutableArray* data = [NSMutableArray array]; // just create with 0 size
then later people will start throwing data at me with integer indexes (all unique) so I just want to do something like this...
if ([data count] < index)
[data resize:index]; // ? how do you resize
and have the array resized so that i can then do...
[data insertObject:obj atIndex:index];
with all the slots between last size and new size being zero which will eventually be filled in later.
So my question is how do I resize an existing NSMutableArray?
Thanks,
Roman
Use an NSPointerArray.
http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSPointerArray_Class/Introduction/Introduction.html
NSPointerArray is a mutable collection
modeled after NSArray but it can also
hold NULL values, which can be
inserted or extracted (and which
contribute to the object’s count).
Moreover, unlike traditional arrays,
you can set the count of the array
directly. In a garbage collected
environment, if you specify a zeroing
weak memory configuration, if an
element is collected it is replaced by
a NULL value.
If you were to use a dictionary like solution, use NSMapTable. It allows integer keys. The NSMutableDictionary based solution recommended has a tremendous amount of overhead related to all of the boxing & unboxing of integer keys.
It sounds like your needs would be better met with an NSMutableDictionary. You will need to wrap the ints into NSNumber objects as follows:
-(void)addItem:(int)key value:(id)obj
{
[data setObject:obj forKey:[NSNumber numberWithInt:key]];
}
-(id)getItem:(int)key
{
return [data objectForKey:[NSNumber numberWithInt:key]];
}
There's no easy was to enlarge the size of an NSMutableArray, since you cannot have nil objects in the in-between slots. You can, however, use [NSNull null] as a 'filler' to create the appearance of a sparse array.
As in Jason's answer, an NSMutableDictionary seems to be the best approach. It adds the overhead of converting the index values to and from NSNumbers, but this is a classic space/time trade off.
In my implementation I also included an NSIndexSet to make traversing the sparse array much simpler.
See https://github.com/LavaSlider/DSSparseArray
I have to disagree with bbum's answer on this. A NSPointerArray is an array, not a sparse array, and there are important differences between the two.
I strongly recommend that bbums solution not be used.
The documentation for NSPointerArray is available here.
Cocoa already has an array object as defined by the NSArray class. NSPointerArray inherits from NSObject, so it is not a direct subclass of NSArray. However, the NSPointerArray documentation defines the class as such:
NSPointerArray is a mutable collection modeled after NSArray but it can also hold NULL values
I will make the axiomatic assumption that this definition from the documentation asserts that this is a "logical" subclass of NSArray.
Definitions-
A "general" array is: a collection of items, each of which has a unique index number associated with it.
An array, without qualifications, is: A "general" array where the indexes of the items have the following properties: Indexes for items in the array begin at 0 and increase sequentially. All items in the array contains an index number less than the number of items in the array. Adding an item to an array must be at index + 1 of the last item in the array, or an item can be inserted in between two existing item index numbers which causes the index number of all subsequent items to be incremented by one. An item at an existing index number can be replaced by another item and this operation does not change the index numbers of the existing operations. Therefore, insert and replace are two distinct operations.
A sparse array is: A "general" array where the index number of the first item can begin at any number and the index number of subsequent items added to the array has no relation to or restrictions based on other items in the array. Inserting an item in to a sparse array does not effect the index number of other items in the array. Inserting an item and replacing an item are typically synonymous in most implementations. The count of the number of items in the sparse array has no relationship to the index numbers of the items in the sparse array.
These definitions make certain predictions about the behavior of a "black box" array that are testable. For simplicity, we'll focus on the following relationship:
In an array, the index number of all the items in the array is less than the count of the number of items in the array. While this may be true of a sparse array, it is not a requirement.
In a comment to bbum, I stated the following:
a NSPointerArray is not a sparse array, nor does it behave like one. You still have to fill all the unused indexes with NULL pointers. Output from [pointerArray insertPointer:#"test" atIndex:17]; on a freshly instantiated NSPointerArray:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSConcretePointerArray insertPointer:atIndex:]: attempt to insert pointer at index 17 beyond bounds 0'
It is stated, without proving, the the behavior of NSPointerArray above violates the very definition of a sparse array. This part of the error message is revealing: attempt to insert pointer at index 17 beyond bounds 0', in particular the part about having to add the first new item at index 0.
bbum then comments:
That is incorrect. You failed to call -setCount: to set the capacity to a sufficient size.
It is non-sensical to "set the count" of the number of items in a sparse array. If NSPointerArray was a sparse array, one would expect that after adding the first item at index 17, the count of the number of items in the NSPointerArray would be one. However, following bbums advice, the number of items in the NSPointerArray after adding the first items is 18, not 1.
QED- It is shown that a NSPointerArray is in fact an array, and for the purposes of this discussion, a NSArray.
Additionally, bbum makes the following additional comments:
NSPointerArray most certainly does support holes.
This is provably false. An array requires all items contained in it to contain something, even if that something is 'nothing'. This is not true of a sparse array. This is the very definition of a 'hole' for the purposes of this discussion. A NSPointerArray does not contain holes in the sparse array sense of the term.
That was one of the whole points of writing the class. You have to set the count first.
It is provably non-sensical to "set the count" of a sparse array.
Whether the internal implementation is a sparse array or a hash or, etc, is an implementation detail.
This is true. However, the documentation for NSPointerArray does not make any reference to how it implements or manages its array of items. Furthermore, it does not state anywhere that a NSPointerArray "efficiently manages an array of NULL pointers."
QED- bbum is depending on the undocumented behavior that a NSPointerArray efficiently handles NULL pointers via a sparse array internally. Being undocumented behavior, this behavior can change at any time, or may not even apply to all uses of the NSPointerArray. A change in this behavior would be catastrophic if the highest index number stored in it are sufficiently large (~ 2^26).
And, in fact, it is not implemented as one big hunk of memory...
Again, this is a private implementation detail that is undocumented. It is extremely poor programming practice to depend on this type of behavior.