У меня проблема, которая, похоже, заключается в преждевременном освобождении неиспользуемого объекта в приложении на базе ARC. Я пытаюсь создать папку на FTP-сервере. Соответствующие части кода приведены ниже; сначала я опишу проблему.
Проблема в том, что отладочный вывод в
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
методе никогда не вызывается.
Вместо этого я просто получаю ошибку _EXC_BAD_ACCESS_. В процессе отладки я обнаружил две вещи:
ошибка появляется только если выполняется следующая строка кода (метод createDir):
[ftpStream open];
если это сообщение не отправляется, остальная часть кода не имеет смысла - но и сбоя не происходит...
Я отследил EXC_BAD_ACCESS с помощью NSZombieEnabled: При включенных зомби-объектах GDB выдает следующую информацию отладчика:
*** -[FTPUploads respondsToSelector:]: сообщение отправлено деаллоцированному экземпляру 0x9166590
Указанный адрес 0x9166590 является адресом моего объекта FTPUploads. Похоже, что делегат потоков деаллоцируется до того, как он сможет обрабатывать сообщения.
Почему система деаллоцирует используемый объект? Как я могу предотвратить преждевременное освобождение объекта?
код:
FTPUploads.h excerpt:
#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 excerpt
#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];