Question:
Hello. I'm trying out the FTP2 component in my iOS app. I'm was having very good success... until I attempted to implement progress callback handling via CkoFTP2Progress.
I'm not sure what I've missed, but at the moment of [ftp Connect], I get the following exception:
-[__NSCFNumber stringWithUtf8:]: unrecognized selector sent to instance 0xa51c060
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber stringWithUtf8:]: unrecognized selector sent to instance 0xa51c060'
*** First throw call stack:
(
0 CoreFoundation 0x037731e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x031a18e5 objc_exception_throw + 44
2 CoreFoundation 0x03810243 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
3 CoreFoundation 0x0376350b ___forwarding___ + 1019
4 CoreFoundation 0x037630ee _CF_forwarding_prep_0 + 14
5 ProtectPlus 0x00272706 _ZN14MmFtp2Progress12ProgressInfoEPKcS1_ + 50
6 ProtectPlus 0x002b2925 _ZN8CkFtp2Cb15pevProgressInfoEPKcS1_ + 39
7 ProtectPlus 0x005de6f9 _ZN15ProgressMonitor12progressInfoEPKcS1_ + 39
8 ProtectPlus 0x004b81d1 _ZN13ChilkatSocket13connectSocketEPKcibjjR12SocketParamsR7LogBaseRb + 301
9 ProtectPlus 0x005fe0cc _ZN15SChannelChilkat9pmConnectEPKcibjR7LogBaseRbP15ProgressMonitor + 114
10 ProtectPlus 0x005fee50 _ZN15SChannelChilkat18connectImplicitSslEPKcibijjR11SystemCertsR12SocketParamsR7LogBaseRbP15SharedCertChain + 232
11 ProtectPlus 0x0061b336 _ZN7Socket28connect2EPKcibbiR11SystemCertsjjR12SocketParamsR7LogBaseRbRiP15SharedCertChain + 756
12 ProtectPlus 0x0061b00d _ZN7Socket214socket2ConnectEPKcibbiR11SystemCertsjjR12SocketParamsR7LogBaseRbRiP15SharedCertChainR14ClsSocksClientP18ClsHttpProxyClient + 757
13 ProtectPlus 0x0053b945 _ZN4Ftp27ConnectER14ClsSocksClientR18ClsHttpProxyClientR7LogBaseP15ProgressMonitorb + 837
14 ProtectPlus 0x00379389 _ZN7ClsFtp212connectInnerEP13ProgressEventPKcbb + 739
15 ProtectPlus 0x00376e89 _ZN7ClsFtp27ConnectEP13ProgressEvent + 79
16 ProtectPlus 0x002acd29 _ZN6CkFtp27ConnectEv + 83
17 ProtectPlus 0x0022b439 -[CkoFtp2 Connect] + 32
I've worked on this awhile, but I can't yet see what I'm doing wrong. Here's my interface:
@interface PPTaskFTPProgress : CkoFtp2Progress {
}
- (id) initWithFile: (PPAudioFile *) file andDestination:(PPDestination *) destination;
- (void) AbortCheck:(BOOL *)abort;
- (void) BeginUploadFile:(NSString *)path skip:(BOOL *)skip;
- (void) EndUploadFile:(NSString *)path numBytes:(NSNumber *)numBytes;
- (void) PercentDone:(NSNumber *)pctDone abort:(BOOL *)abort;
- (void) ProgressInfo:(NSString *)name value:(NSString *)value;
- (void) UploadRate:(NSNumber *)byteCount bytesPerSec:(NSNumber *)bytesPerSec;
- (void) VerifyUploadDir:(NSString *)path skip:(BOOL *)skip;
@end
And then here is my implementation of CkoFtp2Progress:
@interface PPTaskFTPProgress ()
{
}
@property (strong, nonatomic) PPDestination *destination;
@property (strong, nonatomic) PPAudioFile *file;
@end
@implementation PPTaskFTPProgress
- (id) initWithFile: (PPAudioFile *) file andDestination:(PPDestination *) destination {
self = [super init];
if (self) {
_file = file;
_destination = destination;
}
return self;
}
// AbortCheck frequency is controlled by ftp client object HeartBeatMs property
- (void) AbortCheck:(BOOL *)abort {
DDLogDebug(@"AbortCheck called");
*abort = NO;
}
- (void) BeginUploadFile:(NSString *)path skip:(BOOL *)skip {
DDLogDebug(@"BeginUploadFile called. path = %@", path);
}
- (void) EndUploadFile:(NSString *)path numBytes:(NSNumber *)numBytes {
DDLogDebug(@"EndUploadFile called");
DDLogDebug(@"path = %@ / numBytes %@", path, numBytes);
}
- (void) PercentDone:(NSNumber *)pctDone abort:(BOOL *)abort {
DDLogDebug(@"PercentDone called: %@", pctDone);
*abort = NO;
}
- (void) ProgressInfo:(NSString *)name value:(NSString *)value {
DDLogDebug(@"ProgessInfo called. name = %@, value = %@", name, value);
}
- (void) UploadRate:(NSNumber *)byteCount bytesPerSec:(NSNumber *)bytesPerSec {
DDLogDebug(@"UploadRate called. byteCount = %@, bytesPerSec = %@", byteCount, bytesPerSec);
}
- (void) VerifyUploadDir:(NSString *)path skip:(BOOL *)skip {
DDLogDebug(@"VerifyUploadDir called. path = %@", path);
*skip = NO;
}
@end
For completeness:
UnlockComponent: DllDate: Jul 31 2014 ChilkatVersion: 9.5.0.43 UnlockPrefix: NONE Architecture: Little Endian; 32-bit Language: IOS Objective-C VerboseLogging: 0 component: Ftp2 unlockCode: Anything for a 30-Day Trial regKeyUnlock: mdy: 8/1/2014 Component successfully unlocked using trial key --regKeyUnlock Success. --UnlockComponent --ChilkatLog
In my other PPTaskFTP.mm class:
property (strong, nonatomic) CkoFtp2 *ftp;
- (void) startFTP {
self.ftp = [[CkoFtp2 alloc] init];
BOOL success = [self.ftp UnlockComponent:mChilkatLicense];
PPTaskFTPProgress *progressCallbackHandler = [[PPTaskFTPProgress alloc] initWithFile:self.file andDestination:self.destination];
[self.ftp setEventCallbackObject:progressCallbackHandler];
self.ftp.Hostname = self.destination.url;
self.ftp.Port = [NSNumber numberWithInteger:self.destination.port];
self.ftp.Username = self.destination.username;
self.ftp.Password = self.destination.password;
self.ftp.Passive = YES;
// Exception HERE
BOOL success = [self.ftp Connect];
}
Any ideas?
I found the cause of the problem. Your progress object is created as a local variable in this method:
- (void) configureFTP { self.ftp = [[CkoFtp2 alloc] init];MyProgress *progress = [[MyProgress alloc] init]; [self.ftp setEventCallbackObject:progress];
}
When the configureFTP method exits, the progress object is deallocated and later when the callback occurs, it is sent to another object that is occupying the memory that the progress object was previously occupying. The solution is to make the progress object a property of the class just like the ftp object (so that it does not get deallocated).
I'll have to give it a test tomorrow to see if I can reproduce the problem..
Aaaah... I see. I assumed that MyProgress would be "retained," if you will, once set as the event callback. But I guess that's an invalid assumption. Yes, this fixes my issue. Great catch. Many thanks.