Objective-C ARC和传递C对象数组

时间:2012-03-26 09:44:02

标签: objective-c ios automatic-ref-counting

如果这是一个C-noob问题,我很抱歉:我知道我需要注意我的指针。不幸的是,我正处于截止日期,所以没有时间完成整本书的章节,所以我希望有更多有针对性的建议。

我想在C数组中存储一些Objective-C对象。我正在使用ARC。如果我在Mac上,我可以使用NSPointerArray,但我在iOS上并且不可用。

我将存储一个三维C数组:从概念上讲,我的维度是day,height和cacheNumber。每个元素都是指向Objective-C对象的指针,或者是NULL。

高速缓存的数量(即cacheNumber维度的大小)在编译时是已知的,但其他两个是未知的。此外,阵列可能非常大,所以我需要为它动态分配内存。

关于所有权语义,我需要对对象的强引用。

我希望整个三维数组成为Objective-C对象的实例变量。

我计划有一个- tableForCacheNumber:(int)num days:(int*)days height:(int*)height的方法。该方法应该返回一个二维数组,即一个特定的缓存号。 (它还通过引用传回它返回的数组的大小。)

我的问题:

  • 我应该以什么顺序放置我的尺寸,以便我可以轻松地返回指向一个特定缓存编号的子阵列的指针? (我认为应该是第一,但我不是100%。)

  • 我的方法的返回类型应该是什么,以便ARC不抱怨?我不介意返回的数组是否有增加的引用计数,只要我知道它正在做什么。

  • 我的实例变量应该包含哪个类型的三维数组?我认为它应该只是一个指针,因为ivar只表示指向我数组中第一个项目的指针。正确?如果是,我该如何指定?

  • 当我创建三维数组(对于我的ivar)时,我想我会做calloc(X * Y * Z, sizeof(id))之类的事情,并将结果转换为我的ivar类型?

  • 当从ivar中的三维数组访问项目时,我相信我每次都必须取消引用指针,例如(*myArray)[4][7][2]。正确的吗?

  • 我是否会同样访问从方法返回的二维数组?

  • 我是否需要使用objc_returns_inner_pointer标记返回的二维数组?

我再次感到遗憾,这是一个糟糕的Stack Overflow问题(它太长而且部分太多)。我希望SO公民会原谅我。为了提高我的互联网业力,也许我会在这个项目发布时将其写成博客文章。

3 个答案:

答案 0 :(得分:5)

首先关闭:当你没有NSPointerArray时,你CFMutableArrayRef,你可以传递任何你想要的保留/释放/描述回调,包括{ {1}}。可能更容易(并且性能是您可以稍后测量的)首先尝试。

按顺序记下你的观点:

  • 您应该按照预期将尺寸定义为NULL。然后[cacheNumber][days][height]是类型为cache[cacheNumber]的二维数组。正如您所说的表现很重要,请注意迭代此野兽的最快方法是:

    id *[][]
  • 它应该是for (/* cacheNumber loop */) { for (/* days loop */) { for (/* height loop */) { //... } } } 类型:这是指向指向__strong id ***的指针的指针,它与(数组)(指向{{1}的指针的数组)相同}))。

  • 你的ivar需要id(!),因为它是上述事物的一系列。
  • 你猜错了关于分配数组的问题。如果你正在使用多维数组,你需要这样做(为简洁而省略了一个维度):

    id
  • 正确,就是你如何使用这样的指针。

  • 是的,二维数组以相同的方式工作, sans 第一维。
  • 我不这么认为,你是在分发__strong id ****对象指针,所以你应该是伟大的。也就是说,现在我们的能力已达到极限,所以我可能错了。

答案 1 :(得分:3)

回答我自己的问题,因为this web page给了我一些我需要的信息。我也赞成格雷厄姆的答案,因为他非常乐于帮助我理解一些语法。

我缺少的技巧是知道如果我想通过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()调用。我还没有尝试过这种方法,因为我刚才描述的方法目前运作良好。

答案 2 :(得分:0)

我会想到一个不同的实现。除非它是可证明的(即你已经测量并量化了)性能问题,否则尝试将Objective-C对象存储在普通C数组中通常会产生代码味道。

在我看来,你需要一个中间容器对象,我们现在称之为Cache。每个缓存号都有一个实例,你的对象将保存一个NS(Mutable)数组。 Cache个对象将具有最大天数和高度的属性。

Cache对象最容易使用其中的对象的NSArray实现,使用简单的算法来模拟两个维度。您的缓存对象将有一个方法-objectAtDay:Height:来通过其坐标访问该对象。

这样,根本不需要担心内存管理,ARC会为你做这件事。

修改

鉴于性能是一个问题,我会使用一维数组并滚动我自己的算法来计算偏移量。实例变量的类型为:

__strong id* myArray;

如果您知道所有维度的范围(第一个维度除外),则只能使用C多级下标(array[i][j][k])。这是因为实际偏移量计算为

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

如果编译器不知道max_j和max_k,则无法执行此操作。这正是你所处的情况。

鉴于您必须使用一维数组并手动计算偏移量,Apple示例将适合您。