2016-11-19 107 views
1

在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。)

回答

1

本教程中存在錯誤,您閱讀(或至少缺少信息) - 爲了使用@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我認爲你是對的 - 現在改變。 –

2

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要求或明確的理由來指定你自己的實例變量(不想使用屬性,需要真正的私有存儲等),否則屬性應該足夠了。這些問題大部分是正交的。 –