2010-02-19 26 views
77

Özellikle Apple'ın Kakao/Kakao-Touch ortamında, Objective C'deki örneklenmiş bir nesnenin içeriğini dökmek için yerleşik bir yöntem, işlev, API, yaygın olarak kabul edilen bir yol var mı?Amaç C Gözlemleme/Yansıma

Ben

MyType *the_thing = [[MyType alloc] init]; 
NSString *the_dump = [the_thing dump]; //pseudo code 
NSLog("Dumped Contents: %@", the_dump); 

gibi bir şey yapabilmek istiyorum ve nesnenin örnek değişken adları ve değerleri çalışma zamanında çağrısı için herhangi yöntemlerle birlikte görüntülenir var. İdeal olarak okunması kolay bir formatta.

PHP'yi bilen geliştiriciler için temel olarak yansıma işlevlerinin (var_dump(), get_class_methods()) ve OO Yansıtma API'sinin eşdeğerini arıyorum.

+0

Aynı sorunun birkaç gün olduğunu merak ediyorum. Bu soru için teşekkür ederim. –

+7

Evet, harika bir soru. ObjC'nin diğer benzer dillere göre en büyük avantajlarından biri, bunun gibi harika şeyler yapmanıza olanak tanıyan inanılmaz dinamik çalışma zamanı sistemidir. Ne yazık ki, insanlar nadiren onu tam potansiyeline kullanırlar, bu yüzden SO topluluğunu bu konuyla ilgili öğretmenin kudosudur. – rpj

+0

Yansıma ile ilgilenmek için hafif bir kütüphane oluşturdum [OSReflectionKit] (https://github.com/iAOS/OSReflectionKit). Bu kütüphaneyi kullanarak [the_thing fullDescription] 'ı arayabilirsiniz. –

cevap

111

GÜNCELLEME: bu tarz şeyleri yapmak isteyen Herkes Mike Ash's ObjC wrapper for the Objective-C runtime kontrol etmek isteyebilirsiniz. Oradan

#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); 
} 

, bir örneğin özelliklerinin gerçek değerleri elde etmek kolay, ama onlar ilkel olup olmadığını görmek için kontrol etmek zorunda:

Bu daha fazla veya bu konuda gitmek istiyorum nasıl azdır türleri veya nesneleri, ben de koymak için çok tembel oldu. Ayrıca bir nesne üzerinde tanımlanan özellikleri tüm almak için miras zincirini taramak için seçebilirsiniz. Daha sonra kategoriler üzerinde tanımlanmış yöntemler var ve daha fazlası ... Ama hemen hemen her şey hazır. o tüm özelliklerini damping çalışır -

İşte
{ 
    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 
    ); 
} 
+6

+1, bunu nasıl yapacağımı oldukça iyi anladım (bir NSObject kategorisi haline getirin). Bununla birlikte, 'ivar'larınız,' özellik'leriniz ve 'yöntemler 'dizilerinizi serbest bırakmanız ya da onları sızdırıyor olmanız gerekir. –

+0

Woops, bunu unuttu. Şimdi içine koy. Teşekkürler. – Felixyz

+4

Ivarların değerlerini görüyor muyuz? –

12

description yönteminden (Java'daki .toString() gibi) kısa bir süre içinde, yerleşik olanı duymamıştım, ancak bir tane oluşturmak çok zor olmaz. The Objective-C Runtime Reference vb bir nesnenin örnek değişkenleri, yöntemleri, özellikleri hakkında bilgi almak için kullanabileceğiniz işlevlerin bir grup var

+0

+1, sadece aradığım şey. Bu, önceden oluşturulmuş bir çözümü umduğum için, hızlıca birlikte hackleyeceğim herhangi bir şeyin dilin bazı ustalıklarını kaçırabileceğine inandığım kadar yeni bir dil olmasını umuyordum. –

+6

Eğer bir cevabı reddedecekseniz, lütfen niçin bir yorum bıraktığınızı belirtin. –

8

Şu anda nihai halka serbest bırakılması için bir kütüphanede, otomatik sınıf değişkenleri yazdırmak için kullanıyorum ne: Burada

Yukarıdaki kod UILabel için döker ne bir alıntı örnek sınıf, miras ağacını tüm yollarla destekliyor. KVC'ye teşekkürler, bir özellik ilkel bir tür olup olmadığına (çoğu tür için) dikkat etmeniz gerekmez.

// 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]]]; 
} 
+0

+1 çok faydalıdır, ancak soruya sadece kısmen cevap verir. Kütüphaneyi serbest bıraktığınızda buraya bir yorum ekler misiniz? – Felixyz

+0

Evet, buna ekleyeceğim ... –

+0

+1 Bu harika, çok teşekkürler. – prototypical

3

Dürüst olmak gerekirse, bu iş için doğru araç Xcode'un hata ayıklayıcısıdır. Tüm bu bilgilere görsel olarak kolayca erişilebilmektedir. Nasıl kullanacağınızı öğrenmek için zaman ayırın, bu gerçekten güçlü bir araçtır. Daha fazla bilgi için: Xcode Debugging Guide.

+4

+1, ancak bazen bir nesnenin durumunu iki noktaya dökmek daha hızlıdır ve çıktıyı, bir programın durumuna tek bir noktadan başlayarak, daha hızlıdır. –

4

Kendall'ın benim için çok kullanışlı olan özellik değerlerini yazdırmaya yönelik koduna birkaç kez tweaks yaptım. Bunu bir sınıf yöntemi yerine örnek bir yöntem olarak tanımladım, çünkü bu, süper sınıf özyineleme yöntemini böyle çağırır. Ayrıca sigara KVO uyumlu özellikleri için taşıma istisna eklendi ve ilave hat daha kolay okumak (ve fark) yapmak için çıkış kırar:

-(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; 
} 
+0

BÜYÜK BİLGİLER !!! - Yansıma'yı kullanarak bulduğunuzda gerçek özelliği nasıl ayarlayacağınız hakkında bir fikriniz var mı? Burada bir sorum var: http://stackoverflow.com/q/25538890/1735836 – Patricia

1

Ben cocoapod bu işten yaptık https://github.com/neoneye/autodescribe

Christopher Pickslay'ın kodunu değiştirdim ve NSObject üzerinde bir kategori yaptım ve buna bir de ekledi.Nasıl kullanılır:

@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