可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need to create a mutable two-dimensional array in Objective-C.
For example I have:
NSMutableArray *sections;
NSMutableArray *rows;
Each item in sections
consists of an array rows
. rows
is an array that contains objects.
And I want to do something like this:
[ sections[i] addObject: objectToAdd]; //I want to add a new row
In order have something like this:
section 0, rows: obj1, obj2, obj3
section 1, rows: obj4, obj5, obj6, obj 7...
Is there a way to do that in Objective-C?
回答1:
First, you must allocate and initialize your objects before use, something like: NSMutableArray * sections = [[NSMutableArray alloc] initWithCapacity:10];
For the rows, you need one object for each, not a single NSMutableArray * rows;
Second, depending on whether you're using Xcode 4.4+ (which introduced subscripting, a.k.a section[i]
& section[i] = …
) you may have to use [sections objectAtIndex:i]
for reading and [section replaceObjectAtIndex:i withObject: objectToAdd]
for writing.
Third, an array cannot have holes, i.e., obj1, nil, obj2. You must provide actual object to every index. If you do need to put nothing, you can use NSNull
object.
Moreover, don't forget that you can also store Objective-C objects in plain C arrays:
id table[lnum][rnum];
table[i][j] = myObj;
回答2:
If you want to do this using arrays, you can intialize your sections
array, then add a rows array as follows:
NSMutableArray *sections = [[NSMutableArray alloc] init];
NSMutableArray *rows = [[NSMutableArray alloc] init];
//Add row objects here
//Add your rows array to the sections array
[sections addObject:rows];
If you want to add this rows object at a certain index, use the following:
//Insert your rows array at index i
[sections insertObject:rows atIndex:i];
You can then modify this rows array by retrieving the array:
//Retrieve pointer to rows array at index i
NSMutableArray *rows = [sections objectAtIndex:i]
//modify rows array here
You could always create your own class called Section
, which has an NSMutableArray
member called rows
; then you store your rows inside this array, and store the Section
objects in an array:
@interface Section : NSObject {
NSMutableArray *rows;
}
Then you simply create Section
items, and you can create methods inside your class to add/remove row items. Then you package all the Sections
items up inside another array:
Section *aSection = [[Section alloc] init];
//add any rows to your Section instance here
NSMutableArray *sections = [[NSMutableArray alloc] init];
[sections addObject:aSection];
This becomes more useful if you want to add more properties for each Section
instance.
回答3:
The language has no support for multi-dimensional object-oriented arrays, but you can make a class that does it using an NSMutableArray full of NSMutableArrays, like the following. I haven't tried compiling this or anything, I just typed it in.
@interface SectionArray : NSObject {
NSMutableArray *sections;
}
- initWithSections:(NSUInteger)s rows:(NSUInteger)r;
+ sectionArrayWithSections:(NSUInteger)s rows:(NSUInteger)r;
- objectInSection:(NSUInteger)s row:(NSUInteger)r;
- (void)setObject:o inSection:(NSUInteger)s row:(NSUInteger)r;
@end
@implementation SectionArray
- initWithSections:(NSUInteger)s rows:(NSUInteger)r {
if ((self = [self init])) {
sections = [[NSMutableArray alloc] initWithCapacity:s];
NSUInteger i;
for (i=0; i<s; i++) {
NSMutableArray *a = [NSMutableArray arrayWithCapacity:r];
for (j=0; j<r; j++) {
[a setObject:[NSNull null] atIndex:j];
}
[sections addObject:a];
}
}
return self;
}
+ sectionArrayWithSections:(NSUInteger)s rows:(NSUInteger)r {
return [[[self alloc] initWithSections:s rows:r] autorelease];
}
- objectInSection:(NSUInteger)s row:(NSUInteger)r {
return [[sections objectAtIndex:s] objectAtIndex:r];
}
- (void)setObject:o inSection:(NSUInteger)s row:(NSUInteger)r {
[[sections objectAtIndex:s] replaceObjectAtIndex:r withObject:0];
}
@end
You'd use it like this:
SectionArray *a = [SectionArray arrayWithSections:10 rows:5];
[a setObject:@"something" inSection:4 row:3];
id sameOldSomething = [s objectInSection:4 row:3];
回答4:
what the hell. As long as we're reviving this question, here's something for the age of literal collection syntax and visual format interpretation!
In case anyone is wondering, this works:
NSMutableArray *multi = [@[ [@[] mutableCopy] , [@[] mutableCopy] ] mutableCopy];
multi[1][0] = @"Hi ";
multi[1][1] = @"There ";
multi[0][0] = @"Oh ";
multi[0][1] = @"James!";
NSLog(@"%@%@%@%@", multi[0][0], multi[1][0], multi[1][1], multi[0][1]);
Result: "Oh Hi There James!"
Of course, there is the problem of trying something like multi[3][5] = @"?"
and getting an invalid index exception, so I wrote a category for NSMutableArray.
@interface NSMutableArray (NullInit)
+(NSMutableArray *)mutableNullArrayWithSize:(NSUInteger)size;
+(NSMutableArray *)mutableNullArraysWithVisualFormat:(NSString *)string;
@end
@implementation NSMutableArray (NullInit)
+(NSMutableArray *)mutableNullArrayWithSize:(NSUInteger)size
{
NSMutableArray *returnArray = [[NSMutableArray alloc] initWithCapacity:size];
for (int i = 0; i < size; i++) {
[returnArray addObject:[NSNull null]];
}
return returnArray;
}
+(NSMutableArray *)mutableNullArraysWithVisualFormat:(NSString *)string
{
NSMutableArray *returnArray = nil;
NSRange bitRange;
if ((bitRange = [string rangeOfString:@"^\\[\\d+]" options:NSRegularExpressionSearch]).location != NSNotFound) {
NSUInteger size = [[string substringWithRange:NSMakeRange(1, bitRange.length - 2)] integerValue];
if (string.length == bitRange.length) {
returnArray = [self mutableNullArrayWithSize:size];
} else {
returnArray = [[NSMutableArray alloc] initWithCapacity:size];
NSString *nextLevel = [string substringWithRange:NSMakeRange(bitRange.length, string.length - bitRange.length)];
NSMutableArray *subArray;
for (int i = 0; i < size; i++) {
subArray = [self mutableNullArraysWithVisualFormat:nextLevel];
if (subArray) {
[returnArray addObject:subArray];
} else {
return nil;
}
}
}
} else {
return nil;
}
return returnArray;
}
@end
As you can see, we have a convenience method for making an array full of NSNull
so that you can set indices with wild abandon.
Secondly, there's a recursive method that parses a string with a visual format like: [3][12]
(3 x 12 array). If your string is invalid in some way the method will return nil
, but if it's valid you get a whole multidimensional array of the sizes you specify.
Here are some examples:
NSMutableArray *shrub = [NSMutableArray mutableNullArrayWithSize:5];
NSMutableArray *tree = [NSMutableArray mutableNullArraysWithVisualFormat:@"[3][12]"]; // 2-Dimensional Array
NSMutableArray *threeDeeTree = [NSMutableArray mutableNullArraysWithVisualFormat:@"[3][5][6]"]; // 3-Dimensional Array
NSMutableArray *stuntedTree = [NSMutableArray mutableNullArraysWithVisualFormat:@"[6][4][k]"]; // Returns nil
You can pass as many dimensions as you like into the visual format method, and then access them with the literal syntax, like so:
NSMutableArray *deepTree = [NSMutableArray mutableNullArraysWithVisualFormat:@"[5][3][4][2][7]"];
deepTree[3][2][1][0][5] = @"Leaf";
NSLog(@"Look what's at 3.2.1.0.5: %@", deepTree[3][2][1][0][5]);
Anyway, did this more as an exercise than anything else; it's probably pretty efficient in the grand scheme of things...considering as how we're making multi-dimensional arrays of Objective-C object pointers.
回答5:
Thanks to Jack for his code , i worked on it a bit; i need a multidimensional nsmutablearray for strings in one of my project , it still has some things that need to be fixed but works strings only at the moment , i will post it here , i am open for suggestions please cause i am only just a beginner in objective c at the moment...
@interface SectionArray : NSObject {
NSMutableArray *sections;
}
- initWithSections:(NSUInteger)intSections:(NSUInteger)intRow;
+ sectionArrayWithSections:(NSUInteger)intSections:(NSUInteger)intRows;
- objectInSection:(NSUInteger)intSection:(NSUInteger)intRow;
- (void)setObject:(NSString *)object:(NSUInteger)intSection:(NSUInteger)intRow;
@end
@implementation SectionArray
- initWithSections:(NSUInteger)intSections:(NSUInteger)intRow {
NSUInteger i;
NSUInteger j;
if ((self = [self init])) {
sections = [[NSMutableArray alloc] initWithCapacity:intSections];
for (i=0; i < intSections; i++) {
NSMutableArray *a = [NSMutableArray arrayWithCapacity:intRow];
for (j=0; j < intRow; j++) {
[a insertObject:[NSNull null] atIndex:j];
}
[sections addObject:a];
}
}
return self;
}
- (void)setObject:(NSString *)object:(NSUInteger)intSection:(NSUInteger)intRow {
[[sections objectAtIndex:intSection] replaceObjectAtIndex:intRow withObject:object];
}
- objectInSection:(NSUInteger)intSection:(NSUInteger)intRow {
return [[sections objectAtIndex:intSection] objectAtIndex:intRow];
}
+ sectionArrayWithSections:(NSUInteger)intSections:(NSUInteger)intRows {
return [[self alloc] initWithSections:intSections:intRows] ;
}
@end
This is working fine !!!
i am currently using it like this
SectionArray *secA = [[SectionArray alloc] initWithSections:2:2];
[secA setObject:@"Object":0:0];
[secA setObject:@"ObjectTwo":0:1];
[secA setObject:@"ObjectThree":1:0];
[secA setObject:@"ObjectFour":1:1];
NSString *str = [secA objectInSection:1:1];
NSLog(@" object is = %@" , str);
Thanks again Jack !!
回答6:
FYI: Jack's code needs a bunch of work before it works. Among other issues, the autorelease can cause the data to get released before you access it and his usage calls the class method arrayWithSections:rows: when it is actually defined as sectionArrayWithSections:rows:
I may try to post actual working code later if I get a chance.
回答7:
Reviving an old thread, but I reworked Jack's code so 1. it compiles and 2. it is in the order of 2D c arrays [rows][columns] instead of [sections(columns)][rows] as he has it. Here you go!
TwoDArray.h:
#import <Foundation/Foundation.h>
@interface TwoDArray : NSObject
@property NSMutableArray *rows;
- initWithRows:(NSUInteger)rows columns:(NSUInteger)columns;
+ sectionArrayWithRows:(NSUInteger)rows columns:(NSUInteger)columns;
- objectInRow:(NSUInteger)row column:(NSUInteger)column;
- (void)setObject:(id)obj inRow:(NSUInteger)row column:(NSUInteger)column;
@end
TwoDArray.m:
#import "TwoDArray.h"
@implementation TwoDArray
- (id)initWithRows:(NSUInteger)rows columns:(NSUInteger)columns {
if ((self = [self init])) {
self.rows = [[NSMutableArray alloc] initWithCapacity: rows];
for (int i = 0; i < rows; i++) {
NSMutableArray *column = [NSMutableArray arrayWithCapacity:columns];
for (int j = 0; j < columns; j++) {
[column setObject:[NSNull null] atIndexedSubscript:j];
}
[self.rows addObject:column];
}
}
return self;
}
+ (id)sectionArrayWithRows:(NSUInteger)rows columns:(NSUInteger)columns {
return [[self alloc] initWithRows:rows columns:columns];
}
- (id)objectInRow:(NSUInteger)row column:(NSUInteger)column {
return [[self.rows objectAtIndex:row] objectAtIndex:column];
}
- (void)setObject:(id)obj inRow:(NSUInteger)row column:(NSUInteger)column {
[[self.rows objectAtIndex:row] replaceObjectAtIndex:column withObject:obj];
}
@end
回答8:
This is what I did in order to initialize array of arrays.
NSMutableArray *arrayOfArrays = [[NSMutableArray alloc] initWithCapacity:CONST];
for (int i=0; i<=CONST; i++) {
[arrayOfArrays addObject:[NSMutableArray array]];
}
Then later in a code I could simply:
[[arrayOfArrays objectAtIndex:0] addObject:myObject];