【发布时间】:2012-02-05 14:59:09
【问题描述】:
我有一个问题,似乎是在基于 ARC 的应用程序中过早释放正在使用的对象。我正在尝试在 FTP 服务器上创建一个文件夹。代码的相关部分如下;我先描述一下问题。
代码的问题是,在
中的调试输出- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
永远不会调用方法。
相反,我只是收到一个 _EXC_BAD_ACCESS_ 错误。 在调试时我发现了两件事:
-
只有在执行以下代码行(createDir 方法)时才会出现错误:
[ftpStream open];
如果未发送该消息,则其余代码实际上没有意义 - 但它也不会崩溃...
-
我使用 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;
}
}
EDIT:添加了在UploadDatasetVC类中使用的创建代码:
FTPUploads *uploads = [[FTPUploads alloc] initWithTimeseriesID: timeseries_id
fromDatasetDir: datasetDir
withFiles: files
andCallbackObject: self];
[uploads createDir];
【问题讨论】:
-
对象通常不保留(或者在 arc 的情况下,保留对其委托的强引用)。在我看来,问题在于创建您的 FtpUploads 对象的代码以及它随后对它的作用
-
我将该代码添加到问题的另一个 sn-p 中。另请参阅我对第一个答案的评论。
-
然后会发生什么?如果上传只是一个局部变量,那么当它超出范围时,ARC 会删除它
-
是的,你完全正确;请参阅下面的@grahamparks 答案,了解我为解决此问题所做的工作。使它成为一个类变量是不够的,所以我为上传创建了一个属性。
标签: objective-c automatic-ref-counting dealloc