NSMutableArray with size of 9 - objective-c

I need to have an NSMutableArray with a constant count of 9 where I can make index-specific insertions and deletions. I know that array = [[NSMutableArray alloc] initWithCapacity:9]; will declare an array with a capacity of 9, but when you get the size of the array, it returns 0.
My first attempt at a solution was to declare an array with capacity 9 (see above) and then fill it with NSNull objects. This code crashes with the error
[NSMutableArray insertObjects:atIndexes:]: array argument is not an NSArray'
- (void) setBlankArray: (NSMutableArray*)array {
for (int i = 0; i < 9; i++) {
[array insertObjects:[NSNull null] atIndexes:i];
}
}
-(void) addCurrentTile: (TileView*)aTile {
[currentTurnTilesArray insertObject:aTile atIndex: aTile.getValue-1];
}
-(void) removeCurrentTile: (TileView*)aTile {
[currentTurnTilesArray removeObjectAtIndex: aTile.getValue-1];
}
Is there a better way to accomplish it?

Not sure what you are trying to accomplish or why, but your removeCurrentTile will break it, because it will reduce the size of the array by 1. What you need to do is wrap this array with a class that guards it such that it can never never never have any other number of elements than 9.
Personally, I think what you're trying to do is silly. If you know you will always have exactly 9 slots, then you should start with a normal array, not a mutable array. It is the objects at each index that you want to mutate - not the array itself. For example, if these things were to be strings, then you would make an immutable array of 9 NSMutableString objects:
NSArray* arr = #[
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string]
];
Now each string can be mutated into another value, but no strings can be added or removed such as to change the length of the array.
Of course that's just an example (using strings, I mean). For maximum flexibility, this would be an NSArray of nine NSMutableDictionary objects. Now every NSMutableDictionary can contain anything, or nothing. But the number of NSMutableDictionaries will always be exactly nine, because the array itself is immutable.

You're looking for insertObject:atIndex:, or more simply addObject:.

[[NSMutableArray alloc] initWithCapacity:9] does not create an array with 9 elements.
It creates an empty array initialized with enough memory to hold 9 objects.
The purpose of this method is to allocate that much memory at once as you declare, so you can add elements to this array and system has not to allocate memory every time. This is only for optimization.
NSMutableArray reference

I just read your question, and I think I understand exactly what you need. Here is the code:
Declare a property:
#property (nonatomic, retain) NSMutableArray *myArray;
Synthesize it:
#synthesize myArray = _myArray;
Overrride its getter like this:
- (NSMutableArray *)myArray
{
if (!_myArray)
{
_myArray = [[NSMutableArray alloc] initWithCapacity:9];
for (int i = 0; i < 9; i++)
{
[self.myArray addObject:[NSNull null]];
}
}
return _myArray;
}
The "setBlankArray" method will simly set the property to nil, and next time you call the getter of the array property it will get automatically initialized with exactly what you need:
- (void)setBlankArray:(NSMutableArray *)array
{
self.myArray = nil;
}
VERY IMPORTANT: Do not write this code:
for (int i = 0; i < 9; i++)
{
[self.myArray addObject:[NSNull null]];
}
in the method just mentioned as this will make the array to contain 18 elements.
Then write the other 2 methods:
// you can also change the parameter from "id" to "TileView *"
- (void)addCurrentTile:(id)sender
{
NSInteger tileIndex = 1; // replace 1 with ((TileView *) sender).getValue - 1
[self.myArray replaceObjectAtIndex:tileIndex
withObject:sender];
}
// you can also change the parameter from "id" to "TileView *"
- (void)removeCurrentTile:(id)sender
{
NSInteger tileIndex = 1; // replace 1 with ((TileView *) sender).getValue - 1
[self.myArray replaceObjectAtIndex:tileIndex
withObject:[NSNull null]];
}
But, DO NOT FORGET to replace "id" with "TileView *", and TO SET the value of tileIndex to "((TileView *) sender).getValue - 1".
Hope this all makes sense, and is helpful for you.
Best regards

Related

Interpreting a data stream in objective c

I am getting a stream from a live feed. It looks something like this as a mutableArray:
"Columbia Heights",
E04,
"Cleveland Park",
A05
The first line is a name and the second is a station code.
I need to turn this into a dictionary that is Plist compliant.
So the result in mind would look like this:
name:
"Columbia Heights",
code:
E04,
name:
"Cleveland Park",
code:
A05
What I've written so far is this:
NSMutableDictionary *stationDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"Name",#"name",#"Code",#"code", nil];
int i = 0;
for (i=0; i < counter; i++) {
// get names and codes for each of 3 fave stations
//[stationArray addObject:[mutableFaveArray objectAtIndex:i]];
NSString *tempName = [mutableFaveArray[i] valueForKey:#"name"];
NSString *tempCode = [mutableFaveArray[i] valueForKey:#"code"];
[stationDict setObject:tempName forKey:#"name"];
[stationDict setObject:tempCode forKey:#"code"];
}
I'm no longer getting an error, but I get as output, the same as my input.. I'm also only getting the last record..
"Cleveland Park",
A05
Updated Code:
NSMutableDictionary *stationDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"Name",#"name",#"Code",#"code", nil];
int i = 0;
for (i=0; i < mutableFaveArray.count; i++) {
NSString *tempName = [mutableFaveArray[i] valueForKey:#"name"];
NSString *tempCode = [mutableFaveArray[i] valueForKey:#"code"];
[stationDict setObject:tempName forKey:#"name"];
[stationDict setObject:tempCode forKey:#"code"];
[stationArray addObject:stationDict];
}
This results in the last dictionary entry being saved twice..
{
code = A05;
name = "Cleveland Park";
},
{
code = A05;
name = "Cleveland Park";
}
The problem is that you're only creating a single dictionary, stationDict, before the for loop. You then hit the for loop, which changes the values in stationDict, adds stationDict to the stationArray, changes the values in stationDict again, adds that same dictionary to stationArray, and so on. What you end up with is an array that contains the same object repeated mutableFaveArray.count times.
The solution is to either create a new dictionary at the beginning of the body of the for loop, or add a copy of the dictionary to add to the array. So, either this:
for (i=0; i < mutableFaveArray.count; i++) {
stationDict = [NSMutableDictionary dictionary];
//...continue with the rest of your code...
or this:
//...other for loop code precedes this...
[stationArray addObject:[stationDict copy]];
}
Either approach ensures that each thing you add to the array is a dictionary that's distinct from the others you've added. As a refinement, you could probably speed up the code a little by using a non-mutable dictionary. If you have to create a new dictionary each time through the loop, you can create it with the data you want it to contain rather than changing the objects, so you don't necessarily need it to be mutable. Also, you can declare the loop variable inside the for statement, which ensures that it'll go out of scope as soon as the for terminates. Also, you can use Objective-C's notation for dictionaries:
NSMutableArray *stationArray = [NSMutableArray array];
for (int i=0; i < mutableFaveArray.count; i++) {
[stationArray addObject:#{#"name":mutableFaveArray[i][#"name"],
#"code":mutableFaveArray[i][#"code"]}];
}
You can further abbreviate that and maybe speed it up a bit using fast enumeration:
NSMutableArray *stationArray = [NSMutableArray array];
for (NSDictionary *fave in mutableFaveArray) {
[stationArray addObject:#{#"name":fave[#"name"],
#"code":fave[#"code"]}];
}
Or you can let mutableFaveArray do the enumerating for you:
__block NSMutableArray *stationArray = [NSMutableArray array];
[mutableFaveArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop))
{
[stationArray addObject:#{#"name":obj[#"name"],
#"code":obj[#"code"]}];
}];
That last one might be a bit faster, but perhaps a bit harder to read.

property for ivar that points to two-dimensional array of pointers to NSStrings

I want to create a class that contains a dynamic, two-dimensional c-array of pointers to NSStrings. I know I can simulate a two-dimensional array using an NSArray containing multiple NSArrays, but if possible I'd like to do this using a traditional two-dimensional c-array. ARC won't allow a simple assignment of a pointer to an NSString to an element of a c-array unless you use "__unsafe_unretained":
#interface NumberStringsArray : NSObject
{
#public
NSString * __unsafe_unretained **_array;
}
To avoid memory leaks and to give an object in the class ownership of each NSString assigned to the c-array, I add a pointer to each NSString object to an NSMutableArray. In -(void)dealloc I free the memory acquired to create the two-dimensional c-array.
Here's my question: How do I declare a property based on the _array ivar so that I can refer to the i,j element of the array as "foobar.array[i][j]" rather than "foobar->array[i][j]"?
Later amplification: I did it in a very similar manner to the answerer except for the __bridge stuff. I don't know if that makes a difference. I allocate the two-dimensional array here:
self->_array = (NSString * __unsafe_unretained **)calloc(_columnCount, sizeof(void *));
if (!self->_array)
return nil;
for (UINT16 i = 0; i < _columnCount; i++)
{
self->_array[i] = (NSString * __unsafe_unretained *)calloc(_rowCount, sizeof(void *));
if (!self->_array[i])
{
for (UINT16 a = 0; a < _columnCount; a++)
if (self->_array[a])
free(self->_array[a]);
if (self->_array)
free(self->_array);
return nil;
}
}
I put pointers to the NSString objects into the array using substrings generated from a file of comma-separated values:
NSArray *numbers = [line componentsSeparatedByString: #","];
for (UINT16 i = 0; i < _columnCount; i++)
{
NSString *number = #"";
if (i < [numbers count])
number = [numbers objectAtIndex: i];
//
// save it in owners
//
[self.owners addObject: number];
self->_array[i][j] = number;
}
In -(void)dealloc I free all the memory:
-(void)dealloc
{
for (UINT16 i = 0; i < self.columnCount; i++)
if (self->_array[i])
free(self->_array[i]);
if (self->_array)
free(self->_array);
}
Declare this property:
#property (nonatomic) NSString * __unsafe_unretained **_array;
Then you can allocate the pointers to objects:
_array= (NSString * __unsafe_unretained **) malloc(M*sizeof(CFTypeRef) );
for(NSUInteger i=0; i<M;i++)
{
_array[i]= ((NSString * __unsafe_unretained *) malloc(N*sizeof(CFTypeRef) );
for(NSUInteger j=0; j<N;j++)
{
_array[i][j]= (__bridge NSString*) (__bridge_retained CFTypeRef) [[NSString alloc]initWithCString: "Hello" encoding: NSASCIIStringEncoding];
// I see that you got habit with C so you'll probably like this method
}
}
Then when you don't need it anymore, free the array:
for(NSUInteger i=0; i<M; i++)
{
for(NSUInteger j=0; j<N;j++)
{
CFTypeRef string=(__bridge_transfer CFTypeRef) _array[i][j];
}
free(_array[i]);
}
free(_array);
You can't because you can't declare a concrete object for an Objective-C class. So
NumberStringsArray object;
is not allowed.
You are forced to declare it as
NumberStringsArray *object = [[NumberStringsArray alloc] init.. ];
so you have to access to the ivar through the correct -> operator applied to pointers. Mind that the object.something in Objective-C is just a shorthand for [object something] while in standard C you would use . to access to fields of a concrete struct.
(Note: This addresses the creation/use of the property to access the data, not the way the data should be managed by conventional Objective-C storage management or by ARC. Thinking about that makes my head hurt.)
If you want a read-only C array to "look" like an Objective-C property, declare the property such as #property (readonly, nonatomic) char* myProp; and then, rather than using #synthesize, implement a getter for it along the lines of:
-(char**)myProp {
return myPropPointer;
// Or, if the array is allocated as a part of the instance --
return &myPropArray[0];
}

How can I pass a C array to a objective-C function?

I'm not familiar with C. How can I pass a C array to a Objective-C function ?
I actually need an example of a class function converting NSArray to C arrays.
This is what I have so far:
+ (NSArray *)convertArray:(NSString*)array { //I don't think this is correct: the argument is just a NSString parameter and not an array
NSMutableArray * targetArray = [NSMutableArray array];
for (i = 0; i < SIZE; i++) //SIZE: I dunno how to get the size of a C array.
{
[targetArray addObject: [NSString stringWithString:array[i]];
}
return targetArray;
}
There are a few ways.
If your array size is fixed at compile-time, you can use the C99 static modifier:
-(void) doSomething:(NSString *[static 10]) arg
{
}
If not, you have to pass it as two separate arguments. One as a pointer to the first element of it, and the second as the length of it:
-(void) doSomething:(NSString **) arg count:(size_t) count
{
}
Now you can access your variables like any other array you may have.
Because you are dealing with a C-array of objective-c objects, you can actually use NSArray's built in constructor for turning a C-array into a NSArray:
NSArray *result = [NSArray arrayWithObjects:arg count:count];

Add a tag to NSMutableArray

Is it possible to set a tag for an NSMutableArray? I have to somehow determine, in an array of arrays, the single array which needs to be rewritten, and if I could just set the tag to that inner array to 1 (or some other number), this would be extremely easy.
Example:
NSMutableArray* outerArray = [NSMutableArray new];
NSMutableArray* innerArray1 = [NSMutableArray new];
NSMutableArray* innerArray2 = [NSMutableArray new];
NSMutableArray* innerArray3 = [NSMutableArray new];
NSMutableArray* innerArray4 = [NSMutableArray new];
[outerArray addObject:innerArray1];
[outerArray addObject:innerArray2];
[outerArray addObject:innerArray3];
[outerArray addObject:innerArray4];
//now let's say innerArray1 needs to be rewritten
//I would like to be able to do this
[innerArray1 setTag:100];
//then later, when I need to determine which of the arrays inside outerArray
//needs to be rewritten, I can just do this
for(NSMutableArray* temp in outerArray) {
if(temp.tag == 100) {
//do what I need to do
}
}
But you can't use setTag: with NSMutableArrays. What would be a workaround?
Arrays are ordered collections, so why don't you just keep track of which index needs to be rewritten.
When something happens such that the array at index 0 (which, in your example, would be innerArray1) of outer array needs to be written, cache index 0 -- as a property if this routine needs to span across separate methods.
Then, when it comes time to do the rewrite, consult the cached index. Retrieve the array to be rewritten like this: NSArray *arrayToRewrite = [outerArray objectAtIndex:cachedIndexToRewrite]; Or access it directly: [[outerArray objectAtIndex:cachedIndexToRewrite] replaceObjectAtIndex:whatever withObject:whatever];
You could use an NSMutableDictionary instead. The "tag" would just be the key and the array would be the value.
Use associated objects. You can even add a category to NSMutableArray that would add a tag property to them.
#interface NSMutableArray (TagExtension)
#property (nonatomic, assign) NSInteger tag;
#end
#implementation NSMutableArray (TagExtension)
#dynamic tag;
static char TagExtensionKey;
-(NSInteger)tag {
NSNumber *ourTag = (NSNumber *)objc_getAssociatedObject(self, &TagExtensionKey);
if( ourTag ) {
return( [ourTag integerValue] );
}
return(0);
}
-(void)setTag:(NSInteger)newTag {
objc_setAssociatedObject(self, &TagExtensionKey, [NSNumber numberWithInteger:newTag], OBJC_ASSOCIATION_RETAIN);
}
#end
See also: How to add properties to NSMutableArray via category extension?
Not sure why a dictionary is a bad idea here… as alternatives, you can:
remember the index
or if each entry is a unique array, you can simply refer to it by pointer:
NSArray * tagged = theArray;
for (NSMutableArray * at in outerArray) {
if (tagged == at) {
//do what I need to do
}
}
Make your inner arrays class variables. Then you can just access them as:
for(NSMutableArray* temp in outerArray) {
if(temp == self.innerArray1) {
//do what I need to do
}

NSMutableArray insert object at index

I have an empty mutable array. Is it possible to insert object at index 2 for example, while there's nothing at index 0 and 1? I mean to increase capacity dynamically or something like that. .Regards.
NSMutableArray is not a sparse array; it does not allow empty slots that can be filled in later. initWithCapacity: just hints to the array that it will be filled to a certain amount; it isn't generally necessary in practice and, unless you know exactly how many items you are going to shove in the array, don't bother calling it (just use init).
A mutable array will quite efficiently grow in size as objects are added.
If you need a data structure that supports "holes", then either use something else or put a placeholder object in the slots that are supposed to be empty.
I.e. if you wanted an array with 10 slots, you might do:
NSMutableArray *a = [NSMutableArray array];
for(int i = 0; i<10; i++) [a addObject: [NSNull null]];
You can then check if the retrieved object isEqual: [NSNull null] to know if the slot is empty or not. And you can use replaceObjectAtIndex:withObject: to stick an object at a specific index.
Or you could use a different data structure; a dictionary with the indices as the keys would work, for example.
You can use a NSPointerArray for that.
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.
NSPointerArray is available in OS X v10.5 and later and iOS 6.0 and later. If you target a lower OS version you can, for example:
Use a NSMutableDictionary, wrap you indices into NSNumbers and use these as keys.
Use a NSMutableArray and fill the "holes" with NSNull objects.
Write yourself a SparseArray class using an underlying NSMutableDictionary. Something like this (minimal code, barely tested, but it should give you the idea).
#interface SparseArray : NSObject {
#private
NSMutableDictionary* _dict;
int count;
}
-(SparseArray*)initWithCapacity:(NSUInteger)anInt;
-(id)objectAtIndex:(int)anIndex;
-(void)insertObject:(id)anObject atIndex:(int)anIndex;
- (void)removeObjectAtIndex:(int)anIndex;
-(int)count;
#implementation SparseArray
-(SparseArray*)initWithCapacity:(NSUInteger)anInt {
if ((self = [super init])) {
_dict = [[NSMutableDictionary dictionaryWithCapacity:anInt] retain];
count = 0;
}
return self;
}
-(id)objectAtIndex:(int)anIndex {
NSNumber* key = [NSNumber numberWithInt:anIndex];
id object = [_dict objectForKey:key];
return object;
}
-(void)insertObject:(id)anObject atIndex:(int)anIndex {
NSNumber* key = [NSNumber numberWithInt:anIndex];
[_dict setObject:anObject forKey:key];
count++;
}
- (void)removeObjectAtIndex:(int)anIndex {
NSNumber* key = [NSNumber numberWithInt:anIndex];
id object = [_dict objectForKey:key];
if (object) {
[_dict removeObjectForKey:key];
count--;
}
}
-(int)count {
return count;
}
-(void)dealloc {
[_dict release];
[super dealloc];
}
#end