سؤال

I know Objective-C doesn't allow you to use C arrays as property types. I get the compiler error I expect in that case.

But I'm surprised at the behavior I'm seeing with regards to C arrays inside struct properties:

  • No compilation errors or warnings.
  • Unexpected address of the address itself (gdb's info malloc doesn't know about it, not sure if it's uninitialized memory or what. But I would expect a crash or apparently-working albeit with memory corruption).
  • Assignment becomes a no-op.

I boiled it down this example code:

#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

Any explanations to my puzzle points above?

هل كانت مفيدة؟

المحلول

struct objects are always copied by value, not by reference. This means that when your struct is returned via an accessor method, that returned object is a copy of the one in your object instance. I suspect this comes from C where it makes no difference in the scenario of a standalone function that also shares that return type:

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

نصائح أخرى

While this may not directly answer your question, the answer is most likely found here: objc-accessors.mm

A quick peek at it reveals the mechanisms by which Objective-C properties are synthesized, and this (somewhat troubling) header comment seems relevant.

// 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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top