GCC与现代Clang编译器下使用Objective-C的差异

Objective-C自上世纪80年代发展到至今(2018年)已经有30多年了。目前Objective-C的版本相比于其他一些经典的主流编程语言(Java、Python等)还是有些碎的。目前对Objective-C的进化起带头作用的仍然是Apple Inc.公司,而LLVM-Clang编译器也能支持不少现代化的Objective-C语法特性,详细请见:自Objective-C 2.0以来的新增语法特性。而GCC从4.2之后就基本中断了对Objective-C的升级,当前这里主要是指gobjc编译器,我们在除Apple以外的其他类Unix系统上主要使用的GNUStep还是在不断升级中,尤其是配合Clang对Objective-C的新增语法支持所新增的一些方法接口。

笔者撰写本文的目的是为了提醒Objective-C程序员,如果自己所写的代码想在其他类Unix上使用GCC+GNUStep进行编译运行的话所需要注意的一些事项。尽管GCC4.2比较老了,但是还是有不少开发环境、硬件平台不得不使用GCC,因为LLVM-Clang目前所能支持的处理器架构仍然不多,基本都属主流处理器架构。所以在其他一些小众平台上开发,那我们倘若没能力自己修改Clang代码进行适配的话,那么只能使用GCC了。

下面,我先给出GNUStep的官方API参考文档,GNUStep中的Base类库与Apple自家的Cocoa Framework中的Foundation类库大部分都能兼容,就是有些类的方法可能会稍微有些差异,我们在具体使用过程中如果遇到编译报错,可以进行参考:GNUstep Base。下面就开始我们的话题。

1、GCC中的Objective-C不能进行自动synthesize,因此如果我们要声明一个property并且不想自己实现其getter与setter方法的话就需要自己写@synthesize。此外,我们还需要在类中声明使用此property所对应的成员变量。

2、GCC中的Objective-C,Category不能声明成员变量,只能声明方法与property。再结合第一条,如果我们在Category中声明了一个property,并且在类中没有声明此property对应的成员变量,那么我们只能手动实现其getter与setter方法。

3、其实在比较早的Objective-C中就已经把“点语法”扩展得比较深了。这意味着,即便在一个类中没有声明某个property,但是有符合getter/setter命名规则的方法在,那么我们就可以使用点语法。不过这里需要注意的是,在GCC中比较有意思,如果一个类的类方法的返回类型是id,那么用该类通过点语法去访问该类方法的结果不能作为消息接受者!

4、GCC中的Objective-C,对于在类的实现中所定义的内部方法,它们必须要放在调用这些方法的方法之前,否则的话编译器会有warning提示消息可能无法响应。或者可以用私有Category进行在先声明。

如果大家不能一下子明白上述几点的具体使用场景,那么我将使用一个简单的demo进行解答:

#import 

@interface MyObject : NSObject
{
@private
    
    /// 这里必须显式声明string实例属性,
    /// 以提供手工synthesize的能力。
    NSString *string;
}

@property (nonatomic, retain) NSString *string;

- (void)testMethod;

+ (id)classMethod;

@end

@protocol MyProt

- (void)testMethod;

@end

// GCC中Category也能实现协议
@interface MyObject()

/// GCC中,Category里也可以声明一个property
@property (nonatomic, readonly, assign) int theValue;

/// 通过扩展声明innerMethod方法,
/// 这样可使得innerMethod能定义在MyObject中的任意空位
- (int)innerMethod;

@end


@implementation MyObject

@synthesize string;

- (void)dealloc
{
    self.string = nil;
    [super dealloc];
    
    NSLog(@"MyObject is destroyed!");
}

+ (id)classMethod
{
    return @"class method";
}

- (id)myself
{
    NSLog(@"This is myself!");
    return self;
}

- (void)testMethod
{
    NSUInteger length = self.string.length;
    
    length += [self.myself string].length;
    
    length += self.innerMethod;
    
    length += self.theValue;
    
    NSLog(@"length = %tu", length);
}

- (int)innerMethod
{
    return 100;
}

- (int)theValue
{
    return 10;
}

@end


int main(void)
{
    NSAutoreleasePool *pool = NSAutoreleasePool.new;
    
    MyObject *obj = MyObject.new;
    obj.string = @"Hello, world!";
    [obj testMethod];
    NSLog(@"The class method content is: %@", MyObject.classMethod);
    // 由于classMethod类方法返回的是id类型,
    // 因此这里不能直接使用[MyObject.classMethod length]这种调用方式。
    // 不过以下两种方式却都没问题。
    NSLog(@"The length is: %tu", [(NSString*)MyObject.classMethod length]);
    NSLog(@"The length is: %tu", [[MyObject classMethod] length]);
    [obj release];
    
    [pool drain];
}

上述代码简洁扼要地列出了大家在基于GCC编译器进行编写Objective-C时所需要注意的点。

你可能感兴趣的:(GCC与现代Clang编译器下使用Objective-C的差异)