it-swarm.com.de

Ziel C Finder der Methode suchen

Gibt es eine Möglichkeit, die Codezeile zu bestimmen, aus der eine bestimmte method aufgerufen wurde?

84
ennuikiller

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]);
177
intropedro

Im vollständig optimierten Code gibt es keine 100% ige Möglichkeit, den Aufrufer für eine bestimmte Methode zu bestimmen. Der Compiler kann eine Rückrufoptimierung einsetzen, während der Compiler den Stack-Frame des Aufrufers für den Angerufenen effektiv wiederverwendet.

Um ein Beispiel dafür zu sehen, setzen Sie mit gdb einen Haltepunkt für eine beliebige Methode und sehen Sie sich die Rückverfolgung an. Beachten Sie, dass Sie objc_msgSend () nicht vor jedem Methodenaufruf sehen. Das liegt daran, dass objc_msgSend () einen Tail-Aufruf für die Implementierung jeder Methode ausführt.

Sie können Ihre Anwendung zwar nicht optimiert kompilieren, aber Sie benötigen nicht optimierte Versionen aller Systembibliotheken, um nur dieses eine Problem zu vermeiden.

Und das ist nur ein Problem. Sie fragen tatsächlich: "Wie erfinde ich CrashTracer oder gdb neu?". Ein sehr hartes Problem, auf dem Karrieren gemacht werden. Wenn Sie nicht möchten, dass "Debugging-Tools" Ihre Karriere sind, würde ich empfehlen, diesen Weg nicht zu gehen.

Welche Frage versuchst du wirklich zu beantworten?

49
bbum

Mit der Antwort von intropedro kam ich dazu:

#define CALL_Origin NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])

das wird mir einfach Original Klasse und Funktion zurückgeben:

2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]

p.s. - Wenn die Funktion mit performSelector aufgerufen wird, lautet das Ergebnis:

Origin: [NSObject performSelector:withObject:]
9

Die Swift 2.0-Version von @ Intropedro's 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])")
6
Geoff H

Wenn es um die Fehlersuche geht, machen Sie es sich zur Gewohnheit, eine NSLog(@"%s", __FUNCTION__); zu setzen.

Als erste Zeile in jeder Methode in Ihren Klassen. Dann können Sie die Reihenfolge der Methodenaufrufe immer aus dem Debugger erkennen.

5
Giovanni

Schreibe einfach eine Methode, die dies für Sie tun wird:

- (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;
}
5
Roy K

Sie können self als eines der Argumente an die Funktion übergeben und dann den Klassennamen des Aufruferobjekts 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 ein beliebiges Objekt übergeben, mit dem Sie feststellen können, wo das Problem möglicherweise liegt.

4
pckill

Eine etwas 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! :(";
}
2
Andrew

@Nennikiller 

//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 etwa Folgendes.

Anrufer: 2 MyApp 0x0004e8ae - [IINClassroomInit buildMenu] + 86

Sie können diese Zeichenfolge auch analysieren, um weitere Daten zum Stack-Frame 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 Identify Calling Method in iOS entnommen.

2
DannyBios

Die Swift 4-Version von @Geoff H antwortet auf copy and paste;] 

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])")
1
Andy Obusek

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])")
0
Carmelo Gallo