La cadena es NullOrEmpty como categoría
-
14-12-2019 - |
Pregunta
Estoy intentando crear un método que compruebe si hay una cadena nula/nula/vacía, y estoy intentando que funcione como una categoría, pero no tengo suerte.
Estoy usando este código, basado en las respuestas en este tema:
@implementation NSString (NSStringExtension)
- (BOOL)isNullOrEmpty {
return self == nil ||
self == (id)[NSNull null] ||
[@"" isEqualToString:self] ||
[[self stringByReplacingOccurrencesOfString:@" " withString:@""] length] == 0||
[self isEqualToString:@"(null)"]
|| ([self respondsToSelector:@selector(length)] && [(NSData *) self length] == 0)
|| ([self respondsToSelector:@selector(count)] && [(NSArray *) self count] == 0)
|| [[self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0;
}
@end
Sin embargo, cuando intento usar esto, esto es lo que obtengo:
NSLog([@"" isNullOrEmpty] ? @"1":@"0"); // prints 1
NSString *s1 = nil;
NSLog([s1 isNullOrEmpty] ? @"1":@"0"); // prints 0
NSLog([args.itemName isNullOrEmpty] ? @"1":@"0"); // prints 0
NSLog([(NSString*)nil isNullOrEmpty] ? @"1":@"0"); // prints 0
Esto me desconcierta, y sólo puedo suponer que alguna combinación de iOS5/ARC está provocando que el objeto nulo sea forzado a una cadena/puntero en blanco.El depurador muestra la cadena como 0x0, pero cuando uso mi isNullOrEmpty
método, me sale falso.
Solución
return self == nil
Esto nunca puede suceder.Si intentas enviar isNullOrEmpty
(o cualquier otro mensaje) a nil
, no pasa nada (objc_msgSend()
, la función responsable del envío de mensajes, comprueba si hay un nil
receptor como una de las primeras cosas que hace y aborta).
self == (id)[NSNull null]
Esto tampoco sucederá nunca.Si tu envías isNullOrEmpty
a un objeto que es una instancia de NSNull
, su método aquí, que es un método en NSString
, no será llamado.En cambio, NSNull
La versión (que probablemente no existe) será.
Asimismo, ([self respondsToSelector:@selector(count)] && [(NSArray *) self count])
nunca va a suceder.Si el objeto es un NSArray
, entonces isNullOrEmpty
nunca se ejecutará, porque, nuevamente, es un método de NSString
.
En consecuencia, [(NSData *) self length]
no hace lo que crees que hace. NSString
instancias hacer responder a length
, pero lanzando el objeto a NSData
no usa el NSData
versión del método: todavía termina como la NSString
versión de length
, porque el objeto en realidad es un NSString
(La transmisión solo ocurre en tiempo de compilación;no puede cambiar nada en tiempo de ejecución).
[self isEqualToString:@"(null)"]
Aquí parece que estás comprobando nil
otra vez, pero estás siendo engañado por el representación eso NSLog
elige cuando imprime nil
:
NSLog(@"%@", nil);
Esto muestra (null)
en la consola, pero eso no significa que el objeto en sí sea una cadena con esos caracteres. NSLog
simplemente elige esa cadena para mostrar nil
.*
Varias de las cosas que estás haciendo requerirían que esto esté en una categoría en NSObject
, de modo que el método haría de hecho ser llamado incluso si el objeto no era un NSString
.
Para verificar una cadena que consta solo de espacios en blanco, todo lo que necesita es la comparación con la cadena vacía @""
después de recortar los espacios en blanco:
NSString * trimmedSelf = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
// Then either:
[trimmedSelf isEqualToString:@""];
// Or:
([trimmedSelf length] == 0);
*Y aún mejor, haciendo NSLog(@"%@", [NSNull null]);
muestra <null>
(corchetes angulares en lugar de paréntesis), maravillosamente confuso las primeras veces que te encuentres NSNull
.
Otros consejos
Otro enfoque puede ser definir una macro simple.
#define NSStringIsNullOrEmpty(str) ((str==nil) || [(str) isEqualToString:@""])
Es simple y efectivo.Si no te gustan las macros, siempre puedes convertirlas en una llamada de función sin afectar el resto de tu código.
-- Actualizar:
@Bryan ha planteado un buen punto.Una función en línea es una excelente manera de hacerlo.Aquí hay una macro actualizada que evaluará str solo una vez.
#define NSStringIsNullOrEmpty(str) ({ NSString *_str=(str); ((tmp==nil) || [tmp isEqualToString:@""]);})
En Objective-C, enviar un mensaje a cero siempre devolverá 0 (o NO
, una estructura puesta a cero, NULL, etc., según el tipo de retorno declarado).El isNullOrEmpty
El método que usted escribió en realidad no será invocado cuando envíe isNullOrEmpty
a nil
.Vea la respuesta aceptada a ¿Enviando un mensaje a cero? para más información.
Quizás podrías cambiar tu método para ser isNotNullOrEmpty
.Luego un valor de retorno de 0 al enviar isNotNullOrEmpty
a nil
tendrá sentido.
No estás llamando a tu método, pero enviando un mensaje a cero.
Este es el comportamiento esperado.Estás enviando un mensaje a nil
después de todo.Por lo tanto, devuelve nil (o algún otro valor 0).Qué cortocircuitos a falso para que se imprima '0' en los casos que se muestran a continuación:
NSLog([s1 isNullOrEmpty] ? @"1":@"0"); // prints 0
NSLog([(NSString*)nil isNullOrEmpty] ? @"1":@"0"); // prints 0
Incluso puede confirmar que su mensaje no se llama en esos casos estableciendo un punto de interrupción en su nuevo método de categoría.
Como otros han dicho, llamar [nil isNullOrEmpty];
En realidad, no ejecutará su método.El nil
objeto es solo eso:vaciarse.
Como solución, me gustaría decir que no es porque estés en un lenguaje orientado a objetos que nunca debes usar funciones.
BOOL IsStringNilOrEmpty(NSString*)str
{
return str == nil ||
str == null ||
[@"" isEqualToString:str] ||
[[str stringByReplacingOccurrencesOfString:@" " withString:@""] length] == 0||
[str isEqualToString:@"(null)"]
|| ([str respondsToSelector:@selector(length)] && [(NSData *) str length] == 0)
|| ([str respondsToSelector:@selector(count)] && [(NSArray *) str count] == 0)
|| [[str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0;
}
De hecho, acabo de solucionar este problema dándole la vuelta así
-(BOOL) isNotNullOrWhiteSpace
{
return [self length] != 0 && [[self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] != 0;
}
entonces, en lugar de isNullOrWhiteSpace, es isNotNullOrWhiteSpace.
Este es mi método para verificar nulo/vacío
-(nsstring*) nullinputinitWithString: (nsstring*) inputString {
if( (InputString == nil) ||(InputString ==(NSString*)[NSNull null])||([InputString isEqual:nil])||([InputString length] == 0)||([InputString isEqualToString:@""])||([InputString isEqualToString:@"(NULL)"])||([InputString isEqualToString:@"<NULL>"])||([InputString isEqualToString:@"<null>"]||([InputString isEqualToString:@"(null)"])||([InputString isEqualToString:@"NULL"]) ||([InputString isEqualToString:@"null"])))
return @"";
else
return InputString ;
}
¿Has pensado en crear un método de clase en una categoría que extienda NSString?
NSString+NSStringExtensions.h
#import <Foundation/Foundation.h>
@interface NSString(NSStringExtensions)
+(BOOL)isNilOrEmpty:(NSString*)string;
@end
NSString+NSStringExtensions.m
#import "NSString+NSStringExtensions.h"
@implementation NSString(NSStringExtensions)
+(BOOL)isNilOrEmpty:(NSString*)string
{
if (nil == string)
{
return YES;
}
if (string.length == 0)
{
return YES;
}
return NO;
}
@end
Entonces lo usas así:
#import "NSString+NSStringExtensions.h"
...
NSLog([NSString isNilOrEmpty:@""] ? @"1":@"0");