C'è un modo per determinare la riga di codice da cui è method
stato chiamato un determinato ?
C'è un modo per determinare la riga di codice da cui è method
stato chiamato un determinato ?
Risposte:
Mi auguro che questo aiuti:
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]);
Nel codice completamente ottimizzato, non esiste un modo sicuro al 100% per determinare il chiamante a un determinato metodo. Il compilatore può impiegare un'ottimizzazione della chiamata di coda mentre il compilatore riutilizza efficacemente lo stack frame del chiamante per il chiamato.
Per vedere un esempio di ciò, impostare un punto di interruzione su un dato metodo utilizzando gdb e guardare il backtrace. Nota che non vedi objc_msgSend () prima di ogni chiamata al metodo. Questo perché objc_msgSend () esegue una chiamata in coda all'implementazione di ciascun metodo.
Sebbene tu possa compilare la tua applicazione non ottimizzata, avresti bisogno di versioni non ottimizzate di tutte le librerie di sistema per evitare solo questo problema.
E questo è solo un problema; in effetti, stai chiedendo "come faccio a reinventare CrashTracer o gdb?". Un problema molto difficile su cui si fondano le carriere. A meno che tu non voglia che gli "strumenti di debug" siano la tua carriera, ti consiglio di non seguire questa strada.
A quale domanda stai davvero cercando di rispondere?
Usando la risposta fornita da intropedro , mi è venuta questa:
#define CALL_ORIGIN NSLog(@"Origin: [%@]", [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1])
che mi restituirà semplicemente la classe e la funzione originali:
2014-02-04 16:49:25.384 testApp[29042:70b] Origin: [LCallView addDataToMapView]
ps - se la funzione viene chiamata utilizzando performSelector, il risultato sarà:
Origin: [NSObject performSelector:withObject:]
Ho appena scritto un metodo che lo farà per te:
- (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;
}
La versione Swift 2.0 della risposta di @ Intropedro per riferimento;
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])")
Se è per amor di debuttante, prendi l'abitudine di mettere a NSLog(@"%s", __FUNCTION__);
Come prima riga all'interno di ogni metodo nelle tue classi. Quindi puoi sempre conoscere l'ordine delle chiamate ai metodi guardando il debugger.
Puoi passare self
come uno degli argomenti alla funzione e quindi ottenere il nome della classe dell'oggetto chiamante all'interno:
+(void)log:(NSString*)data from:(id)sender{
NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}
//...
-(void)myFunc{
[LoggerClassName log:@"myFunc called" from:self];
}
In questo modo puoi passargli qualsiasi oggetto che ti aiuti a determinare dove potrebbe essere il problema.
Una versione leggermente ottimizzata della fantastica risposta di @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! :(";
}
@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];
Nella finestra di output vedrai qualcosa di simile al seguente.
Caller: 2 MyApp 0x0004e8ae - [IINClassroomInit buildMenu] + 86
Puoi anche analizzare questa stringa per estrarre più dati sullo stack frame.
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
È stato preso da Identify Calling Method in iOS .
La versione Swift 4 di @Geoff H risponde per copia e incolla ;]
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])")
La versione Swift 3 di @Geoff H risponde come riferimento:
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])")