10

I'm developing an iOS application that needs to deploy to iOS 3.1.3. I need to extend some of the functionality of the NSData class and am using the following code inside NSData+Base64 (truncated to show the interesting part):

NSData+Base64.h

[...]

@interface NSData (Base64)

+ (NSData *)dataFromBase64String:(NSString *)aString;
- (NSString *)base64EncodedString;

@end

NSData+Base64.m

@implementation NSData (Base64)

[...]

//
// base64EncodedString
//
// Creates an NSString object that contains the base 64 encoding of the
// receiver's data. Lines are broken at 64 characters long.
//
// returns an autoreleased NSString being the base 64 representation of the
//  receiver.
//
- (NSString *)base64EncodedString
{
    size_t outputLength;
    char *outputBuffer =
        NewBase64Encode([self bytes], [self length], true, &outputLength);
    
    NSString *result =
        [[[NSString alloc]
            initWithBytes:outputBuffer
            length:outputLength
            encoding:NSASCIIStringEncoding]
        autorelease];
    free(outputBuffer);
    return result;
}

@end

However, when I try to message this selector:

NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSString *hash = [HMAC base64EncodedString];

I get the following error:

 -[NSConcreteData base64EncodedString]: unrecognized selector sent to instance 0x6146e70
2010-11-09 13:44:41.443 SpringboardApplication[21318:40b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteData base64EncodedString]: unrecognized selector sent to instance 0x6146e70'

I read a lot about iOS 3.1.x having problems with categories. I tried adding the flags -all_load and -ObjC (both separately and together) to no avail. I would really appreciate some direction of how to get this selector to work.

Thanks!

Community
  • 1
  • 1
Daniel
  • 872
  • 1
  • 11
  • 20
  • Just to be clear, your app is working fine in 3.2 and 4.x, right? You implied that, but just wanted to confirm. – Firoze Lafeer Nov 09 '10 at 20:39
  • I actually hadn't even tried. It has to deploy to 3.1.3 so I didn't even bother. – Daniel Nov 09 '10 at 20:40
  • Just tried. I changed the deploy target to iOS 4.0, cleaned, and tried a rebuild and run. Same error. – Daniel Nov 09 '10 at 20:41
  • you should update your question so people aren't thinking it is a 3.1.x specific issue. So is your .m file for this category in your target in the build phase? – Firoze Lafeer Nov 09 '10 at 20:44
  • Thanks, I updated the question. As far I know the .m is included in the target (the target checkbox is checked). – Daniel Nov 09 '10 at 20:49

5 Answers5

19

It really seems like your category isn't being compiled or linked into the same target that you're using it from. You should make sure that NSData+Base64.m is marked to be compiled by the same target that it's being used from by getting info on the two files and comparing the targets they're assigned to.

A test you can perform is to add a line with an #error error message to NSData+Base64.m, which will cause the build to fail when it gets to that file. Like this:

#error We're now compiling NSData+Base64.m

Then look and see which target fails to compile.

Ryan
  • 16,626
  • 2
  • 23
  • 20
  • I added this line into both NSData+Base64.m as well as the file I'm trying to reference it from (UploadView.m). When I run a build, the compiler shows both errors under the same target (which is the build of the entire project: "Build IP_Module"). – Daniel Nov 10 '10 at 05:41
  • Strange, it sounds like your compilation settings are correct. What happens if you run this code in Xcode's debugger (after removing the #error statement of course)? Specifically, when it crashes, Xcode should bring you to the point of the crash. What happens if you try to print the description of the object that isn't responding to this selector, e.g. with the error string you provided, by typing this into Xcode's console debugger window: "po 0x6146e70" – Ryan Nov 10 '10 at 08:38
  • If I do a po of the object that isn't responding to the selector, it gives me: <1a79019d 1da37de9 efba78af 2112ce62 ecee5682 740b259d 552cdd24 d3059c9f>. This sounds right since it is supposed to be an NSData object. – Daniel Nov 10 '10 at 14:13
  • 1
    Let's try to determine that the linker is picking up NSData+Base64.m's object file. Add a C function to NSData+Base64.m, outside of the category, and declare it in NSData+Base64.h. It'll have nothing to do with the category, but it'll get compiled into the same .o file. Then, just before you call -base64EncodedString in your code, call your new C function. Compile and run to see if your C function will compile, link, and run properly. If so, we know your .m file is being compiled and linked properly. E.g. void doesLinkingWork() { NSLog(@"If you can read this, linking works."); } – Ryan Nov 10 '10 at 16:58
  • Okay, I actually just figured it out. When I posted the question, I left out the structure of my program because I didn't think it was important; turns out it was. I'm working on a project which is a dependent "subproject" of a larger project. This subproject needs to used the category of NSData so I just included the header file in the subproject. But, that must have been too late to add a category to NSData. I moved the category to the main project and it works fine. I would still appreciate further insight into when categories must be declared. – Daniel Nov 10 '10 at 17:06
  • having the same problem ... plz help http://stackoverflow.com/questions/14433512/linking-error-of-a-header-file-in-xcode-when-made-a-static-libary#comment20094392_14433512 – Subbu Jan 21 '13 at 07:10
8

I had the same issue with ARC project which was linking with non-ARC module having category extension.

Fixed the issue by adding "Other Linker Flags: -all_load" in parent ARC project.

Ramesh
  • 1,703
  • 18
  • 13
2

There is a great post on The Carbon Emitter about about handling categories in iOS. It details an easy way to handle importing categories to your project.

Make a file containing all of your category imports, in this example it is Extensions.h:

#import "NSDate+Formatting.h"
#import "UIFonts+MyFonts.h"
#import "UIViewController+Tourbot.h"

Add import your file in AppName-Prefix.pch:

#import <Availability.h>

#ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later."
#endif

#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#import <CoreText/CoreText.h>
#import "Extensions.h" // Add import here
#endif
Chris Knadler
  • 2,819
  • 2
  • 26
  • 32
2

In My case when I got this error I simply added the .m file in the Compiled resources, and it get worked. This can be achieved by selecting target project->Build Phases->Compile Sources. Then you click on the + button from its bottom left. In this case you may add 'NSData+Base64.m' file to the compile sources. Then you clean your project and run. I guess this may help.

rejusss
  • 146
  • 4
2

Have you #imported the header file for your category? I know it sounds simple, but I forget nearly every time.

Brian
  • 15,599
  • 4
  • 46
  • 63
  • I often forget too, but not this time! XCode's code sense finds the selector and even suggests it. – Daniel Nov 09 '10 at 20:13
  • since it's a runtime and not compile-time error the problem would be the .m file not being compiled. – cobbal Nov 09 '10 at 20:17
  • Any suggestions as to why the .m file wouldn't be getting compiled would be greatly appreciated. – Daniel Nov 09 '10 at 20:24