Archived Forum Post

Index of archived forum posts

Question:

iOS FTP2 Callback Events - Unrecognized Selector

Aug 05 '14 at 18:30

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?


Accepted Answer

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).


Answer

I'll have to give it a test tomorrow to see if I can reproduce the problem..


Answer

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.