objective c - Diferencia entre nullable,__nullable y_Nullable en Objective-C
objective-c-nullability (4)
Con Xcode 6.3, se introdujeron nuevas anotaciones para expresar mejor la intención de las API en
Objective-C
(y para garantizar un mejor soporte de Swift, por supuesto).
Esas anotaciones eran, por supuesto, no
nonnull
,
null_unspecified
y
null_unspecified
.
Pero con Xcode 7, aparecen muchas advertencias como:
Al puntero le falta un especificador de tipo de nulabilidad (_Nnnull, _Nullable o _Null_unspecified).
Además de eso, Apple usa otro tipo de especificadores de nulabilidad, marcando su código C ( source ):
CFArrayRef __nonnull CFArrayCreate( CFAllocatorRef __nullable allocator, const void * __nonnull * __nullable values, CFIndex numValues, const CFArrayCallBacks * __nullable callBacks);
En resumen, ahora tenemos estas 3 anotaciones de nulabilidad diferentes:
-
nonnull
,nullable
,null_unspecified
-
_Nonnull
,_Nullable
,_Null_unspecified
-
__nonnull
,__nullable
,__null_unspecified
Aunque sé por qué y dónde usar qué anotación, me confundo un poco qué tipo de anotaciones debo usar, dónde y por qué. Esto es lo que podría reunir:
-
Para las propiedades, debería usar
nonnull
,null_unspecified
,null_unspecified
. -
Para los parámetros del método, debería usar
nonnull
,null_unspecified
,null_unspecified
. -
Para los métodos C, debería usar
__nonnull
,__nullable
,__null_unspecified
. -
Para otros casos, como los punteros dobles, debería usar
_Nonnull
,_Nullable
,_Null_unspecified
.
Pero todavía estoy confundido sobre por qué tenemos tantas anotaciones que básicamente hacen lo mismo.
Entonces mi pregunta es:
¿Cuál es la diferencia exacta entre esas anotaciones, cómo colocarlas correctamente y por qué?
De la
documentation
clang
:
Los calificadores de nulabilidad (tipo) expresan si un valor de un tipo de puntero dado puede ser nulo (el calificador
_Nullable
), no tiene un significado definido para nulo (el calificador_Nonnull
), o si el propósito de nulo no está claro (el_Null_unspecified
Debido a que los calificadores de nulabilidad se expresan dentro del sistema de tipos, son más generales que losnonnull
yreturns_nonnull
, lo que permite expresar (por ejemplo) un puntero anulable a una matriz de punteros no nulos. Los calificadores de nulabilidad se escriben a la derecha del puntero al que se aplican.
y
En Objective-C, hay una ortografía alternativa para los calificadores de nulabilidad que se pueden usar en métodos y propiedades de Objective-C utilizando palabras clave sensibles al contexto y sin subrayar
Por lo tanto, para las devoluciones de métodos y los parámetros, puede usar las versiones con doble guión
__nonnull
/
__nullable
/
__null_unspecified
lugar de las guiones con guiones bajos o con guiones bajos.
La diferencia es que los subrayados simples y dobles deben colocarse después de la definición del tipo, mientras que los que no están subrayados deben colocarse antes de la definición del tipo.
Por lo tanto, las siguientes declaraciones son equivalentes y son correctas:
- (nullable NSNumber *)result
- (NSNumber * __nullable)result
- (NSNumber * _Nullable)result
Para parámetros:
- (void)doSomethingWithString:(nullable NSString *)str
- (void)doSomethingWithString:(NSString * _Nullable)str
- (void)doSomethingWithString:(NSString * __nullable)str
Para propiedades:
@property(nullable) NSNumber *status
@property NSNumber *__nullable status
@property NSNumber * _Nullable status
Sin embargo, las cosas se complican cuando se involucran dobles punteros o bloques que devuelven algo diferente a vacío, ya que los que no están subrayados no están permitidos aquí:
- (void)compute:(NSError * _Nullable * _Nullable)error
- (void)compute:(NSError * __nullable * _Null_unspecified)error;
// and all other combinations
Similar a los métodos que aceptan bloques como parámetros, tenga en cuenta que el calificador no
nonnull
/
nullable
se aplica al bloque y no a su tipo de retorno, por lo tanto, los siguientes son equivalentes:
- (void)executeWithCompletion:(nullable void (^)())handler
- (void)executeWithCompletion:(void (^ _Nullable)())handler
- (void)executeWithCompletion:(void (^ __nullable)())handler
Si el bloque tiene un valor de retorno, entonces se te obliga a una de las versiones de subrayado:
- (void)convertObject:(nullable id __nonnull (^)(nullable id obj))handler
- (void)convertObject:(id __nonnull (^ _Nullable)())handler
- (void)convertObject:(id _Nonnull (^ __nullable)())handler
// the method accepts a nullable block that returns a nonnull value
// there are some more combinations here, you get the idea
Como conclusión, puede usar cualquiera de los dos, siempre que el compilador pueda determinar el elemento al que asignar el calificador.
Del blog de Swift :
Esta característica se lanzó por primera vez en Xcode 6.3 con las palabras clave __nullable y __nonnull. Debido a posibles conflictos con bibliotecas de terceros, las hemos cambiado en Xcode 7 a _Nullable y _Nnnull que ves aquí. Sin embargo, para la compatibilidad con Xcode 6.3, hemos predefinido las macros __nullable y __nonnull para expandir a los nuevos nombres.
Muy útil es
NS_ASSUME_NONNULL_BEGIN
y cerrando con
NS_ASSUME_NONNULL_END
Esto anulará la necesidad del nivel de código ''nullibis'' :-), ya que tiene sentido suponer que
todo
no es nulo (o no nulo o
nonnull
o
_nonnull
__nonnull
) a menos que se indique lo contrario.
Lamentablemente, hay excepciones a esto también ...
-
no se asume que
typedef
s es__nonnull
(nota, nononnull
no parece funcionar, tiene que usar su medio hermano feo) -
id *
necesita un nullibi explícito pero wow the sin-tax (_Nullable id * _Nonnull
<- adivina lo que eso significa ...) -
NSError **
siempre se supone nulable
Entonces, con las excepciones a las excepciones y las palabras clave inconsistentes que provocan la misma funcionalidad, ¿quizás el enfoque sea usar las versiones feas
__nonnull
/
__nullable
/
__null_unspecified
e intercambiar cuando el cumplidor se queja ...?
¿Quizás es por eso que existen en los encabezados de Apple?
Curiosamente, algo lo puso en mi código ... Aborrezco los subrayados en el código (tipo de la vieja escuela Apple C ++), así que estoy absolutamente seguro de que no escribí estos pero aparecieron (un ejemplo de varios):
typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential * __nullable credential );
Y aún más interesante, donde insertó el __nullable está mal ... (¡eek @!)
Realmente desearía poder usar la versión sin subrayado, pero aparentemente eso no vuela con el compilador ya que esto se marca como un error:
typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential * nonnull credential );
Realmente me gustó este artículo , así que simplemente estoy mostrando lo que escribió el autor: https://swiftunboxed.com/interop/objc-nullability-annotations/
-
null_unspecified:
puentes a un Swift opcional envuelto implícitamente. Este es el valor predeterminado . -
nonnull
: el valor no será nulo; puentes a una referencia regular. -
nullable
: el valor puede ser nulo; puentes a un opcional. -
null_resettable
: el valor nunca puede ser nulo cuando se lee, pero puede establecerlo en nulo para restablecerlo. Solo se aplica a propiedades.
Las anotaciones anteriores, luego difieren si las usa en el contexto de propiedades o funciones / variables:
El autor del artículo también proporcionó un buen ejemplo:
// property style
@property (nonatomic, strong, null_resettable) NSString *name;
// pointer style
+ (NSArray<NSView *> * _Nullable)interestingObjectsForKey:(NSString * _Nonnull)key;
// these two are equivalent!
@property (nonatomic, strong, nullable) NSString *identifier1;
@property (nonatomic, strong) NSString * _Nullable identifier2;