Hedef C yöntemi arayan bul


Yanıtlar:


188

Yığın Umarım bu yardımcı olur:

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

1
Ayrıca -Prefix.pch dosyasında bir makro yaptı ve ardından uygulama temsilcisinden çalıştırdı. İlginç bir şekilde, sınıf arayan: "<redacted>"
Melvin Sovereign

4
benim durumumda, dizin 5'te hiçbir şey yok. Bu nedenle bu kod uygulamamı çöktü. son satırı kaldırdıktan sonra çalıştı. Yine de +1 değerinde olması o kadar harika ki!
Brian

1
Bu harika çalışıyor, ama "hattı arayan" ı nasıl yorumlayabiliriz? Benim durumumda bir sayı gösteriyor, örneğin 91, ama neden 91? Aşağıdaki birinci arama talimatını taşırsam, 136 gösterecektir ... Peki bu numara nasıl hesaplanır?
Maxim Chetrusca

@ Pétur Performans üzerinde bir etkisi varsa, ihmal edilebilir, NSThread zaten bu bilgiye sahip, temelde sadece bir diziye erişiyor ve yeni bir tane oluşturuyorsunuz.
Oscar Gomez

Hata ayıklama modunda çalışıyor, ancak IPA paketine arşivledikten sonra çağrı yığını beklendiği gibi çalışmıyor. "CallStackSymbols = 1 SimpleApp 0x00000001002637a4 _Z8isxdigiti + 63136", "_Z8isxdigiti" "AAMAgentAppDelegate uygulaması: didFinishLaunchingWithOptions:" olmalıdır
Alanc Liu

50

Tamamen optimize edilmiş kodda, arayan kişiyi belirli bir yöntemle belirlemenin% 100 kesin bir yolu yoktur. Derleyici, bir kuyruk arama optimizasyonunu kullanabilirken, derleyici, aranan uç için arayanın yığın çerçevesini etkili bir şekilde yeniden kullanır.

Bunun bir örneğini görmek için, gdb'yi kullanarak herhangi bir yöntemde bir kesme noktası ayarlayın ve geriye dönük izlemeye bakın. Her yöntem çağrısından önce objc_msgSend () görmediğinizi unutmayın. Bunun nedeni objc_msgSend () 'in her yöntemin uygulamasına bir kuyruk çağrısı yapmasıdır.

Uygulamanızı optimize edilmemiş olarak derleyebilirsiniz, ancak tek bir problemden kaçınmak için tüm sistem kitaplıklarının optimize edilmemiş sürümlerine ihtiyacınız olacaktır.

Ve bu sadece bir problem; aslında "CrashTracer veya gdb'yi nasıl yeniden icat edebilirim?" diye soruyorsunuz. Kariyerlerin yapıldığı çok zor bir problem. Kariyerinizin "hata ayıklama araçları" olmasını istemiyorsanız, bu yola girmemenizi tavsiye ederim.

Gerçekten hangi soruyu cevaplamaya çalışıyorsun?


3
AMAN TANRIM. Bu beni dünyaya geri getirdi. Neredeyse tam anlamıyla. Tamamen ilgisiz bir sorunu çözüyordum. Teşekkürler bayım!
nimeshdesai

11

İntropedro tarafından sağlanan cevabı kullanarak şunu buldum :

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

bu da bana Orijinal sınıf ve işlevi döndürecektir:

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

ps - if işlev performSelector kullanılarak çağrılırsa, sonuç şöyle olur:

Origin: [NSObject performSelector:withObject:]

2
* Ancak bazı durumlarda işlev adı içermediğini, seçici gerçekleştirmediğini ve dolayısıyla - CALL_ORIGIN'in çağrılması çöküyor. (Öyleyse, tavsiye ederim - bu örneği kullanacaksanız, geçici olarak kullanın ve sonra kaldırın.)
Guntis Treulands

6

Bunu sizin için yapacak bir yöntem yazdım:

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

6

@ Intropedro'nun referans cevabının Swift 2.0 sürümü;

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

Hata ayıklama uğruna ise, bir NSLog(@"%s", __FUNCTION__);

Sınıflarınızdaki her yöntemin içindeki ilk satır olarak. Daha sonra hata ayıklayıcıya bakarak yöntem çağrılarının sırasını her zaman öğrenebilirsiniz.


Bir şekilde kod doğru görünmüyor. FUNCTION'dan önce ve sonra iki alt çizgi var
Giovanni

Kodunuzun düzgün bir şekilde görüntülenebilmesi için ters işaret kaçışlarını (`) kullanmayı deneyin
howanghk

3
Veya Objective-C'yi destekleyen ve yöntemle birlikte nesne adını görüntüleyen __PRETTY_FUNCTION__'i daha iyi kullanın.
pronebird

4

selfİşleve argümanlardan biri olarak iletebilir ve ardından içinde arayan nesnenin sınıf adını alabilirsiniz:

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

//...

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

Bu şekilde, sorunun nerede olabileceğini belirlemenize yardımcı olacak herhangi bir nesneyi iletebilirsiniz.


3

@ Roy Kronenfeld'in harika cevabının biraz optimize edilmiş bir versiyonu:

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

@hayalhanemersin

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

Çıktı penceresinde aşağıdaki gibi bir şey göreceksiniz.

Arayan: 2 Uygulamam 0x0004e8ae - [IINClassroomInit buildMenu] + 86

Yığın çerçevesi hakkında daha fazla veri çıkarmak için bu dizeyi ayrıştırabilirsiniz.

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

İOS'ta Arama Yöntemini Tanımla'dan alınmıştır .


2

@Geoff H'nin Swift 4 sürümü kopyalama ve yapıştırma için yanıt ;]

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

0

Referans için @Geoff H'nin Swift 3 versiyonu:

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])")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.