Gibt es eine Möglichkeit, die Codezeile zu bestimmen, aus der eine bestimmte method
aufgerufen wurde?
Gibt es eine Möglichkeit, die Codezeile zu bestimmen, aus der eine bestimmte method
aufgerufen wurde?
Antworten:
StackIch hoffe, dass dies hilft:
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]);
In vollständig optimiertem Code gibt es keine 100% todsichere Möglichkeit, den Aufrufer für eine bestimmte Methode zu bestimmen. Der Compiler kann eine Tail-Call-Optimierung verwenden, während der Compiler den Stack-Frame des Aufrufers effektiv für den Angerufenen wiederverwendet.
Um ein Beispiel dafür zu sehen, setzen Sie mit gdb einen Haltepunkt für eine bestimmte Methode und sehen Sie sich die Rückverfolgung an. Beachten Sie, dass objc_msgSend () nicht vor jedem Methodenaufruf angezeigt wird. Dies liegt daran, dass objc_msgSend () einen Tail-Aufruf für die Implementierung jeder Methode ausführt.
Während Sie Ihre Anwendung nicht optimiert kompilieren könnten, würden Sie nicht optimierte Versionen aller Systembibliotheken benötigen, um nur dieses eine Problem zu vermeiden.
Und das ist nur ein Problem; Tatsächlich fragen Sie: "Wie erfinde ich CrashTracer oder gdb neu?". Ein sehr schwieriges Problem, auf dem Karrieren gemacht werden. Wenn Sie nicht möchten, dass "Debugging-Tools" Ihre Karriere sind, würde ich empfehlen, diesen Weg nicht einzuschlagen.
Welche Frage versuchst du wirklich zu beantworten?
Mit der Antwort von intropedro kam ich auf Folgendes :
#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])
das wird mir einfach zurückgeben Ursprüngliche Klasse und Funktion:
2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]
ps - Wenn die Funktion mit performSelector aufgerufen wird, lautet das Ergebnis:
Origin: [NSObject performSelector:withObject:]
Ich habe gerade eine Methode geschrieben, die dies für Sie erledigt:
- (NSString *)getCallerStackSymbol {
NSString *callerStackSymbol = @"Could not track caller stack symbol";
NSArray *stackSymbols = [NSThread callStackSymbols];
if(stackSymbols.count >= 2) {
callerStackSymbol = [stackSymbols objectAtIndex:2];
if(callerStackSymbol) {
NSMutableArray *callerStackSymbolDetailsArr = [[NSMutableArray alloc] initWithArray:[callerStackSymbol componentsSeparatedByString:@" "]];
NSUInteger callerStackSymbolIndex = callerStackSymbolDetailsArr.count - 3;
if (callerStackSymbolDetailsArr.count > callerStackSymbolIndex && [callerStackSymbolDetailsArr objectAtIndex:callerStackSymbolIndex]) {
callerStackSymbol = [callerStackSymbolDetailsArr objectAtIndex:callerStackSymbolIndex];
callerStackSymbol = [callerStackSymbol stringByReplacingOccurrencesOfString:@"]" withString:@""];
}
}
}
return callerStackSymbol;
}
Die Swift 2.0-Version von @ Intropedros Antwort als Referenz;
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])")
Wenn es um das Debbuging geht, gewöhnen Sie sich an, ein NSLog(@"%s", __FUNCTION__);
Als erste Zeile in jeder Methode in Ihren Klassen. Dann können Sie die Reihenfolge der Methodenaufrufe immer anhand des Debuggers erkennen.
Sie können self
als eines der Argumente an die Funktion übergeben und dann den Klassennamen des Aufruferobjekts darin abrufen:
+(void)log:(NSString*)data from:(id)sender{
NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}
//...
-(void)myFunc{
[LoggerClassName log:@"myFunc called" from:self];
}
Auf diese Weise können Sie jedes Objekt übergeben, mit dessen Hilfe Sie feststellen können, wo das Problem liegen könnte.
Eine leicht optimierte Version von @Roy Kronenfelds fantastischer Antwort:
- (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! :(";
}
@ennuikiller
//Add this private instance method to the class you want to trace from
-(void)trace
{
//Go back 2 frames to account for calling this helper method
//If not using a helper method use 1
NSArray* stack = [NSThread callStackSymbols];
if (stack.count > 2)
NSLog(@"Caller: %@", [stack objectAtIndex:2]);
}
//Add this line to the method you want to trace from
[self trace];
Im Ausgabefenster sehen Sie ungefähr Folgendes.
Anrufer: 2 MyApp 0x0004e8ae - [IINClassroomInit buildMenu] + 86
Sie können diese Zeichenfolge auch analysieren, um weitere Daten zum Stapelrahmen zu extrahieren.
2 = Thread id
My App = Your app name
0x0004e8ae = Memory address of caller
-[IINClassroomInit buildMenu] = Class and method name of caller
+86 = Number of bytes from the entry point of the caller that your method was called
Es wurde aus der Anrufmethode identifizieren in iOS übernommen .
Die Swift 4-Version von @Geoff H antwortet zum Kopieren und Einfügen ;]
let sourceString: String = Thread.callStackSymbols[1]
let separatorSet :CharacterSet = CharacterSet(charactersIn: " -[]+?.,")
var array = Array(sourceString.components(separatedBy: separatorSet))
array = array.filter { $0 != "" }
print("Stack: \(array[0])")
print("Framework:\(array[1])")
print("Memory Address:\(array[2])")
print("Class Caller:\(array[3])")
print("Method Caller:\(array[4])")
Die Swift 3-Version von @Geoff H als Referenz:
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])")