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

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库,我们按照以下步骤先安装GNUStep库:

1、sudo apt-get install gnustep

2、sudo apt-get install gnustep-devel

这样我们把Objective-C的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用户进行开发使用。

 

由于Objective-C所依赖的编译选项以及运行时库比较多。所以我这里建议各位做一个makefile或是像我在下面描述的写一个shell文件,把需要的编译命令选项放进去。这样我们后面要编译源文件时就会方便很多。

我们首先通过执行以下命令来观察Objective-C编译时所需要的编译选项:

gnustep-config --objc-flags

然后我们把输出的内容先复制到shell文件中保存好。再执行以下命令查看Objective-C连接时所需要的加载选项:

gnustep-config --objc-libs

然后我们把加载选项复制黏贴到我们的shell文件中。

 

下面我们可以创建一个main.m源文件进行测试:

 

#include <dispatch/dispatch.h>
#include <Block.h>
#include <stdio.h>
#include <stdbool.h>

#import <Foundation/Foundation.h>


static int (^BlockTest(void))(int)
{
    __block int a = 10;

    int (^block)(int) = ^int(int i) {
            a += i;
            return a;
        };

    block(100);

    printf("The value is: %d\n", a);

    return Block_copy(block);
}

@interface DummyObject: NSObject

@end

@implementation DummyObject

- (void)dealloc
{
    NSLog(@"DummyObject deallocated!");
    [super dealloc];
}

@end

@interface MyObject: NSObject
{
@private

    /** 
     * 在GNUStep库以及Clang编译器环境下,Block不能作为一个Objective-C对象。
     * 因此这里只能将myBlock作为私有成员,而不能将它设置为property,
     * 然后在实现中设置其相关的getter与setter方法。
    */
    void (^myBlock)(void);

    /** 
     * 在Clang编译器中,property尚未能自动生成与其同名的私有成员,
     * 因此必须在类的私有域中进行显式声明。
     * 此外,对于Clang编译器,对成员对象的声明只能放在类的声明中,
     * 而不能放在实现中。
    */
    NSString *string;

    DummyObject *dummyObject;
}

@property (nonatomic, retain) NSString *string;

/** 此property用于测试使用setter方法是否能够回收成员对象 */
@property (nonatomic, retain) DummyObject *dummyObject;

@end

@implementation MyObject

@synthesize string, dummyObject;

/** myBlock的setter方法 */
- (void)setMyBlock:(void(^)(void))block
{
    if(myBlock != NULL)
    {
        Block_release(myBlock);
        myBlock = NULL;
    }
    if(block != NULL)
        myBlock = Block_copy(block);
}

/** myBlock的getter方法 */
- (void(^)(void))myBlock
{
    return myBlock;
}

- (instancetype)init
{
    self = [super init];

    NSLog(@"MyObject initialized!");

    return self;
}

- (void)dealloc
{
    // 这里使用属性的setter方法进行回收成员对象
    self.myBlock = NULL;
    self.string = nil;
    self.dummyObject = nil;

    NSLog(@"MyObject deallocated!");

    [super dealloc];
}

@end

static void MyTest(void)
{
    // 测试Block语法特性
    int (^block)(int) = BlockTest();

    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^void(void){
        puts("Hello, world!");

        int a = block(50);
        printf("a = %d\n", a);
    });

    Block_release(block);

    // 测试数组字面量以及下标索引语法
    NSArray *array = @[@10, @20, @"Hello, world"];
    // Clang编译器中不能直接使用数组下标语法,诸如:NSNumber *m = array[0];
    NSNumber *m = [array objectAtIndexedSubscript:0];
    NSNumber *n = [array objectAtIndex:1];
    NSLog(@"The sum is: %d, and the string is: %@", m.intValue + n.intValue, [array objectAtIndex:2]);

    // 测试Objective-C对象以及其属性
    DummyObject *dummyObj = [DummyObject new];

    MyObject *obj = [MyObject new];
    obj.myBlock = ^void(void){
        NSLog(@"The array size is: %tu", [array count]);
    };
    obj.string = @"This is my object!";
    obj.dummyObject = dummyObj;
    [dummyObj release];

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_sync(queue, ^void(void) {
        obj.myBlock();
        NSLog(@"obj string: %@", obj.string);
    });

    [obj release];
}

int main(void)
{
    @autoreleasepool {

        MyTest();

        NSLog(@"Program complete!");
    }
}

 

 

 

 

 

 

完了之后,我下面展示一下我自己整理好的build.sh编译shell文件:

 

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目录下。该头文件内容非常简单:

 

#pragma once

#ifdef __cplusplus
extern "C" {
#endif


void *_Block_copy(const void *) __attribute__((weak));
void _Block_release(const void *) __attribute__((weak));

#ifdef __cplusplus
}
#endif


然后我们再次构建的时候会发生更无语的错误——GSBlocks头文件中对_Blocks_copy以及_Blocks_release的声明与Block.h中的冲突。我们找到GSBlocks头文件,打开发现,原来里面声明的_Blocks_copy与_Blocks_release的形参类型是void*,而Block.h里声明的则是const void*……无奈之下,我们修改一下这个源文件,将其参数类型改为const void*就大功告成了。

 

我们成功编译构建之后会发现两个与ARC相关的警告,这些都不用理睬。

当然,如果我们不想使用跟Blocks语法相关的东东并且还想用GCC的话,也可以不折腾这些。下面我将列出不使用Blocks语法的项目配置:

gcc main.m -std=gnu11 -Os -MMD -MP -DGNUSTEP -DGNUSTEP_BASE_LIBRARY=1 -DGNU_RUNTIME=1 -DGNUSTEP_BASE_LIBRARY=1 -fno-strict-aliasing -pthread -fPIC -Wall -DGSWARN -DGSDIAGNOSE -Wno-import -fgnu-runtime -fconstant-string-class=NSConstantString  -I. -I/root/GNUstep/Library/Headers -I/usr/local/include/GNUstep -I/usr/include/GNUstep -I/usr/lib/gcc/x86_64-linux-gnu/5/include/    -rdynamic -L/root/GNUstep/Library/Libraries -L/usr/local/lib -L/usr/lib -lobjc -lm -lgnustep-base -o test

 

最后要说明的是,在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还是非常不错的,值得一用!

可以使用GNUstep在Windows XP上寫Objective-C程式,但是Objective-C已經加了很多新東西,而GNUstep裡的gcc並不提供,所以,这个是GNUstep,再加上新一代的編譯器架構Clang與LLVM(這也是Apple在新版Xcode裡預設使用的編譯器),這樣就能有Objective-C 2.0的功能,以及block與ARC(automatic reference counting)。 問:想開發iPhone或iPad或Mac的軟體,該怎麼辦? 答:使用Apple官方的軟體開發工具Xcode,只要到Apple公司的開發者網站,註冊一下(免費),即可下載最新版的Xcode。Xcode只能在Mac OS X(作業系統)上運行,所以要買一台Mac機器(硬體) 也就是說,如果你有一台Mac電腦(花錢買),下載Xcode(免費下載)後,就可以開始開發,在模擬器上執行看到成果。但是,如果想在iPhone實機上執行你寫的程式的話,那就要加入Apple的iOS Developer Program(要錢,一年要付$99) 問:想先學學Objective-C程式語言,還不想砸錢買Mac,有辦法在Windows上寫Objective-C程式嗎? 答:有,就是這篇的重點了。 节选自:http://yehnan.blogspot.com/2012/03/windowsobjective-cgnustepclangllvm.html 以上这个是中文网址,不过要越墙。 英文原文:https://solarianprogrammer.com/2012/03/21/clang-objective-c-windows/ 这个可以自用访问的。O(∩_∩)O哈哈~ 介于开源与自由精神,就不收费了,免费赠送,就当为这东西做推广了。也可在github上直接下载,只是担心不同地区对于国外网站的速度问题,选择上传csdn给大家提供方便。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值