【问题标题】:Surprising Failures Putting a C Array Inside an Objective-C Struct Property将 C 数组放入 Objective-C 结构属性中的令人惊讶的失败
【发布时间】:2013-07-25 23:51:45
【问题描述】:

我知道 Objective-C 不允许您使用 C 数组作为属性类型。在这种情况下,我得到了预期的编译器错误。

但我对我在结构属性中看到的关于 C 数组的行为感到惊讶:

  • 没有编译错误或警告。
  • 地址本身的意外地址(gdb 的 info malloc 不知道它,不确定它是未初始化的内存还是什么。但我预计会发生崩溃或明显工作,尽管内存损坏)。
  • 分配变成了无操作。

我把这个示例代码归结为:

#import <Foundation/Foundation.h>

#ifndef sizeofA
    #define sizeofA(array) (sizeof(array)/sizeof(array[0]))
#endif

@interface IncludeCArrayDirectly : NSObject // Doesn't even compile
// Uncomment below to see the compilation error for yourself.
//@property(nonatomic, assign) int8_t f[9]; // ERROR: Property cannot have array or function type 'int8_t [9]'
@end

@interface IncludeCArrayInStruct : NSObject // Compiles (no warning) and runs but is amazingly broken.
@property(nonatomic, assign) int normalProperty;
@property(nonatomic, assign) struct { int f[9]; } p;
- (void*)normalPropertysAddress;
@end

@interface IncludeCArrayInIvar : NSObject {  // Totally works.
    @public
    int normalIvar;
    int8_t f[9];
}
@end

int main(int argc, const char *argv[]) {
    @autoreleasepool {
        {{
            IncludeCArrayInStruct *a = [IncludeCArrayInStruct new];

            // Notice a.p.f's address is off in 0x7fffxxxx-land:
            printf("&a = %p, &a.normalProperty = %p, a.p.f = %p\n",
                   a, [a normalPropertysAddress], a.p.f);

            printf("a.p.f[4] BEFORE %d\n", a.p.f[4]);
            a.p.f[4] = 42;
            printf("a.p.f[4] AFTER %d\n", a.p.f[4]);
            assert(a.p.f[4] == 0); // Surprise! Assertion passes. Assignment above is a no-op.

            // Dump all of a.p.f just to take a better look:
            for (unsigned i = 0; i < sizeofA(a.p.f); i++) {
                printf("a.p.f[%d] == %d\n", i, a.p.f[i]);
            }
        }}
        {{
            IncludeCArrayInIvar *b = [IncludeCArrayInIvar new];

            // All these addresses are about what you'd expect:
            printf("&b = %p, &b.normalIvar = %p, b.f = %p\n",
                   b, &b->normalIvar, b->f);

            printf("b->f[4] BEFORE %d\n", b->f[4]);
            b->f[4] = 42;
            printf("a->f[4] AFTER %d\n", b->f[4]);
            assert(b->f[4] == 42); // No surprise here, above assignment worked.

            // Dump all of b.f just to take a better look:
            for (unsigned i = 0; i < sizeofA(b->f); i++) {
                printf("b->f[%d] == %d\n", i, b->f[i]);
            }
        }}

    }
    return 0;
}


@implementation IncludeCArrayDirectly
@end

@implementation IncludeCArrayInStruct
- (void*)normalPropertysAddress {
    return &_normalProperty;
}
@end

@implementation IncludeCArrayInIvar
@end

对我上面的困惑点有什么解释吗?

【问题讨论】:

    标签: objective-c arrays properties ivar


    【解决方案1】:

    struct 对象总是按值复制,而不是按引用复制。这意味着当您的 struct 通过访问器方法返回时,该返回的对象是您的对象实例中的对象的 副本。我怀疑这来自 C,它在独立函数的场景中没有区别,它也共享该返回类型:

    struct sample
    {
        int arr[4];
    };
    
    struct sample FunctionThatReturnsSample(void)
    {
        static struct sample s = { { 0, 1, 2, 3 } };
        return s;
    }
    
    int main(void)
    {
        FunctionThatReturnsSample().arr[3] = 4;
    
        printf("%d\n", FunctionThatReturnsSample().arr[3]);
        // still prints "3"
    }
    

    【讨论】:

    • 啊,让属性返回结构的副本解释了明显的赋值无操作。谢谢!
    【解决方案2】:

    虽然这可能无法直接回答您的问题,但最有可能在这里找到答案:objc-accessors.mm

    快速浏览一下它揭示了合成 Objective-C 属性的机制,并且这个(有些令人不安的)标题注释似乎是相关的。

    // This entry point was designed wrong.  When used as a getter, src needs to be locked so that
    // if simultaneously used for a setter then there would be contention on src.
    // So we need two locks - one of which will be contended.
    void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong)
    

    【讨论】:

      猜你喜欢
      • 2022-06-11
      • 2017-04-10
      • 1970-01-01
      • 2018-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-29
      • 1970-01-01
      相关资源
      最近更新 更多