Objective-C ARC и передача c массивов объектов

мне жаль, если это немного вопрос C-noob: я знаю, что мне нужно swot на моих указателях. К сожалению, у меня крайний срок, поэтому у меня нет времени работать над целой главой книги, поэтому я надеюсь на более целенаправленный совет.

Я хочу сохранить некоторые объекты objective-C в массиве C. Я использую ARC. Если бы я был на Mac, я мог бы использовать nspointerarray вместо этого, но я на iOS, и это недоступно.

Я буду хранить трехмерный массив C: концептуально мои размеры день, высота и cacheNumber. Каждый элемент будет либо указателем на объект objective-C, либо NULL.

количество кэшей (т. е. размер измерения cacheNumber) известно во время компиляции, но два других неизвестны. Кроме того, массив может быть очень большим, поэтому мне нужно динамически выделять для него память.

что касается семантики владения, мне нужны сильные ссылки на объекты.

Я бы весь трехмерный массив, являющийся переменной экземпляра объекта objective-C.

Я планирую иметь метод, который - tableForCacheNumber:(int)num days:(int*)days height:(int*)height. Этот метод должен возвращать двумерный массив, то есть один конкретный номер кэша. (Он также передает по ссылке размер возвращаемого массива.)

мои вопросы:

  • в каком порядке я должен поместить свои измерения, чтобы я мог легко вернуть указатель на поддерево для одного конкретного номера кэша? (Я думаю, что должен быть первым, но я не на 100%.)

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

  • каким типом должна быть переменная экземпляра, содержащая трехмерный массив? Я думаю, что это должен быть просто указатель, так как этот ivar просто представляет указатель на первый элемент, который находится в моем массиве. Правильно? Если да, то как это уточнить?

  • когда я создаю трехмерный массив (для моего Ивара), я думаю, что я делаю что-то вроде calloc(X * Y * Z, sizeof(id)), и приведите результат к типу для моего ivar?

  • при доступе к элементам из трехмерного массива в ivar, я считаю, что я должен разыменовать указатель каждый раз, с чем-то вроде (*myArray)[4][7][2]. Правильно?

  • будет ли двумерный массив, который я возвращаю из метода получить аналогичный доступ?

  • мне нужно пометить возвращаемый двумерный массив с помощью objc_returns_inner_pointer?

мне жаль еще раз, что это немного плохой вопрос переполнения стека (он слишком длинный и с слишком большим количеством частей). Надеюсь, сограждане простят меня. Чтобы улучшить мою карму в интернете, возможно, я напишу об этом в блоге, когда этот проект будет отправлен.

3 ответов


во-первых: пока у вас нет NSPointerArray, вы do есть CFMutableArrayRef и вы можете передать любые обратные вызовы, которые вы хотите для сохранения / выпуска / описания, включая NULL. Это может быть проще (и производительность-это то, что вы можете измерить позже), чтобы попробовать это сначала.

принимая ваши очки в порядке:

  • вы должны определить свои размеры как [cacheNumber][days][height], как вы ожидаете. Тогда cache[cacheNumber] представляет собой двумерный массив типа id *[][]. Как вы сказали производительность важна, имейте в виду, что самый быстрый способ итерации этого зверя:

    for (/* cacheNumber loop */) {
     for (/* days loop */) {
      for (/* height loop */) {
       //...
      }
     }
    }
    
  • он должен быть типа __strong id ***: это указатель на указатель на указатель на id, что совпадает с array of (array of (указатель на id)).

  • ваш Ивар должен быть __strong id **** (!), потому что это массив вышеуказанных вещей.
  • вы неправильно догадываетесь о выделении массива.. Если вы используете многомерный массив, вам нужно сделать это (одно измерение elided для краткости):

    - (__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;
    }
    
  • правильно, вот как вы используете такой указатель.

  • да, массив two-D работает одинаково,sans первое измерение.
  • я так не думаю, ты выдаешь __strong указатели объектов, поэтому вы должны быть великолепны. Тем не менее, мы сейчас на пределе моих возможностей с этим материалом, поэтому я вполне мог бы быть неправильный.

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

трюк, который мне не хватало, - это знать, что если я хочу ссылаться на элементы в массиве через array[1][5][2] синтаксис, и что я не знаю размер моего массива во время компиляции, я не могу просто calloc() один блок данных.

самый простой для чтения (хотя и наименее эффективный) способ сделать это просто с петлей:

__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 *));
    }
}

Я выделяю трехмерный массив Item *s,Item быть классом objective-C. (Я, конечно, опустил код обработки ошибок в этом фрагменте.)

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

cacheItems[zoom][day][heightToUse] = item;

веб-страница, с которой я связан выше, также описывает второй метод для выполнения распределения памяти, который использует только один звонок в calloc() по каждому измерению. Я еще не пробовал этот метод, так как тот, который я только что описал, работает достаточно хорошо на данный момент.


Я бы подумал о другой реализации. Если это не доказанная (т. е. вы измерили и количественно оценили ее) проблема производительности, попытка сохранить объекты Objective-C в простых массивах C часто является запахом кода.

мне кажется, что вам нужен промежуточный объект контейнера, который мы будем называть Cache сейчас. Один экземпляр будет существовать для каждого номера кэша, и ваш объект будет содержать массив NS(изменяемый)из них. Cache объекты будут иметь свойства максимальные дни и высота.

объект кэша легче всего реализовать с помощью NSArray объектов в нем, используя простую арифметику для имитации двух измерений. Ваш объект кэша будет иметь метод -objectAtDay:Height: для доступа к объекту по его координатам.

таким образом, нет необходимости беспокоиться об управлении памятью, ARC делает это за вас.

редактировать

учитывая, что производительность является проблемой, я бы использовал массив 1D и сверните мою собственную арифметику, чтобы вычислить смещения. Тип переменной экземпляра будет:

__strong id* myArray;

вы можете использовать только многоуровневые индексы C (array[i][j][k]) если вы знаете диапазон всех измерений (кроме первого). Это происходит потому, что фактическое смещение рассчитывается как

(i * (max_j * max_k) + j * max_k + k) * sizeof(element type)

если компилятор не знает, max_j и max_k, он не сможет. Именно в вашей ситуации.

учитывая, что вы должны использовать массив 1D и рассчитать смещения вручную, пример Apple будет работать нормально для вас.