2013-07-25 9 views
5

So che Objective-C non consente di utilizzare i C array come tipi di proprietà. Ottengo l'errore del compilatore che mi aspetto in quel caso.Errori sorprendenti che mettono un array C all'interno di una proprietà Objective-C Struct

Ma io sono sorpreso per il comportamento che sto vedendo per quanto riguarda gli array C all'interno proprietà struct:

  • errori di compilazione o avvisi.
  • Indirizzo inatteso dell'indirizzo stesso (gdb's info malloc non lo sa, non è sicuro se si tratta di memoria non inizializzata o cosa, ma mi aspetterei un arresto anomalo o apparentemente funzionante anche se con danneggiamento della memoria).
  • L'assegnazione diventa non operativo.

ho bollito giù questo codice di esempio:

#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 

spiegazioni al mio puzzle di punti di cui sopra?

risposta

9

struct scopi vengono sempre copiati per valore, non per riferimento. Ciò significa che quando il tuo struct viene restituito tramite un metodo accessor, quell'oggetto restituito è una copia di quella nell'istanza dell'oggetto. Ho il sospetto che questo viene da C dove non fa alcuna differenza nello scenario di una funzione autonoma che anche le azioni che tipo di ritorno:

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

Ah, avendo la proprietà restituire una copia della struct spiega l'assegnazione apparente no-op. Grazie! – rentzsch

0

Mentre questo non può rispondere direttamente alla tua domanda, la risposta è più probabile trovare qui: objc-accessors.mm

una rapida occhiata a esso rivela i meccanismi attraverso i quali Objective-C Proprietà sono sintetizzati, e questa intestazione (un po 'preoccupante) il commento sembra pertinente.

// 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)