我知道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
    

    对我上面的难题有什么解释吗?

    最佳答案

    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"
    }
    

    09-25 20:35