Metoda opisu ogólnego Objective-C do drukowania wartości ivar
W Objective-C, powszechne jest nadpisywanie -description
metodą, która wypisuje ID obiektu i nazwy zmiennych/wartości instancji. Chciałbym stworzyć ogólną metodę -description
, która robi to poprzez introspekcję, a nie ręcznie robi to dla każdej klasy. Chciałbym żeby wyjście było coś takiego:
<ClassName: 0x??????, ivar1: value1, ivar2: value2, ivar3: value3, ...>
Byłoby również miło sortować według nazwy zmiennej instancji(więc zawsze są one w tej samej kolejności). Wreszcie, jeśli można to umieścić w kategorii bezpiecznie nadrzędnej NSObject
funkcjonalność, która byłaby idealna (ale widzę, że nie jest to proste, ponieważ {[4] } ma ostrzeżenie o używaniu -[NSString stringWithFormat:]
W -description
).
2 answers
To jest świetne pytanie! Ponieważ nie znalazłem czegoś takiego, napisałem małą funkcję, która robi to za Ciebie. Zastępuje - (NSString *)description
z NSObject
metodą swizzling (yes, I ' m this evil. Mahahahahahahahahahahaha) i drukuje ivar w kolejności, w jakiej pojawiają się również w klasie (można je łatwo edytować, aby wyświetlić je w kolejności alfabetycznej).
NSObjectSwizzleDescription()
!
.plik h:
@interface NSObject (JSObjectAdditions)
@end
void NSObjectSwizzleDescription();
.plik m:
#import <objc/objc.h>
#import "JSObject.h"
@implementation NSObject (JSObjectAdditions)
- (NSString *)verboseDescription
{
NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: %p>", NSStringFromClass([self class]), self];
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([self class], &ivarCount);
if(ivars)
{
[description appendString:@"\n{"];
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
const char *ivarType = ivar_getTypeEncoding(ivar);
id ivarObject = object_getIvar(self, ivar);
[description appendFormat:@"\n %s: ", ivar_getName(ivar)];
// Default signed data types
if(strcmp(ivarType, "c") == 0)
{
char character = (char)ivarObject;
[description appendFormat:@"'%c'", character];
}
else if(strcmp(ivarType, "i") == 0 || strcmp(ivarType, "l") == 0) // l is also 32 bit in the 64 bit runtime environment
{
int integer = (int)ivarObject;
[description appendFormat:@"%i", integer];
}
else if(strcmp(ivarType, "s") == 0)
{
short shortVal = (short)ivarObject;
[description appendFormat:@"%i", (int)shortVal];
}
else if(strcmp(ivarType, "q") == 0)
{
long long longVal = (long long)ivarObject;
[description appendFormat:@"%l", longVal];
}
// Default unsigned data types
else if(strcmp(ivarType, "C") == 0)
{
unsigned char chracter = (unsigned char)ivarObject;
[description appendFormat:@"'%c'", chracter];
}
else if(strcmp(ivarType, "I") == 0 || strcmp(ivarType, "L") == 0)
{
unsigned int integer = (unsigned int)ivarObject;
[description appendFormat:@"%u", integer];
}
else if(strcmp(ivarType, "S") == 0)
{
unsigned short shortVal = (unsigned short)ivarObject;
[description appendFormat:@"%i", (int)shortVal];
}
else if(strcmp(ivarType, "Q") == 0)
{
unsigned long long longVal = (unsigned long long)ivarObject;
[description appendFormat:@"%ll", longVal];
}
// Floats'n'stuff
else if(strcmp(ivarType, "f") == 0)
{
float floatVal;
memcpy(&floatVal, &ivarObject, sizeof(float));
[description appendFormat:@"%f", floatVal];
}
else if(strcmp(ivarType, "d") == 0)
{
double doubleVal;
memcpy(&doubleVal, &ivarObject, sizeof(double));
[description appendFormat:@"%f", doubleVal];
}
// Boolean and pointer
else if(strcmp(ivarType, "B") == 0)
{
BOOL booleanVal = (BOOL)ivarObject;
[description appendFormat:@"%@", (booleanVal ? @"YES" : @"NO")];
}
else if(strcmp(ivarType, "v") == 0)
{
void *pointer = (void *)ivarObject;
[description appendFormat:@"%p", pointer];
}
else if(strcmp(ivarType, "*") == 0 || strcmp(ivarType, ":") == 0) // SEL is just a typecast for a cstring
{
char *cstring = (char *)ivarObject;
[description appendFormat:@"\"%s\"", cstring];
}
else if(strncmp(ivarType, "@", 1) == 0)
{
[description appendFormat:@"%@", ivarObject];
}
else if(strcmp(ivarType, "#") == 0)
{
Class objcClass = (Class)ivarObject;
[description appendFormat:@"%s", class_getName(objcClass)];
}
else
[description appendString:@"???"];
}
[description appendString:@"\n}"];
free(ivars);
}
return description;
}
@end
void NSObjectSwizzleDescription()
{
Method origMethod = class_getInstanceMethod([NSObject class], @selector(description));
Method newMethod = class_getInstanceMethod([NSObject class], @selector(verboseDescription));
method_exchangeImplementations(origMethod, newMethod);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-11 19:27:10
Nie znam żadnego kodu robiącego to, ale jeśli to istnieje, byłbym zainteresowany w celach debugowania!
Dziwię się, że nie znalazłem jeszcze czegoś takiego, klasyczne techniki po prostu działają, ale rzeczywiście byłoby miło mieć jakąś metodę inspect
, która jest, jak sądzę, możliwa za pomocą refleksji, ale może się mylę.
Proponuję rzucić okiem na ten ciekawy post , który jest związany z tym, czego szukasz, i może być podstawą do wdrożenia to.
Kolejną ciekawą rzeczą jest również NSLogger , który jest fajnym narzędziem do debugowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:53:09