method
特定のコードが呼び出された行を特定する方法はありますか?
12 に答える
Stack私はこれが役立つことを願っています:
NSString *sourceString = [[NSThread callStackSymbols] objectAtIndex:1];
// Example: 1 UIKit 0x00540c89 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163
NSCharacterSet *separatorSet = [NSCharacterSet characterSetWithCharactersInString:@" -[]+?.,"];
NSMutableArray *array = [NSMutableArray arrayWithArray:[sourceString componentsSeparatedByCharactersInSet:separatorSet]];
[array removeObject:@""];
NSLog(@"Stack = %@", [array objectAtIndex:0]);
NSLog(@"Framework = %@", [array objectAtIndex:1]);
NSLog(@"Memory address = %@", [array objectAtIndex:2]);
NSLog(@"Class caller = %@", [array objectAtIndex:3]);
NSLog(@"Function caller = %@", [array objectAtIndex:4]);
完全に最適化されたコードでは、特定のメソッドの呼び出し元を特定する 100% 確実な方法はありません。コンパイラは末尾呼び出しの最適化を採用する場合がありますが、コンパイラは呼び出し元のスタック フレームを呼び出し先に効果的に再利用します。
この例を確認するには、gdb を使用して特定のメソッドにブレークポイントを設定し、バックトレースを調べます。すべてのメソッド呼び出しの前に objc_msgSend() が表示されないことに注意してください。これは、objc_msgSend() が各メソッドの実装に対して末尾呼び出しを行うためです。
最適化されていないアプリケーションをコンパイルすることもできますが、この 1 つの問題だけを回避するには、すべてのシステム ライブラリの最適化されていないバージョンが必要になります。
これは問題の 1 つにすぎません。実際には、「CrashTracer や gdb を再発明するにはどうすればよいですか?」と尋ねていることになります。キャリアが築かれる非常に難しい問題。「デバッグ ツール」を自分のキャリアにしたいのでない限り、この道を進むことはお勧めしません。
あなたが本当に答えようとしている質問は何ですか?
intropedroによって提供された回答を使用して、私はこれを思いつきました:
#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])
元のクラスと関数を返すだけです:
2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]
ps - 関数が performSelector を使用して呼び出された場合、結果は次のようになります。
Origin: [NSObject performSelector:withObject:]
参照用の@Intropedroの回答のSwift 2.0バージョン。
let sourceString: String = NSThread.callStackSymbols()[1]
let separatorSet :NSCharacterSet = NSCharacterSet(charactersInString: " -[]+?.,")
let array = NSMutableArray(array: sourceString.componentsSeparatedByCharactersInSet(separatorSet))
array.removeObject("")
print("Stack: \(array[0])")
print("Framework:\(array[1])")
print("Memory Address:\(array[2])")
print("Class Caller:\(array[3])")
print("Method Caller:\(array[4])")
デバッグのためなら、 NSLog(@"%s", __FUNCTION__);
クラスの各メソッド内の最初の行として。そうすれば、デバッガーを見ることでメソッド呼び出しの順序をいつでも知ることができます。
@Roy Kronenfeldの素晴らしい答えのわずかに最適化されたバージョン:
- (NSString *)findCallerMethod
{
NSString *callerStackSymbol = nil;
NSArray<NSString *> *callStackSymbols = [NSThread callStackSymbols];
if (callStackSymbols.count >= 2)
{
callerStackSymbol = [callStackSymbols objectAtIndex:2];
if (callerStackSymbol)
{
// Stack: 2 TerribleApp 0x000000010e450b1e -[TALocalDataManager startUp] + 46
NSInteger idxDash = [callerStackSymbol rangeOfString:@"-" options:kNilOptions].location;
NSInteger idxPlus = [callerStackSymbol rangeOfString:@"+" options:NSBackwardsSearch].location;
if (idxDash != NSNotFound && idxPlus != NSNotFound)
{
NSRange range = NSMakeRange(idxDash, (idxPlus - idxDash - 1)); // -1 to remove the trailing space.
callerStackSymbol = [callerStackSymbol substringWithRange:range];
return callerStackSymbol;
}
}
}
return (callerStackSymbol) ?: @"Caller not found! :(";
}
参照用の@Geoff Hの回答のSwift 3バージョン:
let sourceString: String = Thread.callStackSymbols[1]
let separatorSet: CharacterSet = CharacterSet(charactersIn: " -[]+?.,")
let array = NSMutableArray(array: sourceString.components(separatedBy: separatorSet))
array.remove("")
print("Stack: \(array[0])")
print("Framework:\(array[1])")
print("Memory Address:\(array[2])")
print("Class Caller:\(array[3])")
print("Method Caller:\(array[4])")