Issue with extensions in Objective-C
Asked Answered
V

1

8

The following is a code snippet which deals with class extensions. What I am trying to do is generate a random ID called internal ID (that is used by the program later on) which is stored in an encrypted form in memory. The code fails to compile with both gcc and clang (I am running Objective C via GNUStep on Windows) with different error messages for each compiler which is mentioned as comments in the code.

Please note that I'm aware of the fact that this problem can be easily solved by disregarding the use of extensions and declaring methods and properties in the main @interface (i.e the one after the #import statement) itself. The only reason I'm going with extensions is that this class is inherited by a few other sub-classes to which the "internalID" property must be inaccessible.

#import <Foundation/Foundation.h>

@interface Foo : NSObject
{
    NSString * idStr;
}
- (void)setInternalID;
- (NSString *)fetchExternalID;
@end

// Extension declaration
@interface Foo()
{ // Compilation via gcc throws error at this line stating "Expected identifier or '(' before '{' token"
    NSString *internalID; // Compilation via clang throws error at this line stating "instance variables may not be placed in class extension"
}
@end

@implementation Foo
- (void)setInternalID{
    internalID = [NSString stringWithFormat: 
    @"__KEY__INTERNAL__DUMP2872167841398551___8765%d98KLPYFGF(&^$#ESFK___JNHGV",arc4random()%100];
}
- (NSString *)fetchExternalID{
    NSString * externalID=[internalID stringByReplacingOccurrencesOfString: 
    @"__KEY__INTERNAL__DUMP2872167841398551___8765" withString:@""];
    externalID=[externalID stringByReplacingOccurrencesOfString: 
    @"98KLPYFGF(&^$#ESFK___JNHGV" withString:@""];
    return externalID;
}
@end

int main(int argc, const char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    Foo * bar = [[Foo alloc]init];
    [bar setInternalID];
    NSLog(@"External ID: %@",[bar fetchExternalID]);        
    [pool drain];
    return 0;
}

The compilation string for gcc I'm using is gcc -o _exec fwdORM.m -I /GNUstep/System/Library/Headers -L /GNUstep/System/Library/Libraries -lobjc -lgnustep-base -fconstant-string-class=NSConstantString

My GNUmakefile for clang is as follows:

#
# Command script for clang 
# C:\GNUstep\msys\1.0\home\hecate\my\GNUmakefile
#

GNUSTEP_MAKEFILES=/GNUStep/System/Library/Makefiles

include $(GNUSTEP_MAKEFILES)/common.make

TOOL_NAME = fwdORM

fwdORM_OBJC_FILES = fwdORM.m
fwdORM_OBJCFLAGS = -std=c99

# Implementation mixing Objective-C/CPP file going to be compiled; Currently not required
#fwdORM_OBJCC_FILES = fwdORM_21.mm

# Implementation CPP file going to be compiled; Currently N/A
#fwdORM_CC_FILES = sec_class_exec.cpp

# Header files of project
#fwdORM_HEADER_FILES = .h // N/A

# Define compilation flags
ADDITIONAL_CPPFLAGS = -Wall -Wno-import -fblocks

# Include rules for creating a command line tool for Objective-C
include $(GNUSTEP_MAKEFILES)/tool.make

Thanks in advance.

Vasti answered 19/3, 2017 at 2:57 Comment(4)
I cannot reproduce the error with clang/Mac OS. However, to reach your goal, it is better to define the ivar at @implementation.Jaquelynjaquenetta
@AminNegm-Awad Hey, can you be a bit more specific on your recommended way to solve it? Because according to what I know, all ivars must be placed in the '@interface'. And when you say you have no error in clang/Mac OS, did you implement the same GNUmakefile? I don't think it is possible for the code to compile in Mac OS and fail in GNUStep/Windows. Sorry for the late reply. I was pretty engrossed in another part of the project :)Vasti
No, I did it without makefile. It is not true, that ivars has to be defined in a @interface section. You can do the same thing at '@implementation`.Jaquelynjaquenetta
@Vasti is there a reason you're making internalId an ivar instead of a property in the class extension? With a property, you still get the same "private" behavior, and in fact it's the currently recommended semantics for this sort of thing.Mathews
M
4

Try implementing this by using a @property declared in the class extension instead of an ivar:

#import <Foundation/Foundation.h>

@interface Foo : NSObject
- (void)setInternalID;
- (NSString *)fetchExternalID;
@end

// Extension declaration
@interface Foo()
@property (strong) NSString *internalID;
@end

@implementation Foo

- (void)setInternalID {
    self.internalID = [NSString stringWithFormat:@"__KEY__INTERNAL__DUMP2872167841398551___8765%d98KLPYFGF(&^$#ESFK___JNHGV",arc4random()%100];
}

- (NSString *)fetchExternalID {
    NSString * externalID = [self.internalID stringByReplacingOccurrencesOfString:@"__KEY__INTERNAL__DUMP2872167841398551___8765" withString:@""];
    externalID = [externalID stringByReplacingOccurrencesOfString:@"98KLPYFGF(&^$#ESFK___JNHGV" withString:@""];
    return externalID;
}

@end

This is standard semantics for what's effectively a private variable, and probably stands a better chance of getting compiler support.

Mathews answered 19/4, 2017 at 17:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.