ऑब्जेक्टिव C विधि का कॉलर खोजता है


90

क्या कोड की रेखा निर्धारित करने का कोई तरीका है methodजिसे एक निश्चित कहा जाता है?


तुम ऐसा क्यों करना चाहते हो? यदि यह डिबगिंग के लिए है, तो उत्तर का एक बहुत अलग सेट है यदि आप इसे उत्पादन में करना चाहते हैं (जिसके लिए उत्तर अधिक संभावना है "नहीं"।)
निकोलस रिले

4
मैं डिबगिंग उत्तर ले लूँगा
ennuikiller

3
क्या कोई उत्पादन उत्तर है?
हरि करम सिंह

जवाबों:


188

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

1
ने भी -Prefix.pch फ़ाइल में एक मैक्रो बनाया और फिर इसे एप्लिकेशन प्रतिनिधि से चलाया। दिलचस्प है, क्लास कॉलर था: "<redacted>"
मेल्विन सॉवरिन

4
मेरे मामले में, सूचकांक 5 पर कुछ भी नहीं है। इस प्रकार इस कोड ने मेरे ऐप को क्रैश कर दिया। यह अंतिम पंक्ति को हटाने के बाद काम किया। बहरहाल, यह अभी भी बहुत बढ़िया है कि यह +1 के लायक है!
ब्रायन

1
यह महान काम करता है, लेकिन हम "लाइन कॉलर" की व्याख्या कैसे करते हैं? मेरे मामले में, यह एक संख्या दिखाता है, उदाहरण के लिए 91, लेकिन यह 91 क्यों है? यदि मैं कॉल एक निर्देश को नीचे ले जाता हूं, तो यह 136 दिखाएगा ... तो यह संख्या कैसे गणना की जाती है?
मैक्सिम चेट्रस्का

@ Pétur यदि प्रदर्शन पर कोई प्रभाव पड़ता है तो यह नगण्य है, NSThread में पहले से ही यह जानकारी है, आप मूल रूप से केवल एक सरणी तक पहुँच रहे हैं और एक नया बना रहे हैं।
ऑस्कर गोमेज़

यह डिबग मोड में काम कर रहा है, लेकिन इसे IPA पैकेज में संग्रहित करने के बाद, कॉल स्टैक अपेक्षा के अनुरूप काम नहीं कर रहा है। मुझे सिर्फ "callStackSymbols = 1 SimpleApp 0x00000001002637a4 _Z8isxdigiti + 63136", "_Z8isxdigiti" "AAMAgentADelegate अनुप्रयोग" चाहिए: didFinishLaunchingWithOptions: "
Alanc लियू

50

पूरी तरह से अनुकूलित कोड में, एक निश्चित विधि के लिए कॉलर को निर्धारित करने के लिए 100% अचूक तरीका नहीं है। कंपाइलर एक टेल कॉल ऑप्टिमाइज़ेशन को नियोजित कर सकता है जबकि कंपाइलर कैली के लिए कॉलर के स्टैक फ्रेम का प्रभावी रूप से पुनः उपयोग करता है।

इसका एक उदाहरण देखने के लिए, gdb का उपयोग करके किसी भी विधि पर एक ब्रेकपॉइंट सेट करें और बैकट्रेस को देखें। ध्यान दें कि आपको हर विधि कॉल से पहले objc_msgSend () दिखाई नहीं देता है। ऐसा इसलिए है क्योंकि objc_msgSend () प्रत्येक विधि के कार्यान्वयन के लिए एक पूंछ कॉल करता है।

जब आप अपने आवेदन को गैर-अनुकूलित कर सकते हैं, तो आपको इस एक समस्या से बचने के लिए सभी सिस्टम लाइब्रेरी के गैर-अनुकूलित संस्करणों की आवश्यकता होगी।

और यह सिर्फ एक समस्या है; वास्तव में, आप पूछ रहे हैं "मैं क्रैशट्रैसर या जीडीबी का पुन: आविष्कार कैसे करूं?" एक बहुत कठिन समस्या जिस पर करियर बनाया जाता है। जब तक आप "डिबगिंग टूल्स" को अपना करियर नहीं बनाना चाहते, मैं इस सड़क के नीचे जाने की सलाह दूंगा।

क्या सवाल आप वास्तव में जवाब देने की कोशिश कर रहे हैं?


3
हे भगवान। इसने मुझे धरती पर वापस लाया। लगभग शाब्दिक रूप से। मैं पूरी तरह से, असंबंधित समस्या को हल कर रहा था। धन्यवाद महोदय!
निमेशदेसाई

11

इंट्रोपीड्रो द्वारा प्रदान किए गए उत्तर का उपयोग करते हुए , मैं इसके साथ आया:

#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:]

2
* लेकिन ध्यान रहे, कि कुछ मामलों में, इसमें फ़ंक्शन नाम नहीं होता है, न ही चयनकर्ता प्रदर्शन करता है, और इस प्रकार - कॉलिंग COR_ORIGIN क्रैश होता है। (एसओ, मैं सलाह देता हूं - यदि आप इस उदाहरण का उपयोग करने जा रहे हैं, तो इसे अस्थायी रूप से उपयोग करें और फिर इसे हटा दें।)
गुंटिस ट्रेन्डेंड्स

6

बस एक विधि लिखी है जो आपके लिए यह करेगी:

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

संदर्भ के लिए @ इंट्रोपेड्रो के जवाब का स्विफ्ट 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])")

5

यदि यह डिबगिंग खातिर है, तो आदत डालने की आदत डालें NSLog(@"%s", __FUNCTION__);

अपनी कक्षाओं में प्रत्येक विधि के अंदर पहली पंक्ति के रूप में। तब आप हमेशा डिबगर को देखने से विधि कॉल का क्रम जान सकते हैं।


किसी तरह कोड सही ढंग से दिखाई नहीं दे रहा है। FUNCTION से पहले और बाद में दो अंडरस्कोर हैं
Giovanni

तो यह प्रदर्शित किया जा सकता ठीक से बैकटिक पलायन ( `) का उपयोग कर अपने कोड को संलग्न करने की कोशिश
howanghk

3
या बेहतर उपयोग __PRETTY_FUNCTION__ जो उद्देश्य-सी का भी समर्थन करता है और विधि के साथ वस्तु का नाम प्रदर्शित करता है।
बेन सिंक्लेयर

4

आप selfफ़ंक्शन में एक तर्क के रूप में पास कर सकते हैं और फिर कॉल करने वाले ऑब्जेक्ट का क्लासनाम अंदर प्राप्त कर सकते हैं:

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

//...

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

इस तरह आप इसे किसी भी ऑब्जेक्ट को पास कर सकते हैं जो आपको यह निर्धारित करने में मदद करेगा कि समस्या कहां हो सकती है।


3

@ रॉयेन क्रोनफेल्ड के शानदार उत्तर का थोड़ा अनुकूलित संस्करण:

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

@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 में आइडेंटिफाई कॉलिंग मेथड से लिया गया था ।


2

कॉपी और चिपकाने के लिए @Geoff H उत्तर का स्विफ्ट 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])")

0

संदर्भ के लिए @Geoff H उत्तर का स्विफ्ट 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])")
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.