Есть ли способ определить, из какой строки кода method
была вызвана определенная ?
Есть ли способ определить, из какой строки кода method
была вызвана определенная ?
Ответы:
Я надеюсь, что это поможет:
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 () выполняет хвостовой вызов каждой реализации метода.
Хотя вы можете скомпилировать свое приложение без оптимизации, вам понадобятся неоптимизированные версии всех системных библиотек, чтобы избежать только этой проблемы.
И это всего лишь одна проблема; по сути, вы спрашиваете: «Как мне заново изобрести 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:]
Просто написал метод, который сделает это за вас:
- (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;
}
Ответ @ 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__);
В качестве первой строки внутри каждого метода в ваших классах. Тогда вы всегда сможете узнать порядок вызовов методов, посмотрев на отладчик.
Вы можете передать в self
качестве одного из аргументов функции, а затем получить имя класса вызывающего объекта внутри:
+(void)log:(NSString*)data from:(id)sender{
NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}
//...
-(void)myFunc{
[LoggerClassName log:@"myFunc called" from:self];
}
Таким образом вы можете передать ему любой объект, который поможет вам определить, где может быть проблема.
Слегка оптимизированная версия фантастического ответа @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];
В окне вывода вы увидите примерно следующее.
Вызывающий: 2 MyApp 0x0004e8ae - [IINClassroomInit buildMenu] + 86
Вы также можете проанализировать эту строку, чтобы извлечь дополнительные данные о кадре стека.
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
Это было взято из " Определить метод вызова" в iOS .
Ответ @Geoff H на Swift 4 для копирования и вставки ;]
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])")
Версия @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])")