• Ubuntu系统下通过Clang编译器编写Objective-C


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

    Objective-C作为Apple的first-class编程语言,在很长一段时间内都得到大量开发者的追捧。其中,Objective-C对C语言的完全兼容、灵活性以及OOP特性,使得它成为一门十分优秀,且平衡度很高的编程语言。在我所有用过的编程语言中,Objective-C是最最适合用于开发驱动以及应用层程序的编程语言,它比C++轻便地多,但功能上又比C++更强;而在完美兼容C语言的基础上增加了教科书般的OOP特性!其中,消息机制是其灵魂。

    为了能够在其他平台上较好地使用现代化的Objective-C,我这里推荐使用LLVM Clang编译工具链。另外,以下描述的安装过程是在Ubuntu16.04下进行的,而更早版本的Ubuntu系统也差不多可按照以下操作步骤完成安装和编译使用。

    我们装好Ubuntu系统之后,GCC及其相关运行时库就已经默认安装在系统中了。为了保证我们当前用使用最新的Objective-C编译器以及Foundation库,我们按照以下步骤先安装gobjc以及GNUStep库:

    1、sudo apt-get install gobjc

    2、sudo apt-get install gnustep

    3、sudo apt-get install gnustep-devel

    这样我们把Objective-C的GCC编译器以及GNUStep运行时库都安装好了。

    下面我们开始下载并安装最新release的LLVM Clang:

    1、sudo apt-get install llvm

    2、sudo apt-get install clang

    完成这些安装之后,我们可以把Clang中Apple所给予的Blocks语法相关的运行时库以及Apple开源的Grand Central Dispatch库给装上。

    1、sudo apt-get install libblocksruntime-dev

    2、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 ", 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 ", 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还是非常不错的,值得一用!

  • 相关阅读:
    Squirrel GUI+ Phoenix 连接Hbase
    Hadoop_Hbase集群完全离线安装[CDH 5.13.1]
    cmake生成Makefile时指定c/c++编译器
    一步一步搭建:spark之Standalone模式+zookeeper之HA机制
    linux 安装nginx
    string和json转换的简单应用
    RHEL7 添加用户,含sudo权限
    RHEL7 Ansible
    RHEL安装docker-compose
    博客园仿github的markdown样式
  • 原文地址:https://www.cnblogs.com/yanwei-wang/p/8674799.html
Copyright © 2020-2023  润新知