2012-02-05 16 views
5

में समयपूर्व डिलोक मुझे एक समस्या है जो एक एआरसी आधारित ऐप में एक उपयोग में आने वाली वस्तु का समयपूर्व रिलीज प्रतीत होता है। मैं एक FTP सर्वर पर एक फ़ोल्डर बनाने की कोशिश कर रहा हूँ। कोड के प्रासंगिक भाग नीचे हैं; मैं पहले समस्या का वर्णन करूंगा। कोड के साथएआरसी आधारित ऐप

समस्या यह है कि

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode 

विधि में डिबग आउटपुट कभी नहीं कहा जाता है, है।

इसके बजाय, मुझे बस _EXC_BAD_ACCESS_ त्रुटि मिलती है। डिबगिंग जबकि मैं दो बातें पता चला:

  1. त्रुटि केवल प्रकट होता है, तो कोड (createDir विधि) की निम्न पंक्ति निष्पादित होने

    [ftpStream open]; 
    

अगर उस संदेश नहीं भेजा है , शेष कोड वास्तव में समझ में नहीं आता है - लेकिन यह या तो क्रैश नहीं होता है ...

  1. मैंने ट्रैक किया NSZombieEnabled साथ EXC_BAD_ACCESS नीचे: ज़ोंबी वस्तुओं सक्षम होने पर, GDB निम्नलिखित डिबगर जानकारी का उत्पादन:

    *** -[FTPUploads respondsToSelector:]: message sent to deallocated instance 0x9166590 
    

में भेजा पता 0x9166590 मेरी FTPUploads का पता आपत्ति है। ऐसा लगता है कि संदेश को संभालने से पहले स्ट्रीम प्रतिनिधि प्रतिनिधि को हटा दिया जाता है।

सिस्टम इन-यूज ऑब्जेक्ट को क्यों रोकता है? मैं इसे समय से पहले हटाए जाने से कैसे रोक सकता हूं?

कोड:

FTPUploads.h अंश:

#import <Foundation/Foundation.h> 

enum UploadMode { 

    UploadModeCreateDir, 
    UploadModeUploadeData 
}; 

@class UploadDatasetVC; 

@interface FTPUploads : NSObject<NSStreamDelegate> { 

    @private 
    NSString *uploadDir; 
    NSString *ftpUser; 
    NSString *ftpPass; 

    NSString *datasetDir; 
    NSArray *files; 

    /* FTP Upload fields */ 
    NSInputStream *fileStream; 
    NSOutputStream *ftpStream; 
    // some more fields... 
    enum UploadMode uploadMode; 
    UploadDatasetVC *callback; 
} 

- (id) initWithTimeseriesID: (int) aTimeseriesID 
      fromDatasetDir: (NSString *) aDir 
        withFiles: (NSArray *) filesArg 
      andCallbackObject: (UploadDatasetVC *) aCallback; 

- (void) createDir; 

@end 

FTPUploads.m अंश

#import "FTPUploads.h" 
#import "UploadDatasetVC" 

@implementation FTPUploads 

- (id) initWithTimeseriesID: (int) aTimeseriesID 
      fromDatasetDir: (NSString *) aDir 
        withFiles: (NSArray *) filesArg 
      andCallbackObject: (UploadDatasetVC *) aCallback { 

    self = [super init]; 

    if (self) { 

     uploadDir = [NSString stringWithFormat: @"ftp://aServer.org/%i/", aTimeseriesID]; 
     ftpUser = @"aUser"; 
     ftpPass = @"aPass"; 

      datasetDir = aDir; 
      files = filesArg; 

     bufferOffset = 0; 
     bufferLimit = 0; 

     index = 0; 

     callback = aCallback; 
    } 

    return self; 
} 

- (void) createDir { 

    uploadMode = UploadModeCreateDir; 
    NSURL *destinationDirURL = [NSURL URLWithString: uploadDir]; 

    CFWriteStreamRef writeStreamRef = CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) destinationDirURL); 
    assert(writeStreamRef != NULL); 

    ftpStream = (__bridge_transfer NSOutputStream *) writeStreamRef; 
    [ftpStream setProperty: ftpUser forKey: (id)kCFStreamPropertyFTPUserName]; 
    [ftpStream setProperty: ftpPass forKey: (id)kCFStreamPropertyFTPPassword]; 

    ftpStream.delegate = self; 
    [ftpStream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode]; 
    // open stream 
    [ftpStream open]; 

    CFRelease(writeStreamRef); 
} 

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { 

    NSLog(@"aStream has an event: %i", eventCode); 

    switch (eventCode) { 
     // all cases handled properly 
     default: 
      // no event 
      NSLog(@"default mode; no event"); 
      break; 
    } 
} 

संपादित: उस वर्ग UploadDatasetVC में प्रयोग किया जाता है जोड़ा सृजन कोड:

FTPUploads *uploads = [[FTPUploads alloc] initWithTimeseriesID: timeseries_id 
               fromDatasetDir: datasetDir 
                withFiles: files 
              andCallbackObject: self]; 
[uploads createDir]; 
+0

ऑब्जेक्ट्स आम तौर पर अपने प्रतिनिधियों को बनाए रखने (या चाप के मामले में, मजबूत संदर्भ बनाए रखने) नहीं रखते हैं। मुझे लगता है कि समस्या उस कोड के साथ होगी जो आपके FtpUploads ऑब्जेक्ट बनाता है और इसके बाद यह क्या करता है –

+0

मैं उस कोड को प्रश्न के दूसरे स्निपेट में जोड़ रहा हूं। पहले जवाब पर भी मेरी टिप्पणी देखें। –

+0

इसके साथ क्या होता है? यदि अपलोड केवल एक स्थानीय चर है, जब यह दायरे से बाहर हो जाता है तो एआरसी इसे –

उत्तर

3

ऐसा लगता है कि आपकी FTPUploads ऑब्जेक्ट का एकमात्र संदर्भ स्ट्रीम पर delegate संपत्ति है। यह आपकी ऑब्जेक्ट को बरकरार नहीं रखेगा, इसलिए अगर किसी और के पास ऑब्जेक्ट का संदर्भ नहीं है, तो ऑब्जेक्ट को हटा दिया जाएगा। A.R.C. इस परिदृश्य को रोकने की कोशिश नहीं करता है।

आपको जो करना है वह है FTPUploads ऑब्जेक्ट को ऑब्जेक्ट का संदर्भ पूरा होने तक ऑब्जेक्ट का संदर्भ रखें।

ftpStream.delegate प्रॉपर्टी को FTPUploads डेलोक विधि में शून्य करने के लिए यह भी एक बुरा विचार नहीं होगा, क्योंकि ऑब्जेक्ट को समय-समय पर डिलीक किया गया है, तो यह क्रैश को रोक देगा।

+0

नहीं; FTPUploads ऑब्जेक्ट बनाने के लिए कॉलिंग कोड उस ऑब्जेक्ट का पहला संदर्भ रखता है? 'createDir' विधि में मैंने एक प्रतिनिधि के रूप में 'स्वयं' सेट किया - ठीक है। लेकिन पहला संदर्भ अपलोडडेटासेटिव में बनाया गया है, जहां मैं एचटीई एफ़टीपी अपलोड करता हूं ऑब्जेक्ट: 'एफ़टीपीउप्लोड्स * अपलोड = [[एफ़टीपीउप्लोड्स ऑलोक] initWithTimeseriesID: ​​timeseries_id डेटासेटडियर से: डेटासेट डीयर साथ फाइलें: फाइल और कॉलबैक ऑब्जेक्ट: स्वयं]; [अपलोड बनाता है डीआईआर]; ' –

+0

अधिकांश प्रतिनिधि गुण "कमजोर" या "असाइन" के रूप में सेट होते हैं, जिसका अर्थ है कि वे बनाए रखने की गिनती की ओर गिनती नहीं करते हैं। आपके 'अपलोड' चर केवल तभी गिना जाएगा जब तक कनेक्शन के दौरान यह अस्तित्व में रहेगा, जिसे मैं अनुमान लगा रहा हूं। – grahamparks

+1

ठीक है, एक वर्कअराउंड उपयोग किए गए FTPUploads ऑब्जेक्ट को मजबूत संदर्भ वाले गुण के रूप में सहेजना है। यह मेरे लिए सबसे अच्छा काम करता है; निम्न डेटा को UploadDataset.h में जोड़ना: '@property (strong) FTPUploads * अपलोड;' और अपलोडडासेटसेट में ऑब्जेक्ट सृजन को अनुकूलित करें। तदनुसार –

0

मेरा अनुमान है कि आप या तो इससे पहले कि आप जारी अपने writeStreamRef

+0

गलत अनुमान है। कारण केवल स्थानीय चर के रूप में FTPUploads की गलत घोषणा थी (मूल प्रश्न में अपलोडडेटासेटवर्क निर्माण कोड देखें)। फिर भी आप मेरे गलत '__bridge_transfer' के संबंध में सही हैं जो एक बग बन गया होगा। मैंने बस इसे ठीक किया, धन्यवाद! –

1

समस्या यह है कि आपके ftpStream वस्तु जा रहा है इंतजार करना चाहिए जब तक धारा CFRelease(writeStreamRef) करने के लिए समाप्त हो गया है, या एक __bridge_transfer करना ftpStream के लिए खत्म हो स्वामित्व का हस्तांतरण करना है deallocated। आप इसे CFWriteStreamCreateWithFTPURL() के साथ बनाते हैं, फिर इसे CFRelease() के साथ रिलीज़ करें। आपने __bridge कास्ट का उपयोग किया, जिसका मूल रूप से अर्थ है "इस असाइनमेंट पर कोई मेमोरी प्रबंधन न करें"। इसलिए जब आपने इसे ftpStream पर असाइन किया था तो एआरसी ने इसे बरकरार रखा नहीं था। चूंकि आपका इरादा सीएफ से एआरसी में स्वामित्व स्थानांतरित करना था, इसलिए इसका उपयोग करने के लिए गलत कास्ट था।

आप वास्तव में __bridge_retained या __bridge_transfer चाहते थे। मैं कभी याद नहीं कर सकता कि कौन सा है, हालांकि। सौभाग्य से, एक और विकल्प है- CFBridgingRetain() और CFBridgingRelease() मैक्रोज़। वे वही ब्रिजिंग कास्ट करते हैं, लेकिन उन्हें अधिक स्पष्ट रूप से नामित किया जाता है।

इस मामले में, आप सीएफ को इसे जारी करना चाहते हैं, लेकिन इसे एआरसी पर पुल करें। तो आप CFBridgingRelease() चाहते हैं। इससे एआरसी को ऑब्जेक्ट का स्वामित्व लेने के लिए कहा जाएगा, और फिर एक CFRelease करें। संक्षेप में, यह बदल देते हैं:

ftpStream = (__bridge NSOutputStream *) writeStreamRef; 
इस के साथ

:

ftpStream = CFBridgingRelease(writeStreamRef); 

और फिर कुछ लाइनें बाद में CFRelease() करने के लिए कॉल को हटा दें।

+0

गलत। जैसा कि मेरे मूल प्रश्न में बताया गया है कि समस्या FTPUploads ऑब्जेक्ट को हटाया जा रहा है - ftpStream नहीं! यह मेरे कोड में बस एक और बग है लेकिन मूल समस्या नहीं है। संकेत के लिए धन्यवाद –

+1

ओह, सच है। खैर, ठीक करने के बाद (जो मैं देखता हूं कि आप पहले से ही कर चुके हैं), यह आपकी अगली समस्या होगी। –