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:
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).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"
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With