【问题标题】:Objective-C ARC and passing C arrays of objectsObjective-C ARC 和传递 C 对象数组
【发布时间】:2012-03-26 09:44:02
【问题描述】:

如果这是一个 C 菜鸟问题,我很抱歉:我知道我需要认真对待我的指针。不幸的是,我在最后期限,所以没有时间完成整本书的章节,所以我希望得到更有针对性的建议。

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

我将存储一个三维 C 数组:从概念上讲,我的维度是天、高度和 cacheNumber。每个元素要么是一个指向 Objective-C 对象的指针,要么是 NULL。

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

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

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

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

我的问题:

  • 我应该按什么顺序排列我的维度,以便我可以轻松地返回一个指向特定缓存编号的子数组的指针? (我觉得应该是第一,但我不是100%。)

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

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

  • 当我(为我的 ivar)创建 3D 数组时,我想我会执行类似 calloc(X * Y * Z, sizeof(id)) 的操作,并将结果转换为我的 ivar 的类型?

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

  • 我从方法返回的二维数组会被类似访问吗?

  • 是否需要将返回的二维数组标记为objc_returns_inner_pointer

再次抱歉,这是一个有点糟糕的 Stack Overflow 问题(它太长且包含太多部分)。我希望SO公民能原谅我。为了提高我的互联网业力,也许我会在这个项目发布后将它写成一篇博文。

【问题讨论】:

  • this page 上找到了一个示例(“我可以在 ARC 下创建一个保留指针的 C 数组吗?”,靠近底部),我试图从中推断。如果有人希望发布答案,我仍然非常感谢您的澄清:)

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


【解决方案1】:

首先:虽然您没有NSPointerArray,但您确实CFMutableArrayRef,您可以传递任何您想要保留/释放/描述的回调,包括NULL。首先尝试一下可能更容易(而且性能是您可以稍后衡量的)。

按顺序计算你的分数:

  • 您应该将您的尺寸定义为[cacheNumber][days][height],正如您所期望的那样。那么cache[cacheNumber] 是一个id *[][] 类型的二维数组。正如你所说的性能很重要,请注意迭代这个野兽的最快方法是:

    for (/* cacheNumber loop */) {
     for (/* days loop */) {
      for (/* height loop */) {
       //...
      }
     }
    }
    
  • 它应该是__strong id ***类型:这是一个指向id的指针的指针,它和array of (array of (pointer to id))相同。

  • 您的 ivar 必须是 __strong id **** (!),因为它是上述内容的数组。
  • 您对分配数组的猜测不正确。如果您使用的是多维数组,则需要这样做(为简洁起见,省略一维):

    - (__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;
    }
    
  • 正确,你就是这样使用这种指针的。

  • 是的,二维数组的工作方式相同,没有第一个维度。
  • 我不这么认为,你正在分发__strong 对象指针,所以你应该是盛大的。也就是说,我们现在在这方面的能力已经接近极限,所以我很可能是错的。

【讨论】:

    【解决方案2】:

    回答我自己的问题,因为this web page 给了我所需的缺失信息。我也赞成 Graham 的回答,因为他对我理解一些语法很有帮助。

    我缺少的技巧是知道如果我想通过 array[1][5][2] 语法引用数组中的项目,并且我在编译时不知道数组的大小,我不能只是 @ 987654323@单块数据给它。

    最容易阅读(虽然效率最低)的方法就是使用循环:

    __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 类。 (我当然在这个 sn-p 中省略了错误处理代码。)

    完成后,我可以使用方括号语法引用我的数组:

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

    我在上面链接到的网页还描述了执行内存分配的第二种方法,每个维度只使用一次对 calloc() 的调用。我还没有尝试过这种方法,因为我刚才描述的方法目前运行良好。

    【讨论】:

      【解决方案3】:

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

      在我看来,您需要一个中间容器对象,我们现在将其称为Cache。每个缓存号都将存在一个实例,并且您的对象将包含它们的 NS(Mutable)Array。 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 示例将非常适合您。

      【讨论】:

      • 哦,这绝对是一个明显的性能问题。我花了数周时间倾注在 Instruments 上,试图分析每一帧发生的事情,以便让 UI 响应。这是核心逻辑的第四次重写,旨在将我能从设备中获得的所有速度都挤出来。
      • 与对象的 C 数组相比,缓存原始属性值的 C 数组可能会更好。
      • @jeremyP:多维下标依赖于具有数组数组的数组,而不是具有归因于索引的额外含义的大型单维数组。我不认为你的句子“你只能使用……”是正确的。
      • @Graham Lee:我是对的。如果您声明一个数组int myarray[5][5][5],编译器将分配一大块内存并使用算术对其进行索引。它不会创建指向指针的指针数组。与流行的神话相反,数组不是 C 中的指针。
      • @Graham:提问者希望动态分配一大块 RAM。我同意编译器不能做我在上一条评论中所说的,这就是重点!
      猜你喜欢
      • 2012-08-27
      • 1970-01-01
      • 1970-01-01
      • 2014-06-19
      • 1970-01-01
      • 1970-01-01
      • 2014-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多