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 <Foundation/Foundation.h>
@interface MyObject : NSObject
{
@private
/// 这里必须显式声明string实例属性,
/// 以提供手工synthesize的能力。
NSString *string;
}
@property (nonatomic, retain) NSString *string;
- (void)testMethod;
+ (id)classMethod;
@end
@protocol MyProt<NSObject>
- (void)testMethod;
@end
// GCC中Category也能实现协议
@interface MyObject()<MyProt>
/// 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时所需要注意的点。