¿Cuándo debería usar @synthesize explícitamente?

81

Hasta donde yo sé, desde XCode 4.4, se @synthesizegenerarán automáticamente los descriptores de acceso de propiedad. Pero acabo de leer una muestra de código sobre NSUndoManager, y en el código noté que @synthesizese agrega explícitamente. Me gusta:

@interface RootViewController  ()

@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@property (nonatomic, strong) NSUndoManager *undoManager;

@end

@implementation RootViewController
//Must explicitly synthesize this
@synthesize undoManager;

Me siento desconcertado ahora ... ¿Cuándo debo agregar @synthesizeexplícitamente a mi código?

罗泽轩
fuente
1
Es posible que el código de muestra sea antiguo. Básicamente, utilícelo a menos que se convierta en un problema (por ejemplo, las propiedades de los delegados no se sintetizarán automáticamente)
borrrden
1
Intente comentar el @sythesize. Si el código aún funciona, entonces no es necesario.
ThomasW

Respuestas:

171

Hay muchas respuestas, pero también una gran confusión. Intentaré poner algo de orden (o aumentar el lío, ya veremos ...)

  1. Dejemos de hablar de Xcode. Xcode es un IDE . clang es un compilador . Esta característica que estamos discutiendo se llama autosíntesis de propiedades y es una extensión del lenguaje Objective-C compatible con clang , que es el compilador predeterminado utilizado por Xcode.
    Solo para que quede claro, si cambia a gcc en Xcode, no se beneficiará de esta función (independientemente de la versión de Xcode). De la misma manera, si usa un editor de texto y compila usando clang desde la línea de comando, será.

  2. Gracias a la autosíntesis, no es necesario sintetizar explícitamente la propiedad, ya que el compilador la sintetizará automáticamente como

    @synthesize propertyName = _propertyName
    

    Sin embargo, existen algunas excepciones:

    • propiedad readwrite con getter y setter personalizados

      al proporcionar tanto una implementación personalizada get y set, la propiedad no se puede sintetizar de forma automática

    • propiedad de solo lectura con getter personalizado

      al proporcionar una implementación de captador personalizado para una propiedad de solo lectura, esto no se sintetizará automáticamente

    • @dinámica

      cuando se usa @dynamic propertyName, la propiedad no se sintetizará automáticamente (bastante obvio, ya que @dynamicy @synthesizeson mutuamente excluyentes)

    • propiedades declaradas en un @protocolo

      cuando se ajusta a un protocolo, cualquier propiedad que defina el protocolo no se sintetizará automáticamente

    • propiedades declaradas en una categoría

      este es un caso en el que la @synthesizedirectiva no es insertada automáticamente por el compilador, pero estas propiedades tampoco se pueden sintetizar manualmente. Si bien las categorías pueden declarar propiedades, no se pueden sintetizar en absoluto, ya que las categorías no pueden crear ivars. En aras de la integridad, agregaré que todavía es posible falsificar la síntesis de propiedades utilizando el tiempo de ejecución de Objective-C .

    • propiedades anuladas (nuevo desde clang-600.0.51, envío con Xcode 6, gracias Marc Schlüpmann)

      cuando anula una propiedad de una superclase, debe sintetizarla explícitamente

Vale la pena señalar que sintetizar una propiedad sintetiza automáticamente el ivar de respaldo, por lo que si falta la síntesis de la propiedad, también faltará el ivar, a menos que se declare explícitamente.

Excepto en los últimos tres casos, la filosofía general es que siempre que especifique manualmente toda la información sobre una propiedad (implementando todos los métodos de acceso o usando @dynamic) el compilador asumirá que desea un control total sobre la propiedad y deshabilitará la autosíntesis en eso.

Aparte de los casos que se enumeran anteriormente, el único otro uso de un explícito @synthesizesería especificar un nombre de ivar diferente. Sin embargo, las convenciones son importantes, por lo que mi consejo es usar siempre la denominación predeterminada.

Gabriele Petronella
fuente
Si el autor de la pregunta original todavía está aquí, creo que esta respuesta debería aceptarse. Es el más completo.
Steven Fisher
3
Las propiedades especificadas en una categoría tampoco se sintetizan automáticamente, por lo que recuerdo. (La razón subyacente es que no puede agregar una variable de instancia en una categoría).
Martin R
@MartinR, buen punto. No se sintetizan automáticamente, pero tampoco se pueden sintetizar manualmente ya que @synthesizeen una categoría está prohibido (no ivar en las categorías, como ya ha señalado). Agregaré una nota.
Gabriele Petronella
Pero esta respuesta no aborda la pregunta específica: ¿CUÁNDO debería usarse @synthesize? ¿Siempre, nunca, solo cuando se cumplen determinadas condiciones?
Jeff
21

Si no utiliza explícitamente, @synthesizeel compilador entenderá su propiedad de la misma manera si hubiera escrito

@synthesize undoManager=_undoManager;

entonces podrás escribir en tu código cosas como:

[_undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

Ésta es la convención común.

Si tú escribes

@synthesize undoManager;

usted tendrá :

[undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

Personalmente dejo de usar @synthesize, ya que ya no es obligatorio. Para mí, la única razón para usarlo @synthesizees vincular un iVara un @property. Si desea generar getter y setter específicos para ello. Pero en el código dado no hay iVar, creo que esto @synthesizees inútil. Pero ahora creo que la nueva pregunta es "¿Cuándo usar iVar?", ¡Y no tengo otra respuesta que "nunca" para esta!

KIDdAe
fuente
2
De acuerdo, dejé de usar @synthesizeporque ya no hay razón para hacerlo. Además, el subrayado inicial sirve como una bonita bandera visual para hacerle saber que está trabajando en torno a la gestión de memoria y la red de seguridad de subprocesos que proporcionan las propiedades (que debería omitir inity deallocmétodos).
BergQuester
1
La pregunta era cuándo es necesario sintetizar explícitamente. No has respondido eso en absoluto.
Fogmeister
1
Hoy descubrí que esto no es del todo cierto. Sin @synthesize, eres responsable de crear la variable de instancia de respaldo. Con él, el compilador lo hará por ti.
Steven Fisher
1
Y no voté en contra de nadie por este descubrimiento. :)
Steven Fisher
1
-1 te estás perdiendo por completo los casos en los que necesitas una síntesis explícita. Además, esta es una característica del compilador, no de Xcode.
Gabriele Petronella
14

¿Cuándo debo agregar @synthesizeexplícitamente a mi código?

Generalmente, si es necesario: Probablemente nunca encontrará un caso donde sea necesario.

Sin embargo, hay un caso que puede resultarle útil.

Supongamos que está escribiendo tanto un getter como un setter personalizados, pero desea que una variable de instancia lo respalde. (Para una propiedad atómica, esto es tan simple como querer un setter personalizado: el compilador escribirá un getter si especificas un setter para una propiedad monoatómica, pero no una propiedad atómica).

Considera esto:

@interface MyObject:NSObject
@property (copy) NSString *title;
@end

@implementation MyObject

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Esto no funcionará porque _titleno existe. Ha especificado tanto un getter como un setter, por lo que Xcode (correctamente) no crea una variable de instancia de respaldo para él.

ingrese la descripción de la imagen aquí

Tienes dos opciones para hacerlo existir. Puede cambiar el@implementation a esto:

@implementation MyObject {
    NSString *_title;
}

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

O cámbielo a esto:

@implementation MyObject

@synthesize title = _title;

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

En otras palabras, aunque sintetizar nunca es necesario para fines prácticos *, se puede utilizar para definir variables de instancia de respaldo de propiedad cuando se proporciona un captador / definidor. Aquí puede decidir qué formulario desea utilizar.

En el pasado, prefería especificar la variable de instancia en el @implementation {}, pero ahora creo que@synthesize ruta es una mejor opción, ya que elimina el tipo redundante y vincula explícitamente la variable de respaldo a la propiedad:

  1. Cambie el tipo de propiedad y el tipo de variable de instancia cambia.
  2. Cambie su calificador de almacenamiento (por ejemplo, hágalo débil en lugar de fuerte o fuerte en lugar de débil) y el calificador de almacenamiento cambia.
  3. Elimine o cambie el nombre de la propiedad y @synthesizegenerará un error del compilador. No acabará con variables de instancia extraviadas.

* -Conozco un caso en el que fue necesario, relacionado con la división de funciones en categorías en varios archivos. Y no me sorprendería si Apple soluciona esto, o incluso si ya lo ha hecho.

Steven Fisher
fuente
¿Estás seguro? ¿Lo intentaste siquiera? Escribí muchos setters y getters personalizados y NUNCA sinteticé mis propiedades. (Y usé su primer ejemplo, que dijo que no funciona)
Marc
Sí estoy seguro. El código se copió y pegó de un nuevo proyecto. No funcionará en versiones recientes de Xcode a menos que especifique no atómico en la propiedad.
Steven Fisher
1
sí, eso es cierto ... pero el 99% de las veces sus propiedades no son atómicas. Por lo tanto, solo en el raro caso de que su propiedad sea atómica y desee un getter / setter personalizado que realmente necesite sintetizar.
Marc
Si proporciona una implementación personalizada tanto para setter como para getter, el compilador asumirá que usted está tomando el control de la propiedad y no la sintetizará por usted.
Gabriele Petronella
2
Estoy de acuerdo contigo en eso, aunque puedo entender por qué Apple inicialmente pensó que era una buena idea. El verdadero problema es que atomicno se agregó como especificador de propiedad hasta más tarde. Ahora que está allí, puede que la bandera de advertencia le resulte CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESinteresante.
Steven Fisher
7

OK, cuando creas una propiedad ...

@property NSString *name;

Xcode sintetizará automáticamente una iVar como si hubiera escrito ...

@synthesize name = _name;

Esto significa que puede acceder a la propiedad con ...

self.name;
// or
_name;

Cualquiera de los dos funcionará, pero solo self.namerealmente usa los métodos de acceso.

Solo hay una vez que la síntesis automática no funciona: si sobrescribe pero el método setter Y getter, necesitará sintetizar el iVar.

Está bien si simplemente anula el setter o si simplemente anula el getter. Pero si hace ambas cosas, el compilador no lo entenderá y necesitará sintetizarlo manualmente.

Sin embargo, como regla general.

No hagas iVars. Solo usa la propiedad. No lo sintetices.

Fogmeister
fuente
1
Hay más casos en los que no se realizará la autosíntesis. Mira mi respuesta.
Gabriele Petronella
@GabrielePetronella ¿puede enumerar esos casos de manera sucinta para el lector promedio?
Dan Rosenstark
@DanRosenstark, esto es irrelevante para cualquiera que esté programando ahora. Realmente deberías usar Swift. Y TBH, creo que todo el asunto de la síntesis ya ni siquiera es una cosa en ObjC. A menos que esté trabajando en una base de código que tenga más de 5 años.
Fogmeister
@Fogmeister sí, sigue siendo una cosa en el caso que mencionas en tu respuesta (donde anulas tanto al setter como al getter). Y en cuanto a que Objective-C no es "relevante para nadie que programe ahora", llame a mi trabajo diario y dígales. También dígaselo a Facebook, Google y Apple, que están usando Objective-C internamente con gran efecto.
Dan Rosenstark
Estoy seguro de que esto es cierto sin Quora, pero de todos modos: quora.com/…
Dan Rosenstark
1

La síntesis de propiedad es necesaria cuando una propiedad se declara en un protocolo. No se sintetizará automáticamente en una interfaz de implementación.

Leo Natan
fuente
cierto, pero es solo un caso. Es posible que desee elaborar más.
Gabriele Petronella
@GabrielePetronella Es el único en el que puedo pensar en esta hora tardía. =]
Leo Natan
0

Gracias por aclarar eso. Tuve un problema similar.

@synthesize firstAsset, secondAsset, audioAsset;
@synthesize activityView;

Así que ahora, habiéndolos comentado, revisé y reemplacé cada ocurrencia con, por ejemplo

self.firstAsset Parece que también podría usar firstAsset, pero encuentro que extraño ver el " " con demasiada frecuencia.

Harry McGovern
fuente
-1

Xcode no requiere una @synthesizedeclaración explícita .

Si no escribe, @synthesizees lo mismo que hacer:

@synthesize manager = _manager;

Es posible que el código de muestra sea antiguo. Lo actualizarán pronto.

Puede acceder a sus propiedades como:

[self.manager function];

Esta es la convención recomendada por Apple. Lo sigo y te recomiendo que lo hagas tú también.

Sam Fischer
fuente
2
Statement [_manager function]NO accederá a la propiedad, sino que accederá directamente al ivar subyacente .
CouchDeveloper
@CouchDeveloper Si eres quisquilloso, debes ser preciso. La propiedad consta de un par de cosas, incluido el ivar. Por eso es mejor decir que [_manager function]no utiliza accesos de la propiedad.
Nikolai Ruhe
@CouchDeveloper - ¡Gracias por eso! ¡Arreglado! :)
Sam Fischer
1
@NikolaiRuhe Tienes razón Nikolai, debería haber sido más preciso. ;)
CouchDeveloper