Цель C найти вызывающего метода

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

Ответ 1

StackI надеюсь, что это поможет:

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

Ответ 2

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

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

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

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

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

Ответ 3

Используя ответ, предоставленный 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. - если функция вызывается с помощью функции performSelector, результатом будет:

Origin: [NSObject performSelector:withObject:]

Ответ 4

Версия 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

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

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

Ответ 6

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

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

//...

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

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

Ответ 7

Просто написал метод, который сделает это для вас:

- (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;
}

Ответ 8

@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];

В окне вывода вы увидите что-то вроде следующего.

Caller: 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.

Ответ 9

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

Ответ 10

Версия Swift 3 для @Geoff H для справки:

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