Premature dealloc in ARC-based application

I have a problem that seems to be a premature release of an object used in an ARC application. I am trying to create a folder on an FTP server. Below are the relevant parts of the code; I will first describe the problem.

the problem with the code is that the debug output in

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

the method is never called.

Instead, I just get the _EXC_BAD_ACCESS_ error. During debugging, I learned two things:

  • The error appears only if the following line of code is executed (createDir method):

    [ftpStream open];
    

if this message is not sent, the rest of the code doesn't really make sense - but it doesn't crash ...

  • I tracked EXC_BAD_ACCESS using NSZombieEnabled: with zombie objects enabled, GDB creates the following debug information:

     *** -[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;
    }
}

EDIT: , UploadDatasetVC:

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

, FTPUploads delegate . , , , . A.R.C. .

, , FTPUploads, .

ftpStream.delegate noil dealloc FTPUploads, , .

+3

, ftpStream . CFWriteStreamCreateWithFTPURL(), CFRelease(). __bridge, " ". ARC , ftpStream. CF ARC, .

__bridge_retained, __bridge_transfer. , . , - CFBridgingRetain() CFBridgingRelease(). , .

, CF , ARC. , CFBridgingRelease(). ARC , CFRelease. , :

ftpStream = (__bridge NSOutputStream *) writeStreamRef;

:

ftpStream = CFBridgingRelease(writeStreamRef);

CFRelease() .

+1

I assume that you need to either wait until the stream ends to do CFRelease(writeStreamRef), or do __bridge_transfer to transfer ownership of ftpStreambefore you releasewriteStreamRef

0
source

All Articles