Czy istnieje wbudowana metoda, funkcja, interfejs API, powszechnie przyjęty sposób itp. W celu zrzucenia zawartości instancji obiektu w Objective-C, szczególnie w środowisku Apple Cocoa / Cocoa-Touch?
Chcę móc zrobić coś takiego
MyType *the_thing = [[MyType alloc] init];
NSString *the_dump = [the_thing dump]; //pseudo code
NSLog("Dumped Contents: %@", the_dump);
i wyświetlają nazwy i wartości zmiennych instancji obiektu, wraz z metodami dostępnymi do wywołania w czasie wykonywania. Idealnie w czytelnym formacie.
Dla programistów zaznajomionych z PHP, zasadniczo szukam odpowiednika funkcji odbicia ( var_dump()
, get_class_methods()
) i API OO Reflection.
objective-c
cocoa
xcode
reflection
introspection
Alan Storm
źródło
źródło
Odpowiedzi:
AKTUALIZACJA: Każdy, kto chce robić tego typu rzeczy, może chcieć sprawdzić opakowanie ObjC Mike'a Asha dla środowiska wykonawczego Objective-C .
Mniej więcej tak możesz to zrobić:
#import <objc/runtime.h> . . . -(void)dumpInfo { Class clazz = [self class]; u_int count; Ivar* ivars = class_copyIvarList(clazz, &count); NSMutableArray* ivarArray = [NSMutableArray arrayWithCapacity:count]; for (int i = 0; i < count ; i++) { const char* ivarName = ivar_getName(ivars[i]); [ivarArray addObject:[NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding]]; } free(ivars); objc_property_t* properties = class_copyPropertyList(clazz, &count); NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count]; for (int i = 0; i < count ; i++) { const char* propertyName = property_getName(properties[i]); [propertyArray addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]]; } free(properties); Method* methods = class_copyMethodList(clazz, &count); NSMutableArray* methodArray = [NSMutableArray arrayWithCapacity:count]; for (int i = 0; i < count ; i++) { SEL selector = method_getName(methods[i]); const char* methodName = sel_getName(selector); [methodArray addObject:[NSString stringWithCString:methodName encoding:NSUTF8StringEncoding]]; } free(methods); NSDictionary* classDump = [NSDictionary dictionaryWithObjectsAndKeys: ivarArray, @"ivars", propertyArray, @"properties", methodArray, @"methods", nil]; NSLog(@"%@", classDump); }
Stamtąd łatwo jest uzyskać rzeczywiste wartości właściwości instancji, ale musisz sprawdzić, czy są to typy prymitywne lub obiekty, więc byłem zbyt leniwy, aby je wprowadzić. Możesz także przeskanować łańcuch dziedziczenia, aby pobierz wszystkie właściwości zdefiniowane w obiekcie. Są też metody zdefiniowane na kategoriach i nie tylko ... Ale prawie wszystko jest łatwo dostępne.
Oto fragment tego, co zrzuca powyższy kod dla UILabel:
{ ivars = ( "_size", "_text", "_color", "_highlightedColor", "_shadowColor", "_font", "_shadowOffset", "_minFontSize", "_actualFontSize", "_numberOfLines", "_lastLineBaseline", "_lineSpacing", "_textLabelFlags" ); methods = ( rawSize, "setRawSize:", "drawContentsInRect:", "textRectForBounds:", "textSizeForWidth:", . . . ); properties = ( text, font, textColor, shadowColor, shadowOffset, textAlignment, lineBreakMode, highlightedTextColor, highlighted, enabled, numberOfLines, adjustsFontSizeToFitWidth, minimumFontSize, baselineAdjustment, "_lastLineBaseline", lineSpacing, userInteractionEnabled ); }
źródło
NSObject
kategorią). Jednak trzeba sięfree()
swoimiivars
,properties
imethods
tablic, albo jesteś ich przecieka.Krótki z
description
metodą (jak .ToString () w Javie), nie słyszałem od jednego, który został zbudowany w, ale to nie byłoby zbyt trudne, aby je utworzyć. Dokumentacja Objective-C Runtime zawiera szereg funkcji, których można użyć do uzyskania informacji o zmiennych instancji obiektu, metodach, właściwościach itp.źródło
Oto, czego obecnie używam do automatycznego drukowania zmiennych klas w bibliotece do ostatecznego publicznego wydania - działa poprzez zrzucanie wszystkich właściwości z klasy instancji do kopii zapasowej drzewa dziedziczenia. Dzięki KVC nie musisz się martwić, czy właściwość jest typem pierwotnym, czy nie (dla większości typów).
// Finds all properties of an object, and prints each one out as part of a string describing the class. + (NSString *) autoDescribe:(id)instance classType:(Class)classType { NSUInteger count; objc_property_t *propList = class_copyPropertyList(classType, &count); NSMutableString *propPrint = [NSMutableString string]; for ( int i = 0; i < count; i++ ) { objc_property_t property = propList[i]; const char *propName = property_getName(property); NSString *propNameString =[NSString stringWithCString:propName encoding:NSASCIIStringEncoding]; if(propName) { id value = [instance valueForKey:propNameString]; [propPrint appendString:[NSString stringWithFormat:@"%@=%@ ; ", propNameString, value]]; } } free(propList); // Now see if we need to map any superclasses as well. Class superClass = class_getSuperclass( classType ); if ( superClass != nil && ! [superClass isEqual:[NSObject class]] ) { NSString *superString = [self autoDescribe:instance classType:superClass]; [propPrint appendString:superString]; } return propPrint; } + (NSString *) autoDescribe:(id)instance { NSString *headerString = [NSString stringWithFormat:@"%@:%p:: ",[instance class], instance]; return [headerString stringByAppendingString:[self autoDescribe:instance classType:[instance class]]]; }
źródło
Zrobiłem kilka poprawek w kodzie Kendall do drukowania wartości właściwości, co bardzo mi się przydało. Zdefiniowałem to jako metodę instancji zamiast metody klasowej, ponieważ tak nazywa ją rekursja nadklasy. Dodałem również obsługę wyjątków dla właściwości niezgodnych z KVO i dodałem podziały wierszy do wyjścia, aby ułatwić czytanie (i porównywanie):
-(NSString *) autoDescribe:(id)instance classType:(Class)classType { NSUInteger count; objc_property_t *propList = class_copyPropertyList(classType, &count); NSMutableString *propPrint = [NSMutableString string]; for ( int i = 0; i < count; i++ ) { objc_property_t property = propList[i]; const char *propName = property_getName(property); NSString *propNameString =[NSString stringWithCString:propName encoding:NSASCIIStringEncoding]; if(propName) { @try { id value = [instance valueForKey:propNameString]; [propPrint appendString:[NSString stringWithFormat:@"%@=%@\n", propNameString, value]]; } @catch (NSException *exception) { [propPrint appendString:[NSString stringWithFormat:@"Can't get value for property %@ through KVO\n", propNameString]]; } } } free(propList); // Now see if we need to map any superclasses as well. Class superClass = class_getSuperclass( classType ); if ( superClass != nil && ! [superClass isEqual:[NSObject class]] ) { NSString *superString = [self autoDescribe:instance classType:superClass]; [propPrint appendString:superString]; } return propPrint; }
źródło
Szczerze mówiąc, odpowiednim narzędziem do tego zadania jest debugger Xcode. Wszystkie te informacje są łatwo dostępne w wizualny sposób. Poświęć trochę czasu, aby nauczyć się go używać, to naprawdę potężne narzędzie.
Więcej informacji:
Korzystanie z debugera
Nieaktualny przewodnik debugowania Xcode - zarchiwizowany przez Apple
Informacje o debugowaniu za pomocą Xcode - zarchiwizowane przez Apple
Informacje o LLDB i debugowaniu - zarchiwizowane przez Apple
Debugowanie za pomocą GDB - zarchiwizowane przez Apple
Przewodnik debugowania SpriteKit - zarchiwizowany przez Apple
Debugowanie tematów programowania dla Core Foundation - zarchiwizowane przez Apple
źródło
Zrobiłem z tego cocoapod, https://github.com/neoneye/autodescribe
Zmodyfikowałem kod Christophera Pickslaya i uczyniłem go kategorią na NSObject, a także dodałem do niego unittest. Oto jak z niego korzystać:
@interface TestPerson : NSObject @property (nonatomic, strong) NSString *firstName; @property (nonatomic, strong) NSString *lastName; @property (nonatomic, strong) NSNumber *age; @end @implementation TestPerson // empty @end @implementation NSObject_AutoDescribeTests -(void)test0 { TestPerson *person = [TestPerson new]; person.firstName = @"John"; person.lastName = @"Doe"; person.age = [NSNumber numberWithFloat:33.33]; NSString *actual = [person autoDescribe]; NSString *expected = @"firstName=John\nlastName=Doe\nage=33.33"; STAssertEqualObjects(actual, expected, nil); } @end
źródło
Wcześniej jestem mylony z introspekcją i refleksją, więc uzyskaj poniżej kilka informacji.
Introspekcja to zdolność obiektu do sprawdzenia, jakiego typu jest, protokołu, który jest zgodny, lub selektora, na który może odpowiedzieć. Interfejs API objc, taki jak
isKindOfClass
/isMemberOfClass
/conformsToProtocol
/respondsToSelector
itp.Zdolność do refleksji jest czymś więcej niż introspekcja , Nie tylko może uzyskać informacje o obiekcie, ale także może obsługiwać metadane, właściwości i funkcje obiektu. takie jak
object_setClass
mogą modyfikować typ obiektu.źródło