2010-10-04 15 views
5

में असफल रहा है मैंने कमांड लाइन प्रोग्राम में इसका उपयोग करते समय एनएसबंडल के साथ कुछ अजीब व्यवहार देखा है। यदि, मेरे प्रोग्राम में, मैं एक मौजूदा बंडल लेता हूं और इसकी एक प्रति बनाते हैं और फिर संसाधन फ़ोल्डर में कुछ देखने के लिए पथफॉर रिसोर्स का उपयोग करने का प्रयास करते हैं, तो शून्य हमेशा तब तक लौटाया जाता है जब तक कि बंडल मैं देख रहा हूं कि मेरे सामने मौजूद नहीं है कार्यक्रम शुरू हुआ। मुझे लगता है कि इस मुद्दे को दोहराता है और प्रासंगिक कोड है एक नमूना एप्लिकेशन बनाया: इस परीक्षण एप्लिकेशन के प्रयोजन के लिएएनएसबंडल पथफॉर रिसोर्स शेल टूल

int main(int argc, char *argv[]) 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    NSString *exePath = [NSString stringWithCString:argv[0] 
              encoding:NSASCIIStringEncoding]; 
    NSString *path = [exePath stringByDeletingLastPathComponent]; 
    NSString *templatePath = [path stringByAppendingPathComponent:@"TestApp.app"]; 

    // This call works because TestApp.app exists before this program is run 
    NSString *resourcePath = [NSBundle pathForResource:@"InfoPlist" 
               ofType:@"strings" 
              inDirectory:templatePath]; 
    NSLog(@"NOCOPY: %@", resourcePath); 

    NSString *copyPath = [path stringByAppendingPathComponent:@"TestAppCopy.app"]; 
    [[NSFileManager defaultManager] removeItemAtPath:copyPath 
               error:nil]; 
    if ([[NSFileManager defaultManager] copyItemAtPath:templatePath 
               toPath:copyPath 
               error:nil]) 
    { 
     // This call will fail if TestAppCopy.app does not exist before 
     // this program is run 
     NSString *resourcePath2 = [NSBundle pathForResource:@"InfoPlist" 
                ofType:@"strings" 
               inDirectory:copyPath]; 
     NSLog(@"COPY: %@", resourcePath2); 
     [[NSFileManager defaultManager] removeItemAtPath:copyPath 
                error:nil]; 
    } 
    [pool release]; 
} 

, मान लेते हैं कि TestApp.app पहले से ही अपने परीक्षण ऐप जैसा निर्देशिका में मौजूद है करते हैं। कॉपी: यदि मैं यह, 2 NSLog कॉल होगा उत्पादन चलाने (शून्य)

अब, अगर मैं अगर बयान में अंतिम removeItemAtPath कॉल बाहर टिप्पणी इतनी है कि जब मेरा कार्यक्रम बाहर निकालता है TestAppCopy.app अभी भी मौजूद है और फिर पुन: चलाएं, कार्यक्रम अपेक्षित काम करेगा।

मैंने इसे सामान्य कोको एप्लिकेशन में आजमाया है और मैं व्यवहार को पुन: पेश नहीं कर सकता। यह केवल एक खोल उपकरण लक्ष्य में होता है। क्या कोई इस कारण से सोच सकता है कि यह क्यों विफल रहा है?

Btw: मैं 10.6.4 पर इस कोशिश कर रहा हूँ और मैं मैक ओएस एक्स

+0

अद्यतन: यदि मैं अपने परीक्षण बंडलों को एक दूसरे निर्देशिका में ले जाता हूं, तो मेरा खोल उपकरण रहता है, यह सब ठीक काम करता है। इसलिए, मुझे केवल समस्या दिखाई देती है यदि बंडल मेरे ऐप के समान निर्देशिका में हैं। मैं इस आवश्यकता के साथ काम कर सकता हूं लेकिन यह जानना अच्छा होगा कि यह मूल रूप से वर्णित क्यों नहीं करता है। – Dustin

+0

जैसा कि कहा गया है, मैंने इस समय के लिए इस बारे में काम किया है, लेकिन मुझे यह समझने में वास्तव में दिलचस्पी है कि यह क्यों हो रहा है और मैं इसे रोकने के लिए क्या कर सकता हूं। – Dustin

उत्तर

3

मैं पुष्टि कर सकता हूं कि यह कोरफॉउंडेशन में एक बग है, फाउंडेशन नहीं। बग सीएफबंडल कोड के कारण है जो निर्देशिका सामग्री कैश पर निर्भर करता है जिसमें स्टेल डेटा होता है। कोड स्पष्ट रूप से मानता है कि न तो बंडल निर्देशिकाएं और न ही उनके तत्काल मूल निर्देशिका अनुप्रयोग रनटाइम के दौरान बदल जाएंगी।

+[NSBundle pathForResource:ofType:inDirectory:] से संबंधित कोरफाउंडेशन कॉल CFBundleCopyResourceURLInDirectory() है, और यह वही दुर्व्यवहार प्रदर्शित करता है। (यह असुरक्षित है, क्योंकि -pathForResource:ofType:inDirectory: स्वयं ही इस कॉल का उपयोग करता है।)

समस्या अंत में _CFBundleCopyDirectoryContentsAtPath() के साथ निहित है। इसे बंडल लोडिंग और सभी संसाधन लुकअप के दौरान कहा जाता है। यह contentsCache में दिखाई देने वाली निर्देशिकाओं के बारे में जानकारी कैश करता है।

यहां समस्या है: TestAppCopy.app की सामग्री प्राप्त करने का समय आने पर, TestApp.app वाली निर्देशिका की कैश की गई सामग्री में TestAppCopy.app शामिल नहीं है। क्योंकि कैश में स्पष्ट रूप से उस निर्देशिका की सामग्री होती है, केवल कैश किए गए सामग्रियों को TestAppCopy.app के लिए खोजा जाता है। जब TestAppCopy.app नहीं पाया जाता है, समारोह लेता है कि एक निश्चित "इस पथ मौजूद नहीं है" के रूप में और निर्देशिका खोलने की कोशिश कर परेशान नहीं करता है:

__CFSpinLock(&CFBundleResourceGlobalDataLock); 
if (contentsCache) dirDirContents = (CFArrayRef)CFDictionaryGetValue(contentsCache, dirName); 
if (dirDirContents) { 
    Boolean foundIt = false; 
    CFIndex dirDirIdx, dirDirLength = CFArrayGetCount(dirDirContents); 
    for (dirDirIdx = 0; !foundIt && dirDirIdx < dirDirLength; dirDirIdx++) if (kCFCompareEqualTo == CFStringCompare(name, CFArrayGetValueAtIndex(dirDirContents, dirDirIdx), kCFCompareCaseInsensitive)) foundIt = true; 
    if (!foundIt) tryToOpen = false; 
} 
__CFSpinUnlock(&CFBundleResourceGlobalDataLock); 

तो, सामग्री सरणी खाली रहता है, इस के लिए कैश की गई हो जाता है पथ, और लुकअप जारी है।अब हमने TestAppCopy.app की (गलत रूप से खाली) सामग्री को कैश किया है, और इस निर्देशिका में लुकअप ड्रिल के रूप में, हम खराब कैश की गई जानकारी को मारते रहते हैं। भाषा लुकअप एक स्टैब लेता है जब उसे कुछ भी नहीं मिलता है और उम्मीद है कि en.lproj आसपास लटक रहा है, लेकिन हमें अभी भी कुछ भी नहीं मिलेगा, क्योंकि हम एक बालों के कैश में देख रहे हैं।

कोरफाउंडेशन में सीएफआई फ़ंक्शन को सीएफबींडल कैश फ्लश करने के लिए शामिल हैं। CoreFoundation में उन्हें एकमात्र स्थान सार्वजनिक एपीआई कॉल करता है __CFBundleDeallocate() है। यह बंडल की निर्देशिका के बारे में सभी कैश की गई जानकारी को फ्लश करता है, लेकिन इसकी मूल निर्देशिका नहीं: _CFBundleFlushContentsCacheForPath(), जो वास्तव में कैश से डेटा को हटा देता है, बंडल पथ के लिए एक एंकर, केस-असंवेदनशील खोज से मेल खाने वाली कुंजियों को हटा देता है।

यह केवल सार्वजनिक रास्ता CoreFoundation के एक ग्राहक TestApp.app की मूल निर्देशिका के बारे में बुरा जानकारी फ्लश सकता मूल निर्देशिका एक बंडल निर्देशिका बनाने के लिए किया जाएगा प्रतीत होता है (ताकि TestApp.appContents के साथ-साथ रहते थे), माता पिता के बंडल के लिए एक CFBundle बनाने निर्देशिका, फिर उस सीएफबंडल को छोड़ दें। लेकिन, ऐसा लगता है कि अगर आपने TestAppCopy.app बंडल के साथ काम करने से पहले काम करने की कोशिश करने की गलती की है, तो TestAppCopy.app के बारे में खराब डेटा फ़्लश नहीं किया जाएगा।

+0

महान जवाब! धन्यवाद। जैसा कि मैंने ऊपर बताया है, मैंने इसके लिए एक बग दर्ज किया है, इसलिए उम्मीद है कि यह किसी बिंदु पर तय हो जाएगा। – Dustin

1

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

@interface Foo:NSObject 
@end 
@implementation Foo 
- (void) doIt { .... your code from main() here .... } 
@end 

... main(...) { 
    Foo *f = [Foo new]; 
    [f performSelector: @selector(doIt) withObject: nil afterDelay: 0.1 ...]; 
    [[NSRunLoop currentRunLoop] run]; 
    return 0; // not reached, I'd bet. 
} 

और देखें कि क्या यह "ठीक करता है"। ये हो सकता है। यह नहीं हो सकता है (स्पष्ट रूप से कुछ अन्य महत्वपूर्ण मतभेद हैं)। किसी भी मामले में, कृपया http://bugreport.apple.com/ के माध्यम से एक बग फ़ाइल करें और टिप्पणी के रूप में बग # जोड़ें।

+0

उत्तर bbum के लिए धन्यवाद। बग को rdar: // 8535620 के रूप में दर्ज किया गया है। मैं इस बक्षीस को कुछ और दिन दूंगा और अगर कोई जवाब के साथ जवाब नहीं देता है, तो मैं आपको बक्षीस दूंगा। – Dustin

+0

मैं यह जोड़ना भूल गया कि मैंने आपके सुझाव की कोशिश की लेकिन यह व्यवहार को नहीं बदला। – Dustin

संबंधित मुद्दे