在Debian 8 GNU/Linux上编译Objective-C使用Clang和GNUstep make

问题描述:

在Debian 8 GNU/Linux上,我试图用Clang和GNUstep make编译一些来自this tutorial的Objective-C代码。我跑到下面的命令来安装构建流水线:在Debian 8 GNU/Linux上编译Objective-C使用Clang和GNUstep make

sudo apt-get install build-essential clang gnustep-devel 

这是我使用的文件:

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 

我与编译:

. /usr/share/GNUstep/Makefiles/GNUstep.sh && make -k 

当我编译,我得到以下错误:

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; 

修正错误的,我需要将Car接口更新为:

@interface Car : NSObject { 
    NSString * _model; 
    double _odometer; 
} 

与汽车实施:

@implementation Car { 
    NSString * _model; 
    double _odometer; 
} 

这似乎是我目前的编译安装程序迫使我比教程提出,需要以编译编写更多的代码。事实上,在本教程中,笔者特别建议对包括在界面中的“受保护的实例变量”:

@interface Car : NSObject { 
    // Protected instance variables (not recommended) 
} 

我觉得像我使用的是过时的/编译错误配置设置。我是吗?如果是这样,我该如何解决这个问题,这样我就可以像引用的教程中那样编写代码了? (如果可能的话,我倾向于使用GNUstep make而不是Clang CLI。)

本教程中存在错误,您阅读(或至少缺少信息) - 为了使用@synthesize model = _model;,您确实需要定义一个名为_model的实例变量;当你这样做时,错误消失。

不过,让我们退后一步。很久以前,Objective-C没有定义属性的语法。本教程显示@property (copy) NSString *model;,但不立即进入这意味着什么。 (阅读链接“properties module”看到从笔者详细了解此问题。)

@property语法之前,您手动定义所有的实例变量:

// In MyClass.h 
@interface MyClass : NSObject { 
    // Visible instance variables. Can be marked @public, @private, and @protected to prevent access. 
} 
... 
@end 

为了让用户访问您的实例变量,编写setter和getters(例如-model-setModel:)是不错的做法,并且让您的实例变量不可访问。这遵循OOP模型来保护对内部状态的访问,并且只允许通过可呈现的界面访问。

这可能是一个相当乏味的过程,并且有很多样板(尤其是手动内存管理,KVO合规性等)。语法来帮助那个。

当您声明@property时,它为您定义了一个实例变量,并且隐式创建了getter和setter方法。您可以指定要在这些获取者和设置者中使用的内存管理模型(例如strong,weak,assign,copy等),原子性等。如果您有一个名为model的属性,则@property将生成一个_model实例变量,以及-model-setModel:(除非您只定义一个吸气剂)。

它曾经是@property确实不是为你创建一个实例变量,只有getter和setter。如果你想要一个实例变量,你必须自己创建它,或者明确地要求编译器执行它。这是@synthesize行的地方。@synthesize model = _model;告诉编译器您自己创建了一个_model实例变量,并且model属性应该使用_model作为存储。该属性的主要下划线仅仅是用来区分属性本身(setter/getter的别名)和底层存储的约定。

无论如何,这些都是过去的细节。使用现代编译器,您不需要@synthesize行,因为编译器会为您生成实例变量。您可以自己添加变量并保持@synthesize model = _model;一行(不推荐),或者保留该行。

还要注意的是,如果你做声明自己的实例变量,他们需要您@interface@implementation之间重复 - 选择一个地方,把它们放在那里。我建议@implementation,因为它们不需要显示,但这取决于你。

+0

Re和代码块“@property语法之前”,我的记忆是不完美这一点,但加入在实现部分高德的声明大约与宣布的财产同时发生,我相信它与/依赖于非脆弱性的变化。 –

+0

@JoshCaswell我认为你是对的 - 现在改变。 –

this answer我发现我的问题可能是我没有使用“现代”的Objective-C运行时。

Instance variable declarations inside the @implementation { } block is a relatively recent Objective-C feature. . . . you also need to be eligible for the "Modern" Objective-C runtime . . .

this thread我发现,

Providing -fobjc-nonfragile-abi will also make some changes to the runtime. On Darwin it will use Apple's 'Modern' runtime, while not specifying it will generate code for the 'Legacy' runtime. If you specify -fobjc-nonfragile-abi and -fgnu-runtime, then clang will generate code for the GNUstep runtime, which you can find in GNUstep svn as libobjc2. This provides all of the ObjC 2 features except for GC, and a few others not found in the Mac runtimes.

所以,我尝试添加以下到我的GNUmakefile:

Main_OBJCFLAGS = -fobjc-nonfragile-abi 

但是,当我试图编译,我得到了错误:

fatal error: 'objc/blocks_runtime.h' file not found 

Fr在那里,我找到了this thread,并得出结论说我可能需要“libobjc2”。

令人失望的是,我找不到Debian的“libobjc2”软件包,所以我试图从源代码编译它。首先我必须安装一些编译依赖:

sudo apt-get install cmake llvm-dev 

然后,我不得不申请this patch向LLVM-3。5-dev软件包,因为它是造成libobjc2编译失败:

wget -qO- https://launchpadlibrarian.net/221945609/fix-llvm-3.5-dev-debian.bash | bash 

所以后来我就能够编译libobjc2用下面的命令:

wget http://download.gna.org/gnustep/libobjc2-1.7.tar.bz2 
tar jxf libobjc2-1.7.tar.bz2 
cd libobjc2-1.7 
mkdir Build 
cd Build 
CC=clang CXX=clang++ cmake -DCMAKE_INSTALL_LIBDIR=lib .. 
make -j8 
sudo -E make install 

在那之后,我试图编译代码再次从教程。编译成功(没有进行任何我不想做的更改)!然而,抱怨我的一个“.so”文件(我不记得确切的错误)和我运行我的程序(用./obj/Main)之间的“潜在冲突”,我在程序输出中看到以下内容:

$ ./obj/Main 
Loading two versions of Protocol. The class that will be used is undefined 
Loading two versions of Object. The class that will be used is undefined 
2016-11-19 16:43:24.012 Main[10166] Created a Toyota Corolla 
2016-11-19 16:43:24.017 Main[10166] Changed the car to a Toyota Camry 
2016-11-19 16:43:24.017 Main[10166] Driving a Toyota Camry. Vrooom! 

显然,编译libobjc2和现场让它旁边的Debian的软件包GNUstep的可能没有“好”。所以,我接受了各种在线资源的建议,着手从源代码编译GNUstep。

首先我卸载Debian软件包:

sudo apt-get uninstall gnustep-devel && sudo apt-get autoremove 

然后我安装了下列建立依赖关系:

sudo apt-get install libffi-dev libicu-dev libgnutls28-dev libxml2-dev libxslt1-dev libtiff5-dev libjpeg-dev libpng-dev libgif-dev libaspell-dev libcups2-dev libaudiofile-dev portaudio19-dev libdispatch-dev 

(我不能保证这是依赖的完整列表,只有那些我可以找到记录;我可能有一些更多必要的库已经安装在我的电脑上,因此编译了不相关的程序)

使用foll由于两个教程(1)(2),稍微调整它们(可能不必要),我设法编译GNUstep。

wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-make-2.6.8.tar.gz | tar xz 
wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-base-1.24.9.tar.gz | tar xz 
wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-gui-0.25.0.tar.gz | tar xz 
wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-back-0.25.0.tar.gz | tar xz 

cd gnustep-make-2.6.8 
CC=clang ./configure --enable-objc-nonfragile-abi 
sudo make install 
cd .. 

cd gnustep-base-1.24.9 
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" ./configure 
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" make 
sudo make install 
cd .. 

cd gnustep-gui-0.25.0 
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" ./configure 
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" make 
sudo make install 
cd .. 

cd gnustep-back-0.25.0 
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" ./configure 
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" make 
sudo make install 
cd .. 

在那之后,我再编译我的教程文件,这次使用:

. /usr/local/share/GNUstep/Makefiles/GNUstep.sh && make -k 

最后,我无法再获得任何“冲突”的错误,当我运行该程序,我不再看到“加载两个版本......”格式的错误。

$ ./obj/Main 
2016-11-19 19:56:07.450 Main[10822:10822] Created a Toyota Corolla 
2016-11-19 19:56:07.451 Main[10822:10822] Changed the car to a Toyota Camry 
2016-11-19 19:56:07.451 Main[10822:10822] Driving a Toyota Camry. Vrooom! 

成功!

我也将这个解决方案稍微重构成a script。这可能有点“更清洁”,更易于使用。我要“以防万一:”在这里贴上最新版本

#!/usr/bin/env bash 

# It seems that many modern Objective-C features aren't available without 
# libobjc2, which doesn't seem to be available as a Debian package. Also, 
# compiling and using it alongside the Debian GNUstep packages doesn't work too 
# well (it seems like they may each provide their own definition of the Protocol 
# and Object classes). Basically, to get a fully-functioning Objective-C 
# compilation environment on Debian 8, run this script. 

# Please ensure any Debian GNUstep packages are uninstalled before running this 
# script. 

# Slightly adapted from 
# http://wiki.gnustep.org/index.php/GNUstep_under_Ubuntu_Linux for Debian 8. 
# Also, uses the latest stable versions of source packages as of this writing 
# (hopefully to improve reproducability of success; but feel free to upgrade 
# them if you want). 

# If this script is successful, you should be able to compile a "main.m" program 
# by running "make" in a directory with a "GNUmakefile" with these contents: 
# 
#  include $(GNUSTEP_MAKEFILES)/common.make 
#  TOOL_NAME = Main 
#  Main_OBJC_FILES = main.m 
#  include $(GNUSTEP_MAKEFILES)/tool.make 

# Show prompt function 
function showPrompt() 
{ 
    if [ "$PROMPT" = true ] ; then 
     echo -e "\n\n" 
     read -p "${GREEN}Press enter to continue...${NC}" 
    fi 
} 

# Set colors 
GREEN=`tput setaf 2` 
NC=`tput sgr0` # No Color 

# Set to true to pause after each build to verify successful build and installation 
PROMPT=true 

# Install Requirements 
sudo apt update 

echo -e "\n\n${GREEN}Installing dependencies...${NC}" 
sudo apt -y install clang ninja cmake libffi-dev libxml2-dev \ 
    libgnutls28-dev libicu-dev libblocksruntime-dev libkqueue-dev libpthread-workqueue-dev autoconf libtool \ 
    libjpeg-dev libtiff5-dev libffi-dev libcairo2-dev libx11-dev libxt-dev libxft-dev \ 
    llvm-dev libdispatch-dev 

# https://bugs.launchpad.net/ubuntu/+source/llvm/+bug/1387011/comments/17 
wget -qO- https://launchpadlibrarian.net/221945609/fix-llvm-3.5-dev-debian.bash | bash 

# Create build directory 
mkdir GNUstep-build 
cd GNUstep-build 

# Set clang as compiler 
export CC=clang 
export CXX=clang++ 

# Checkout sources 
echo -e "\n\n${GREEN}Downloading sources...${NC}" 
mkdir -p libobjc2 && wget -qO- https://github.com/gnustep/libobjc2/archive/v1.8.1.tar.gz | tar xz -C libobjc2 --strip-components=1 
mkdir -p make && wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-make-2.6.8.tar.gz | tar xz -C make --strip-components=1 
mkdir -p base && wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-base-1.24.9.tar.gz | tar xz -C base --strip-components=1 
mkdir -p gui && wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-gui-0.25.0.tar.gz | tar xz -C gui --strip-components=1 
mkdir -p back && wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-back-0.25.0.tar.gz | tar xz -C back --strip-components=1 

showPrompt 

# Build GNUstep make first time 
echo -e "\n\n" 
echo -e "${GREEN}Building GNUstep-make for the first time...${NC}" 
cd make 
./configure --enable-debug-by-default --with-layout=gnustep --enable-objc-nonfragile-abi --enable-objc-arc 
make -j8 
sudo -E make install 

. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh 
echo ". /usr/GNUstep/System/Library/Makefiles/GNUstep.sh" >> ~/.bashrc 

# showPrompt 

# Build libobjc2 
echo -e "\n\n" 
echo -e "${GREEN}Building libobjc2...${NC}" 
cd ../libobjc2 
rm -Rf build 
mkdir build && cd build 
cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang -DCMAKE_ASM_COMPILER=clang -DTESTS=OFF 
cmake --build . 
sudo -E make install 
sudo ldconfig 

export LDFLAGS=-ldispatch 

showPrompt 

OBJCFLAGS="-fblocks -fobjc-runtime=gnustep-1.8.1" 

# Build GNUstep make second time 
echo -e "\n\n" 
echo -e "${GREEN}Building GNUstep-make for the second time...${NC}" 
cd ../../make 
./configure --enable-debug-by-default --with-layout=gnustep --enable-objc-nonfragile-abi --enable-objc-arc 
make -j8 
sudo -E make install 

. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh 

showPrompt 

# Build GNUstep base 
echo -e "\n\n" 
echo -e "${GREEN}Building GNUstep-base...${NC}" 
cd ../base/ 
./configure 
make -j8 
sudo -E make install 

showPrompt 

# Build GNUstep GUI 
echo -e "\n\n" 
echo -e "${GREEN} Building GNUstep-gui...${NC}" 
cd ../gui 
./configure 
make -j8 
sudo -E make install 

showPrompt 

# Build GNUstep back 
echo -e "\n\n" 
echo -e "${GREEN}Building GNUstep-back...${NC}" 
cd ../back 
./configure 
make -j8 
sudo -E make install 

showPrompt 

. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh 

echo -e "\n\n" 
echo -e "${GREEN}Install is done. Open a new terminal to start using.${NC}" 
+0

很高兴看到您已经解决了大部分问题 - 拥有现代运行时是一件好事。但请记住,在这里真的没有理由首先添加自己的实例变量。除非你有一些特定的ABI要求或明确的理由来指定你自己的实例变量(不想使用属性,需要真正的私有存储等),否则属性应该足够了。这些问题大部分是正交的。 –