On Debian 8 GNU/Linux, I'm trying to compile some Objective-C code from this tutorial using Clang and GNUstep make. I ran the following commands to install a build pipeline:
sudo apt-get install build-essential clang gnustep-devel
These are the files I am using:
GNUmakefile:
CC = clang
include $(GNUSTEP_MAKEFILES)/common.make
TOOL_NAME = Main
Main_OBJC_FILES = main.m Car.m
include $(GNUSTEP_MAKEFILES)/tool.make
main.m:
#import <Foundation/Foundation.h>
#import "Car.h"
int main () {
@autoreleasepool {
Car *toyota = [[Car alloc] init];
[toyota setModel:@"Toyota Corolla"];
NSLog(@"Created a %@", [toyota model]);
toyota.model = @"Toyota Camry";
NSLog(@"Changed the car to a %@", toyota.model);
[toyota drive];
}
return 0;
}
Car.h:
#import <Foundation/Foundation.h>
@interface Car : NSObject {}
@property (copy) NSString *model;
- (void)drive;
@end
Car.m:
#import "Car.h"
@implementation Car {
double _odometer;
}
@synthesize model = _model;
- (void)drive {
NSLog(@"Driving a %@. Vrooom!", self.model);
}
@end
I am compiling with:
. /usr/share/GNUstep/Makefiles/GNUstep.sh && make -k
When I compile, I get the following errors:
Car.m:4:10: error: inconsistent number of instance variables specified
double _odometer;
^
Car.m:7:13: error: synthesized property 'model' must either be named the same as a compatible instance variable or must
explicitly name an instance variable
@synthesize model = _model;
To fix the errors, I need to update the Car interface to:
@interface Car : NSObject {
NSString * _model;
double _odometer;
}
And the Car implementation to:
@implementation Car {
NSString * _model;
double _odometer;
}
It seems like my current compilation setup is forcing me to write more code than the tutorial suggests is needed in order to compile. In fact, in the tutorial, the author specifically recommends against including "protected instance variables" in the interface:
@interface Car : NSObject {
// Protected instance variables (not recommended)
}
I feel like I am using an outdated / misconfigured compilation setup. Am I? If so, how can I fix it so I can write code like in the referenced tutorial? (I'd prefer to use GNUstep make rather than the Clang CLI, if possible.)
There is a mistake in the tutorial that you read (or at least, missing information) -- in order to use
@synthesize model = _model;, you indeed need to define an instance variable named_model; when you do, the error goes away.Let's take a step back, though. A long time ago, Objective-C didn't have
@propertysyntax to define properties. This tutorial shows@property (copy) NSString *model;, but doesn't immediately get into what that means. (Read the linked "properties module" to see more from the author about this subject.)Before
@propertysyntax, you defined all of your instance variables manually:In order to allow users to access your instance variables, it was good practice to write setters and getters (e.g.
-modeland-setModel:), and to leave your instance variables inaccessible. This follows the OOP model of protecting access to your inner state and allowing access only through a presentable interface.This could be quite a tedious process, and was a lot of boilerplate (especially with manual memory management, KVO compliance, etc.).
@propertysyntax came along to help with that.When you declare an
@property, it defines an instance variable for you, as well as implicitly creating the getter and setter methods. You can specify the memory management model to use in these getters and setters (e.g.strong,weak,assign,copy, etc.), atomicity, and so on. If you have a property calledmodel,@propertywill generate a_modelinstance variable, along with-modeland-setModel:(unless you define only a getter).It used to be, however, that
@propertydid not create an instance variable for you, only the getter and setter. If you wanted an instance variable, you had to either create it yourself, or explicitly request that the compiler do it. This is where the@synthesizeline comes in.@synthesize model = _model;tells the compiler that you have created a_modelinstance variable yourself, and that themodelproperty should use_modelas the storage. The leading underscore for the property was simply convention to help differentiate between the property itself (alias for a setter/getter) and the underlying storage.In any case, those are details of the past. With a modern compiler, you will not need an
@synthesizeline, as the compiler will generate the instance variable for you. Either add the variable yourself and keep the@synthesize model = _model;line (not recommended), or leave that line out.Note also that if you do declare your own instance variables, they do not need to be repeated between your
@interfaceand@implementation-- pick a place and put them there. I recommend the@implementation, since they need not be visible, but that is up to you.