Objective-C、具体的にはAppleのCocoa/Cocoa-Touch環境でインスタンス化されたオブジェクトのコンテンツをダンプするための組み込みのメソッド、関数、API、一般に受け入れられている方法などはありますか?
次のようなことができるようになりたい
_MyType *the_thing = [[MyType alloc] init];
NSString *the_dump = [the_thing dump]; //pseudo code
NSLog("Dumped Contents: %@", the_dump);
_
オブジェクトのインスタンス変数名と値、および実行時に呼び出すことができるメソッドが表示されます。読みやすい形式であることが理想的です。
PHPに精通している開発者のために、基本的にリフレクション関数(var_dump()
、get_class_methods()
)とOO Reflection API)に相当するものを探しています。
UPDATE:この種のことをしたい人は誰でもチェックアウトしたいと思うかもしれません Objective-Cランタイム用のMike AshのObjCラッパー 。
これは多かれ少なかれあなたがそれについて行くだろう方法です:
#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);
}
そこから、インスタンスのプロパティの実際の値を取得するのは簡単ですが、それらがプリミティブ型またはオブジェクトであるかどうかを確認する必要があるため、私はそれを入れるのが面倒でした。また、継承チェーンをスキャンしてgetallオブジェクトで定義されたプロパティ。次に、カテゴリで定義されたメソッドなどがあります...しかし、ほとんどすべてがすぐに利用できます。
上記のコードが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
);
}
description
メソッド(Javaの.toString()など)が不足しているため、組み込まれたものは聞いていませんが、作成するのはそれほど難しくありません。 Objective-Cランタイムリファレンス には、オブジェクトのインスタンス変数、メソッド、プロパティなどに関する情報を取得するために使用できる一連の関数があります。
最終的にパブリックリリースのライブラリで、クラス変数を自動的に出力するために現在使用しているものは次のとおりです。インスタンスクラスからすべてのプロパティを継承ツリーの最後までダンプすることで機能します。 KVCのおかげで、プロパティがプリミティブ型であるかどうかを気にする必要はありません(ほとんどの型の場合)。
// 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]]];
}
プロパティ値を印刷するためにケンドールのコードをいくつか調整しました。これは非常に便利です。それをクラスメソッドではなくインスタンスメソッドとして定義しました。スーパークラスの再帰がそれを呼び出す方法だからです。また、KVOに準拠していないプロパティの例外処理を追加し、出力に改行を追加して読みやすくしました(および差分)。
-(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;
}
正直なところ、この仕事に適したツールはXcodeのデバッガーです。これらの情報はすべて視覚的に簡単にアクセスできます。それを使用する方法を学ぶために時間をかけて、それは本当に強力なツールです。
詳しくは:
旧式のXcodeデバッグガイド -Appleによりアーカイブ
Xcodeを使用したデバッグについて -Appleによりアーカイブ
LLDBとデバッグについて -Appleによりアーカイブ
GDBを使用したデバッグ -Appleによりアーカイブ
SpriteKitデバッグガイド -Appleによりアーカイブ
Core Foundationのプログラミングトピックのデバッグ -Appleによってアーカイブされました
これからココアポッドを作りました https://github.com/neoneye/autodescribe
Christopher Pickslayのコードを修正し、NSObjectのカテゴリにし、ユニットテストも追加しました。使用方法は次のとおりです。
@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
私は以前、内観と宗教と混同されていたので、以下の情報を入手してください。
イントロスペクションは、オブジェクトがどのタイプであるか、準拠しているプロトコル、または応答できるセレクターをチェックする機能です。 isKindOfClass
/isMemberOfClass
/conformsToProtocol
/respondsToSelector
などのオブジェクトAPI.
Refection機能はIntrospectionよりも優れています。オブジェクト情報を取得できるだけでなく、オブジェクトのメタデータ、プロパティ、関数を操作することもできます。といった object_setClass
はオブジェクトタイプを変更できます。