2011-09-30 15 views
20

Quiero crear un método como este:objetivo-c: ¿doble punteros a la propiedad no permitidos?

- (void) checkAndUpdateStringProperty: (NSString **) property{ 
    if (...) 
     *property = @"whatever"; 
} 

quiero usar esto para pasar en una propiedad de la misma clase:

- (void) somewhereElse{ 
     .... 
     [self checkAndUpdateStringProperty: &self.productName]; 
} 

Pero esto da un error de sintaxis, diciendo "Dirección de expresión de propiedad solicitada ". ¿Alguna razón por qué? Si se sustituye la propiedad de clase con una variable local, que funciona bien:

- (void) somewhereElse{ 
     NSString *name = nil; 
     [self checkAndUpdateStringProperty: &name]; 
} 

Respuesta

26

propiedades deben ser accedidos solamente siempre a través de sus métodos get y set, con la excepción de inicialización y Desinicializando en init y dealloc los métodos del objeto (cuando el objeto no está en un estado totalmente coherente) - acceder a ellos cualquier otra manera derrota su propósito y no respeta sus atributos (retain, copy, etc.).

La razón por la que no se compila es porque los descriptores de acceso a propiedades son azúcar sintáctico para las llamadas a métodos, y no puede tomar la dirección del valor de retorno de un método. self.productName es idéntico a [self productName], y &[self productName] no tiene sentido.

Si realmente quiere hacer esto, no lo haga con una propiedad, lo hace con sólo una variable normal (como una variable local o Ivar), y documento de la semántica de su función: hace lo función de llamada necesita release el objeto devuelto? Etc. Por ejemplo, los diversos métodos de Apple que toman un NSError** especifican que el objeto de error que se devuelve, si lo hay, es un objeto liberado automáticamente, por lo que no debería hacerlo usted mismo a menos que también lo retain.

+1

la excepción a acceder a Ivars propiedad está directamente en '' dealloc' init' y donde la clase no está en una forma totalmente completa y cualquier efecto secundario podría ser un problema. – zaph

+0

Buen punto, actualizado. –

+2

Gracias. Realmente descubrí que (dado que este era un NSManagedObject) podía pasar el nombre de la propiedad y acceder al valor usando valueForKey, y configurarlo dentro del método 'check' usando setValue: forKey: No era necesario hacer la indirección de doble puntero . –

7

Hay muy pocos lugares en las API de Apple que un valor se pasa de esta manera, siendo la principal NSError. Lo mejor es simplemente devolver el resultado. Si tiene un parámetro de referencia, la regla general de Apple es que debe ser el último parámetro que el nombre del método comience con "get" como en getCheckAndUpdateStringProperty.

Pero puede hacerlo, simplemente no es a lo que estamos acostumbrados.

El error que está viendo es que self.productName es realmente la abreviatura de la llamada al método: [self productName] por lo &self.productName se interpreta como &[self productName].

Además en este caso si productName es @property con un atributo de retención, el recuento de referencias se subvertirá.

5

Si está realmente cuidado acerca de los atributos de propiedad, puede obtener alrededor de este problema mirando a lo siguiente:

[self property] 

vs:

self.property 

vs:

self->property 

Una vez que tenga en claro la diferencia, puede intentar esto:

[self checkAndUpdateStringProperty: &self->productName] 

Nota el uso de: ->

lo uso todo el tiempo cuando el atributo de propiedad es asignar y cuando se trata de un tipo de objeto primitivo, no: (float, int. ..)

+0

'self-> productName' es una muy mala idea, rompe la encapsulación. – zaph

0

crear una propiedad de esa variable, a la que desea acceder en otra clase (@property (fuerte, no atómica);) y luego sintetizarla .... (@synthesize;) y luego acceder a través de nombre del objeto de esa clase, donde define la propiedad y la sintetiza .....

+0

Formatea tu respuesta para que quede claro. – NAZIK

0

me gustaría hacer esto utilizando los selectores de cambio (que tendrá que convertirlo en función de CA para conseguir que funcione sin previo aviso (ver here):

- (void) checkAndUpdateStringProperty: (SEL)setter { 
    if (...) 
    IMP imp = [self methodForSelector:setter]; 
    void (*func)(id, SEL, id) = (void *)imp; 
    func(self, setter, @"whatever"); 
} 

Use esto para pasar en el selector para el misma clase:

- (void) somewhereElse{ 
     .... 
     [self checkAndUpdateStringProperty:@selector(setProductName:)]; 
} 
Cuestiones relacionadas