42

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

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

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?

ответ

88

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

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

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

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

id table[lnum][rnum]; 
table[i][j] = myObj; 
+24

+1 для 'id' типа в прямых массивов C. – Tim

+4

Отличное предложение использовать простые массивы C, особенно когда размеры массива известны заранее. – timbo

+0

+1 для включения синтаксиса чтения и записи. –

17

Если вы хотите сделать это с помощью массивов, вы можете инициализировать свой 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

Язык не поддерживает многомерные объектно-ориентированные массивы, но вы можете создать класс, который делает это, используя 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]; 
0

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

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

5

Спасибо Джеку за его код, я немного поработал над ним; Мне нужен многомерный 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); 

Еще раз спасибо Jack !!

0

Воспроизведение старой темы, но я переработал код Джека, так что 1. он компилирует и 2. он находится в порядке 2D-массивов [rows] [columns] вместо [section (columns)] [rows], поскольку он есть это. Ну вот!

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 
+1

Если вы собираетесь возродить этот старый поток, по крайней мере, используйте современные вещи, такие как синтаксис литерала, некоторые хорошие ссылки здесь: http://stackoverflow.com/a/13482653/700471 Я думаю, вы можете даже сделать что-то вроде ' myArray [0] [3] ', хотя я мог ошибаться. –

+0

@MattMc: Спасибо за ссылку! Я довольно неопытен в этом, просто подумал, что поделюсь тем, что сработало для меня. Конечно, проверить это. –

+0

Уверенная вещь :) Я принял мое собственное предложение и бросил вместе метод, который включает в себя литеральный синтаксис и так далее, чтобы вы могли получить представление; это ниже. –

6

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

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

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.

+1

Для коллекции с отверстиями я использую 'NSMutableDictionary', где ключ имеет такие индексы, как это:' [dict setObject: o forKey: @ [@ 2, @ 5]] 'Таким образом, вы не получите' NSNull' везде – Tricertops

+0

@iMartin Хм, что это делает, когда вы вызываете 'setObject' с массивом в качестве ключа? Он устанавливает объект как значение для каждой клавиши в массиве? Кроме того, я думаю, вы хотите сделать '@ (2)', чтобы сделать его числом, я не думаю, что он действителен без круглых скобок. –

+0

Он действителен без '()'. Если вы используете массив чисел в качестве ключа, объект связывается с указанной комбинацией индексов. Еще одно. Это просто нормальный ключ. – Tricertops

-1

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

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

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

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

[[arrayOfArrays objectAtIndex:0] addObject:myObject]; 
+0

1st: вы не добавляете ничего нового и ценного в эту тему. 2nd: если мы говорим о 2-мерных массивах с помощью NSArrays, его нужно добавить, как обрабатывать пустые записи. – vikingosegundo