Цель C найти вызывающего метода - PullRequest
85 голосов
/ 20 сентября 2009

Есть ли способ определить строку кода, с которой был вызван определенный method?

Ответы [ 11 ]

180 голосов
/ 07 марта 2012

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]);
49 голосов
/ 20 сентября 2009

В полностью оптимизированном коде нет 100% надежного способа определения вызывающего абонента для определенного метода.Компилятор может использовать оптимизацию хвостового вызова, в то время как компилятор эффективно повторно использует кадр стека вызывающего для вызываемого.

Чтобы увидеть пример этого, установите точку останова для любого данного метода, используя gdb, и посмотрите на обратную трассировку,Обратите внимание, что вы не видите objc_msgSend () перед каждым вызовом метода.Это потому, что objc_msgSend () выполняет хвостовой вызов для реализации каждого метода.

Хотя вы можете скомпилировать ваше приложение неоптимизированным, вам потребуются неоптимизированные версии всех системных библиотек, чтобы избежать только этой одной проблемы.

И это только одна проблема;по сути, вы спрашиваете «как мне изобрести CrashTracer или gdb?».Очень сложная проблема, на которой делается карьера.Если вы не хотите, чтобы «инструменты отладки» были вашей карьерой, я бы не советовал идти по этому пути.

На какой вопрос вы действительно пытаетесь ответить?

10 голосов
/ 04 февраля 2014

Используя ответ, предоставленный 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]

p.s. - если функция вызывается с помощью executeSelector, результат будет:

Origin: [NSObject performSelector:withObject:]
6 голосов
/ 07 ноября 2015

Версия Swift 2.0 @ Intropedro для справки;

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])")
5 голосов
/ 08 сентября 2015

Только что написал метод, который сделает это за вас:

- (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 голосов
/ 11 октября 2012

Если это ради отладки, привыкни ставить NSLog(@"%s", __FUNCTION__);

Как первая строка внутри каждого метода в ваших классах. Тогда вы всегда сможете узнать порядок вызовов методов, посмотрев на отладчик.

4 голосов
/ 04 июля 2013

Вы можете передать self в качестве одного из аргументов функции, а затем получить имя класса вызывающего объекта внутри:

+(void)log:(NSString*)data from:(id)sender{
    NSLog(@"[%@]: %@", NSStringFromClass([sender class]), data);
}

//...

-(void)myFunc{
    [LoggerClassName log:@"myFunc called" from:self];
}

Таким образом, вы можете передать ему любой объект, который поможет вам определить, где может быть проблема.

2 голосов
/ 16 ноября 2015

Немного оптимизированная версия фантастического ответа @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! :(";
}
2 голосов
/ 26 декабря 2013

@ 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 .

1 голос
/ 09 февраля 2018

Версия Swift 4 @Geoff H для копирования и вставки ;]

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])")
...