2D-массивы с использованием NSMutableArray - PullRequest
43 голосов
/ 07 апреля 2009

Мне нужно создать изменяемый двумерный массив в Objective-C.

Например, у меня есть:

NSMutableArray *sections;
NSMutableArray *rows;

Каждый элемент в sections состоит из массива rows. rows - это массив, содержащий объекты.

И я хочу сделать что-то вроде этого:

[ sections[i] addObject: objectToAdd]; //I want to add a new row

Для того, чтобы что-то вроде этого: секция 0, строки: obj1, obj2, obj3 раздел 1, строки: obj4, obj5, obj6, obj 7 ...

Есть ли способ сделать это в Objective-C?

Ответы [ 8 ]

88 голосов
/ 07 апреля 2009

Во-первых, вы должны выделить и инициализировать ваши объекты перед использованием, что-то вроде: NSMutableArray * sections = [[NSMutableArray alloc] initWithCapacity:10]; Для строк вам нужен один объект для каждого, а не один NSMutableArray * rows;

Во-вторых, в зависимости от того, используете ли вы Xcode 4.4+ (который ввел подписку, a.k.a section[i] & section[i] = …), вам может понадобиться использовать [sections objectAtIndex:i] для чтения и [section replaceObjectAtIndex:i withObject: objectToAdd] для записи.

В-третьих, массив не может иметь дыр, то есть obj1, nil, obj2. Вы должны предоставить фактический объект для каждого индекса. Если вам не нужно ничего ставить, вы можете использовать NSNull object.

Кроме того, не забывайте, что вы также можете хранить объекты Objective C в простых массивах C:

id table[lnum][rnum];
table[i][j] = myObj;
17 голосов
/ 07 апреля 2009

Если вы хотите сделать это с помощью массивов, вы можете инициализировать ваш массив sections, а затем добавить массив строк следующим образом:

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];

Если вы хотите добавить объект этих строк по определенному индексу, используйте следующее:

//Insert your rows array at index i
[sections insertObject:rows atIndex:i];

Затем вы можете изменить этот массив строк, получив массив:

//Retrieve pointer to rows array at index i
NSMutableArray *rows = [sections objectAtIndex:i]
//modify rows array here

Вы всегда можете создать свой собственный класс с именем Section, в котором есть член NSMutableArray с именем rows; затем вы сохраняете свои строки внутри этого массива и сохраняете объекты Section в массиве:

@interface Section : NSObject {
    NSMutableArray *rows;
}

Затем вы просто создаете Section элементов и можете создавать методы внутри своего класса для добавления / удаления элементов строк. Затем вы упаковываете все элементы Sections в другой массив:

Section *aSection = [[Section alloc] init];
//add any rows to your Section instance here

NSMutableArray *sections = [[NSMutableArray alloc] init];
[sections addObject:aSection];

Это становится более полезным, если вы хотите добавить больше свойств для каждого Section экземпляра.

9 голосов
/ 07 апреля 2009

Язык не поддерживает многомерные объектно-ориентированные массивы, но вы можете создать класс, который делает это, используя NSMutableArray, полный NSMutableArrays, как показано ниже. Я не пытался скомпилировать это или что-то еще, я просто набрал его.

@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

Вы бы использовали это так:

SectionArray *a = [SectionArray arrayWithSections:10 rows:5];
[a setObject:@"something" inSection:4 row:3];
id sameOldSomething = [s objectInSection:4 row:3];
6 голосов
/ 07 июля 2013

какого черта. Пока мы возрождаем этот вопрос, вот что-то для эпохи синтаксиса буквального набора и интерпретации визуального формата!

Если кому-то интересно, это работает:

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]);

Результат: «О, привет, Джеймс!»

Конечно, существует проблема с попыткой чего-то вроде multi[3][5] = @"?" и получением недопустимого исключения индекса, поэтому я написал категорию для 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

Как вы можете видеть, у нас есть удобный метод для создания массива, заполненного NSNull, чтобы вы могли устанавливать индексы с полным упущением.

Во-вторых, есть рекурсивный метод, который анализирует строку в визуальном формате, например: [3][12] (массив 3 x 12). Если ваша строка каким-то образом неверна, метод вернет nil, но если она верна, вы получите целый многомерный массив указанных вами размеров.

Вот несколько примеров:

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

Вы можете передать сколько угодно измерений в метод визуального формата, а затем получить к ним доступ с помощью буквального синтаксиса, например:

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]);

Во всяком случае, сделал это больше как упражнение, чем что-либо еще; это, вероятно, довольно эффективно в большой схеме вещей ... учитывая, как мы создаем многомерные массивы указателей объектов Objective-C.

5 голосов
/ 17 марта 2011

Спасибо Джеку за его код, я немного поработал над ним; Мне нужен многомерный nsmutablearray для строк в одном из моих проектов, в нем все еще есть некоторые вещи, которые необходимо исправить, но он работает со строками только в данный момент, я опубликую его здесь, я открыт для предложений, потому что я только начинающий в объективе c на данный момент ...

@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

Это работает нормально !!!

Я сейчас использую это так

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);

Еще раз спасибо, Джек !!

0 голосов
/ 09 сентября 2013

Это то, что я сделал для инициализации массива массивов.

NSMutableArray *arrayOfArrays = [[NSMutableArray alloc] initWithCapacity:CONST];

for (int i=0; i<=CONST; i++) {
    [arrayOfArrays addObject:[NSMutableArray array]];
}

Тогда позже в коде я мог бы просто:

[[arrayOfArrays objectAtIndex:0] addObject:myObject];
0 голосов
/ 07 июля 2013

Восстановление старого потока, но я переработал код Джека так, чтобы 1. он компилировался и 2. он был в порядке двумерных массивов c [строк] [столбцов] вместо [разделов (столбцов)] [строк], как он имеет Это. Вот, пожалуйста!

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
0 голосов
/ 07 августа 2009

К вашему сведению: код Джека нуждается в куче работы, прежде чем он заработает. Среди других проблем, автоматическое освобождение может привести к тому, что данные будут освобождены, прежде чем вы получите к ним доступ, и его использование вызывает метод класса arrayWithSections: rows: когда он фактически определен как sectionArrayWithSections: row:

Я могу попытаться опубликовать фактический рабочий код позже, если у меня будет возможность.

...