Ubuntu系統下經過Clang編譯器編寫Objective-C

轉自:https://blog.csdn.net/zenny_chen/article/details/52507022linux

Objective-C做爲Apple的first-class編程語言,在很長一段時間內都獲得大量開發者的追捧。其中,Objective-C對C語言的徹底兼容、靈活性以及OOP特性,使得它成爲一門十分優秀,且平衡度很高的編程語言。在我全部用過的編程語言中,Objective-C是最最適合用於開發驅動以及應用層程序的編程語言,它比C++輕便地多,但功能上又比C++更強;而在完美兼容C語言的基礎上增長了教科書般的OOP特性!其中,消息機制是其靈魂。shell

 

爲了可以在其餘平臺上較好地使用現代化的Objective-C,我這裏推薦使用LLVM Clang編譯工具鏈。另外,如下描述的安裝過程是在Ubuntu16.04下進行的,而更早版本的Ubuntu系統也差很少可按照如下操做步驟完成安裝和編譯使用。編程

咱們裝好Ubuntu系統以後,GCC及其相關運行時庫就已經默認安裝在系統中了。爲了保證咱們當前用使用最新的Objective-C編譯器以及Foundation庫,咱們按照如下步驟先安裝gobjc以及GNUStep庫:數組

一、sudo apt-get install gobjcxcode

二、sudo apt-get install gnustepruby

三、sudo apt-get install gnustep-develbash

這樣咱們把Objective-C的GCC編譯器以及GNUStep運行時庫都安裝好了。編程語言

下面咱們開始下載並安裝最新release的LLVM Clang:工具

一、sudo apt-get install llvm測試

二、sudo apt-get install clang

完成這些安裝以後,咱們能夠把Clang中Apple所給予的Blocks語法相關的運行時庫以及Apple開源的Grand Central Dispatch庫給裝上。

一、sudo apt-get install libblocksruntime-dev

二、sudo apt-get install libdispatch-dev

這樣,編譯器以及必要的運行時庫的安裝都結束了。使用Ubuntu系統的一大好處就是安裝一些常規工具很是便利,只須要一個sudo apt-get install就能搞定。因此它比較適合非深度Linux用戶進行開發使用。

 

在編譯以前,咱們進入 /usr/share/GNUstep/Makefiles 目錄,來對編譯環境進行設置。咱們直接在控制檯執行:

sudo bash /usr/share/GNUstep/Makefiles/GNUstep.sh

便可完成環境配置。

 

因爲Objective-C所依賴的編譯選項以及運行時庫比較多。因此我這裏建議各位作一個makefile或是像我在下面描述的寫一個shell文件,把須要的編譯命令選項放進去。這樣咱們後面要編譯源文件時就會方便不少。

咱們首先經過執行如下命令來觀察Objective-C編譯時所須要的編譯選項:

gnustep-config --objc-flags

而後咱們把輸出的內容先複製到shell文件中保存好。再執行如下命令查看Objective-C鏈接時所須要的加載選項:

gnustep-config --objc-libs

而後咱們把加載選項複製黏貼到咱們的shell文件中。

 

下面咱們能夠建立一個main.m源文件進行測試:

 

[objc]  view plain  copy
 
  1. #include <dispatch/dispatch.h>  
  2. #include <Block.h>  
  3. #include <stdio.h>  
  4. #include <stdbool.h>  
  5.   
  6. #import <Foundation/Foundation.h>  
  7.   
  8.   
  9. static int (^BlockTest(void))(int)  
  10. {  
  11.     __block int a = 10;  
  12.   
  13.     int (^block)(int) = ^int(int i) {  
  14.             a += i;  
  15.             return a;  
  16.         };  
  17.   
  18.     block(100);  
  19.   
  20.     printf("The value is: %d\n", a);  
  21.   
  22.     return Block_copy(block);  
  23. }  
  24.   
  25. @interface DummyObject: NSObject  
  26.   
  27. @end  
  28.   
  29. @implementation DummyObject  
  30.   
  31. - (void)dealloc  
  32. {  
  33.     NSLog(@"DummyObject deallocated!");  
  34.     [super dealloc];  
  35. }  
  36.   
  37. @end  
  38.   
  39. @interface MyObject: NSObject  
  40. {  
  41. @private  
  42.   
  43.     /**  
  44.      * 在GNUStep庫以及Clang編譯器環境下,Block不能做爲一個Objective-C對象。 
  45.      * 所以這裏只能將myBlock做爲私有成員,而不能將它設置爲property, 
  46.      * 而後在實現中設置其相關的getter與setter方法。 
  47.     */  
  48.     void (^myBlock)(void);  
  49.   
  50.     /**  
  51.      * 在Clang編譯器中,property還沒有能自動生成與其同名的私有成員, 
  52.      * 所以必須在類的私有域中進行顯式聲明。 
  53.      * 此外,對於Clang編譯器,對成員對象的聲明只能放在類的聲明中, 
  54.      * 而不能放在實現中。 
  55.     */  
  56.     NSString *string;  
  57.   
  58.     DummyObject *dummyObject;  
  59. }  
  60.   
  61. @property (nonatomic, retain) NSString *string;  
  62.   
  63. /** 此property用於測試使用setter方法是否可以回收成員對象 */  
  64. @property (nonatomic, retain) DummyObject *dummyObject;  
  65.   
  66. @end  
  67.   
  68. @implementation MyObject  
  69.   
  70. @synthesize string, dummyObject;  
  71.   
  72. /** myBlock的setter方法 */  
  73. - (void)setMyBlock:(void(^)(void))block  
  74. {  
  75.     if(myBlock != NULL)  
  76.     {  
  77.         Block_release(myBlock);  
  78.         myBlock = NULL;  
  79.     }  
  80.     if(block != NULL)  
  81.         myBlock = Block_copy(block);  
  82. }  
  83.   
  84. /** myBlock的getter方法 */  
  85. - (void(^)(void))myBlock  
  86. {  
  87.     return myBlock;  
  88. }  
  89.   
  90. - (instancetype)init  
  91. {  
  92.     self = [super init];  
  93.   
  94.     NSLog(@"MyObject initialized!");  
  95.   
  96.     return self;  
  97. }  
  98.   
  99. - (void)dealloc  
  100. {  
  101.     // 這裏使用屬性的setter方法進行回收成員對象  
  102.     self.myBlock = NULL;  
  103.     self.string = nil;  
  104.     self.dummyObject = nil;  
  105.   
  106.     NSLog(@"MyObject deallocated!");  
  107.   
  108.     [super dealloc];  
  109. }  
  110.   
  111. @end  
  112.   
  113. static void MyTest(void)  
  114. {  
  115.     // 測試Block語法特性  
  116.     int (^block)(int) = BlockTest();  
  117.   
  118.     dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^void(void){  
  119.         puts("Hello, world!");  
  120.   
  121.         int a = block(50);  
  122.         printf("a = %d\n", a);  
  123.     });  
  124.   
  125.     Block_release(block);  
  126.   
  127.     // 測試數組字面量以及下標索引語法  
  128.     NSArray *array = @[@10, @20, @"Hello, world"];  
  129.     // Clang編譯器中不能直接使用數組下標語法,諸如:NSNumber *m = array[0];  
  130.     NSNumber *m = [array objectAtIndexedSubscript:0];  
  131.     NSNumber *n = [array objectAtIndex:1];  
  132.     NSLog(@"The sum is: %d, and the string is: %@", m.intValue + n.intValue, [array objectAtIndex:2]);  
  133.   
  134.     // 測試Objective-C對象以及其屬性  
  135.     DummyObject *dummyObj = [DummyObject new];  
  136.   
  137.     MyObject *obj = [MyObject new];  
  138.     obj.myBlock = ^void(void){  
  139.         NSLog(@"The array size is: %tu", [array count]);  
  140.     };  
  141.     obj.string = @"This is my object!";  
  142.     obj.dummyObject = dummyObj;  
  143.     [dummyObj release];  
  144.   
  145.     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  146.   
  147.     dispatch_sync(queue, ^void(void) {  
  148.         obj.myBlock();  
  149.         NSLog(@"obj string: %@", obj.string);  
  150.     });  
  151.   
  152.     [obj release];  
  153. }  
  154.   
  155. int main(void)  
  156. {  
  157.     @autoreleasepool {  
  158.   
  159.         MyTest();  
  160.   
  161.         NSLog(@"Program complete!");  
  162.     }  
  163. }  



 

 

完了以後,我下面展現一下我本身整理好的build.sh編譯shell文件:

 

[delphi]  view plain  copy
 
  1. clang main.m -std=gnu11 -fblocks -lBlocksRuntime -ldispatch -lgnustep-base -MMD -MP -DGNUSTEP -DGNUSTEP_BASE_LIBRARY=1 -DGNU_GUI_LIBRARY=1 -DGNU_RUNTIME=1 -DGNUSTEP_BASE_LIBRARY=1 -fno-strict-aliasing -pthread -fPIC -Wall -DGSWARN -DGSDIAGNOSE -Wno-import -O2 -fgnu-runtime -fconstant-string-class=NSConstantString -I. -I/home/zenny-chen/GNUstep/Library/Headers -I/usr/local/include/GNUstep -I/usr/include/GNUstep -I/usr/lib/gcc/x86_64-linux-gnu/5/include/    -rdynamic -fgnu-runtime -L/home/zenny-chen/GNUstep/Library/Libraries -L/usr/local/lib -L/usr/lib -lobjc -lm -o test  


上述build.sh文件中,咱們使用-std=gnu11命令表示將當前的Objective-C以及C語言標準設置爲符合GNU11標準語法的,即C11標準加Clang GNU擴展。若是咱們不用GNU語法擴展,咱們就沒法使用Blocks語法。-fblocks使得Clang編譯器能解析Blocks語法,並生成相應運行時代嗎。在上述命令選項中,我把全部有關異常運行時庫的命令全都刪除了,由於咱們不須要使用Objective-C的異常運行時庫。此外,我把-g命令也去掉了,由於咱們也不須要對該程序進行調試。

 

 

咱們在運行build.sh的時候會發現,Clang編譯器會報一個很烏龍的錯誤——在GSVersionMacros.h中沒法找到<objc/blocks_runtime.h>。咱們在/usr目錄下搜索一下objc目錄所在位置(在個人系統環境下,目錄位置爲:/usr/lib/gcc/x86_64-linux-gnu/5/include/),而後咱們在桌面或其餘用戶目錄下建立一個blocks_runtime.h頭文件,輸入如下內容後用sudo拷貝到該obj目錄下。該頭文件內容很是簡單:

 

[ruby]  view plain  copy
 
  1. #pragma once  
  2.   
  3. #ifdef __cplusplus  
  4. extern "C" {  
  5. #endif  
  6.   
  7.   
  8. void *_Block_copy(const void *) __attribute__((weak));  
  9. void _Block_release(const void *) __attribute__((weak));  
  10.   
  11. #ifdef __cplusplus  
  12. }  
  13. #endif  


而後咱們再次構建的時候會發生更無語的錯誤——GSBlocks頭文件中對_Blocks_copy以及_Blocks_release的聲明與Block.h中的衝突。咱們找到GSBlocks頭文件,打開發現,原來裏面聲明的_Blocks_copy與_Blocks_release的形參類型是void*,而Block.h裏聲明的則是const void*……無奈之下,咱們修改一下這個源文件,將其參數類型改成const void*就大功告成了。

 

咱們成功編譯構建以後會發現兩個與ARC相關的警告,這些都不用理睬。

 

最後要說明的是,在Clang 3.8編譯器中,Objective-C能支持@autoreleasepool、複合字面量、instancetype等高級語法特性;但還不支持property自動綜合,甚至不能在類的category以及implementation中聲明成員對象。另外也不支持字典、數組的下標索引語法,儘管GNUStep庫中已經引入瞭如下四個方法:

- (void)setObject:(id)object forKeyedSubscript:(id < NSCopying >)aKey;
- (id)objectForKeyedSubscript:(id)key;


- (void)setObject:(id)anObject atIndexedSubscript:(NSUInteger)index;
- (id)objectAtIndexedSubscript:(NSUInteger)index;

上面兩個用於NSMutableDictionary,下面兩個用於NSMutableArray。可是在語法層面上還不支持下標索引方式,因此在代碼示例中用了[array objectAtIndex:0]這種形式,而不是十分簡潔的array[0]。

但總的來講,LLVM Clang 3.8仍是很是不錯的,值得一用!

相關文章
相關標籤/搜索