从2008年起,由于Apple开放了App Store,iOS开发热浪席卷全球,Objective-C编程语言也似乎一夜之间成为了各个程序猿家喻户晓的编程语言了。其实Objective-C很早就诞生了,它早先由Brad Cox和他妻子Tom Love两人一起开发出来的,其目标就是基于C语言打造出一款拥有SmallTalk消息机制、动态类型的面向对象的编程语言。这个时间比Bjarne Stroustrup开发出第一版C++的时间还要早2年。
Objective-C与C++的设计思想与目标截然不同,前者是完全基于C语言,而在C语言之上做一些可与其运行时库直接交互的标签语法,使得Objective-C看上去既面向对象,而且又具有动态特性,其实Objective-C的语法相当简单,如果各位用过OpenMP、OpenACC的话那就更容易理解了——它其实就类似于OpenMP中的pragma标签,Objective-C源代码一开始经由Objective-C专门的预编译器(注意,这里的预编译器pre-compiler与C编译器中的预处理器preprocessor不是同一概念,但十分类似)翻译成纯C代码,然后送给C语言编译构建,这个过程其实就跟处理OpenMP的预处理pragma类似。所以,Objective-C真正的灵魂其实就是它那一套运行时库了,也称为Objective-C runtime library。
Objective-C runtime library提供了非常丰富的动态机制,比如可通过字符串来找到相应的类;对已有的类添加属性和方法;修改已有的方法实现等。这也就使得Objective-C十分灵活机动了。在1998年,由Shawn Amundson发起的GLib项目在Gnome项目组中发布了第一版,如果各位对GLib有些了解的话就能发现,GLib库也是通过纯C语言打造出一套动态的、具有面向对象特性的库,这与早它10多年的Objective-C的设计而言不谋而合。不过可惜的是,由于GLib库十分庞杂,而且也缺乏系统性的文档,所以使得许多开发者望而却步,如果它一开始就利用简单的Objective-C打造整个库的话效果就会截然不同。我们可以对比一下Apple的Cocoa Framework与GLib,无论是从易用性也好还是从文档化方面来看,Cocoa Framework都要完胜GLib!这里我找到一篇对GLib介绍得比较好的文章,各位感兴趣的话可以参考:http://blog.csdn.net/Rodney443220/article/details/51606384?locationNum=3&fps=1
Bjarne Stroustrup设计的C++编程语言其思路是不基于C编译器,而是通过完全实现一个崭新的编译器的方式去打造C++,而在语法上仅仅与C语言兼容而已。C++当前的发展越来越庞杂,用Bjarne本人的话来说,目前几乎没有一个人能把C++各个方面的语法知识全都掌握……这是非常可怕的一件事。且不说C++编译器是否能经受住即将到来的C++17的考验,就拿一些大型项目来说,在一种有10个程序员能写出10种不同风格的编程语言上进行开发,可以想象,这是何等恐怖的事情。而Objective-C不需要如此庞杂的语法糖就能做到比C++更灵活。
说了那么多,想必大家一定对于Objective-C的简单性馋涎欲滴了罢,这里我将提供一个十分干净清澈的Objective-C源代码给各位看看其简洁性。这个代码片段中几乎囊括了Objective-C的所有语法特性,除了protocol没有展示之外。
//
// main.m
// PureClassTest
//
// Created by Zenny Chen on 2016/11/28.
// Copyright © 2016年 GreenGames Studio. All rights reserved.
//
#include
#include
#include
#include
/**
* 定义一个纯粹的Objective-C类,不继承任何父类
*
* 这里需要用OBJC_ROOT_CLASS属性作为前缀,否则在Clang编译器中会引发编译错误
*/
OBJC_ROOT_CLASS @interface MyPureClass
{
@private
/** 每个Objective-C类实例必须包含isa成员,并且必须将它作为第一个成员且不能随意改动 */
Class isa;
long retainCount;
}
/** 引用计数属性 */
@property (nonatomic) long retainCount;
+ (instancetype)alloc;
+ (size_t)classSize;
+ (instancetype)new;
- (instancetype)init;
- (void)dealloc;
- (Class)class;
- (size_t)instanceSize;
- (instancetype)retain;
- (instancetype)release;
@end
@implementation MyPureClass
@synthesize retainCount;
+ (instancetype)alloc
{
size_t size = [self classSize];
// 这里分配的Objective-C实例对象确保其大小为16字节的倍数
size = (size + 15) & ~15;
void *ptr = malloc(size);
memset(ptr, 0, size);
id instance = objc_constructInstance(self, ptr);
return instance;
}
+ (instancetype)new
{
return [[self alloc] init];
}
+ (size_t)classSize
{
return class_getInstanceSize(self);
}
- (instancetype)init
{
self.retainCount = 1;
return self;
}
- (void)dealloc
{
const size_t size = self.instanceSize;
void *address = objc_destructInstance(self);
memset(address, 0, size);
free(address);
}
- (Class)class
{
return object_getClass(self);
}
- (size_t)instanceSize
{
Class cls = [self class];
return [cls classSize];
}
- (instancetype)retain
{
self.retainCount++;
return self;
}
- (instancetype)release
{
if(--self.retainCount <= 0)
{
[self dealloc];
return nil;
}
return self;
}
@end
@interface Child : MyPureClass
{
@public
int value;
}
- (void)hello;
@end
@implementation Child
- (instancetype)init
{
self = [super init];
value = 10;
return self;
}
- (void)hello
{
printf("Hi, Child's value is: %d\n", value);
}
- (void)dealloc
{
puts("Child has been deallocated!");
[super dealloc];
}
@end
/** 这里是用了Category语法特性 */
@interface Child(MethodExpansion)
- (void)method;
@end
@implementation Child(MethodExpansion)
- (void)method
{
puts("This is a new method!");
}
@end
int main(int argc, const char * argv[])
{
// insert code here...
MyPureClass *obj = [MyPureClass new];
obj = [obj retain];
obj = [obj release];
printf("obj size is: %zu bytes\n", obj.instanceSize);
obj = [obj release];
if(obj == nil)
puts("obj has been released!");
Child *child = [Child new];
child->value += 20;
[child hello];
// 调用使用Category新增的方法
[child method];
[child release];
return 0;
}
我们发现,在上述代码中,我们没有使用Foundation库,而直接用了Objective-C运行时以及C运行时库。这里展示了一般如何定义一个Objective-C的根类,然后再定义其子类的过程。这里也使用了retain/release作为对象的引用计数方法。大家可以放到macOS上即可通过编译运行。
如果各位使用的是Linux上的gobjc2编译器(包含在GCC中),那么可以使用以下代码。因为GCC中所包含的Objective-C编译器的运行时库与Apple的运行时库会有一些不同,消息机制上也有些差别,所以这里需要做少许适配。不过下面这个例子更详细,列出了Objective-C 2.0中retain property以及protocol的使用等情况。
//
// main.m
// PureClassTest
//
// Created by Zenny Chen on 2016/11/28.
// Copyright © 2016年 GreenGames Studio. All rights reserved.
//
#include
#include
#include
#include
/**
* 定义一个纯粹的Objective-C类,不继承任何父类
*/
@interface MyPureClass
{
@private
/** 每个Objective-C类实例必须包含isa成员,并且必须将它作为第一个成员且不能随意改动 */
Class isa;
long retainCount;
}
/** 引用计数属性 */
@property (nonatomic) long retainCount;
+ (id)alloc;
+ (size_t)classSize;
+ (id)new;
+ (BOOL)respondsToClassSelector:(SEL)aSelector;
+ (BOOL)respondsToInstanceSelector:(SEL)aSelector;
+ (BOOL)conformsToProtocol:(Protocol*)protocol;
- (id)init;
- (void)dealloc;
- (Class)class;
- (size_t)instanceSize;
- (id)retain;
- (id)release;
@end
@implementation MyPureClass
@synthesize retainCount;
+ (id)alloc
{
id instance = class_createInstance(self, 0);
return instance;
}
+ (id)new
{
return [[self alloc] init];
}
+ (size_t)classSize
{
return class_getInstanceSize(self);
}
+ (BOOL)respondsToClassSelector:(SEL)aSelector
{
return class_respondsToSelector(object_getClass(self), aSelector);
}
+ (BOOL)respondsToInstanceSelector:(SEL)aSelector
{
return class_respondsToSelector(self, aSelector);
}
+ (BOOL)conformsToProtocol:(Protocol*)protocol
{
return class_conformsToProtocol(self, protocol);
}
- (id)init
{
self.retainCount = 1;
return self;
}
- (void)dealloc
{
object_dispose(self);
}
- (Class)class
{
return object_getClass(self);
}
- (size_t)instanceSize
{
Class cls = [self class];
return [cls classSize];
}
- (id)retain
{
self.retainCount++;
return self;
}
- (id)release
{
if(--self.retainCount <= 0)
{
[self dealloc];
return nil;
}
return self;
}
@end
@protocol MyProt
@required
@property (nonatomic) double doubleValue;
@optional
- (double)calculate:(double)d;
@end
@interface DummyObject : MyPureClass
- (void)greet;
@end
@implementation DummyObject
- (void)greet
{
puts("Hello, this is DummyObject!");
}
- (void)dealloc
{
puts("DummyObject deallocated!");
[super dealloc];
}
@end
@interface Child : MyPureClass
{
@public
int value;
@private
double doubleValue;
DummyObject *dummyObj;
}
/** 只要Objective-C类中含有retain/release方法,那么retain属性依然能有效工作 */
@property (nonatomic, retain) DummyObject *dummyObj;
- (void)hello;
@end
@implementation Child
@synthesize doubleValue, dummyObj;
- (id)init
{
self = [super init];
value = 10;
return self;
}
- (void)hello
{
printf("Hi, Child's value is: %d\n", value);
}
- (double)calculate:(double)d
{
return doubleValue + d;
}
- (void)dealloc
{
self.dummyObj = nil;
puts("Child has been deallocated!");
[super dealloc];
}
@end
/** 这里是用了Category语法特性 */
@interface Child(MethodExpansion)
- (void)method;
@end
@implementation Child(MethodExpansion)
- (void)method
{
puts("This is a new method!");
}
@end
int main(int argc, const char * argv[])
{
// insert code here...
MyPureClass *obj = [MyPureClass new];
obj = [obj retain];
obj = [obj release];
printf("obj size is: %zu bytes\n", obj.instanceSize);
obj = [obj release];
if(obj == nil)
puts("obj has been released!");
Child *child = [Child new];
child->value += 20;
[child hello];
DummyObject *dumObj = [DummyObject new];
child.dummyObj = dumObj;
[dumObj release];
[child.dummyObj greet];
// 调用使用Category新增的方法
[child method];
if([Child respondsToClassSelector:@selector(alloc)])
puts("Child responds to alloc");
if([[child class] respondsToInstanceSelector:@selector(method)])
puts("child responds to method");
if([[child class] conformsToProtocol:@protocol(MyProt)])
{
printf("Current doubleValue is: %f\n", child.doubleValue);
child.doubleValue = 30.5;
if([[child class] respondsToInstanceSelector:@selector(calculate:)])
printf("The double value is: %f\n", [child calculate:0.5]);
}
[child release];
return 0;
}
gcc main.m -std=gnu11 -MMD -MP -DGNU_GUI_LIBRARY=1 -fno-strict-aliasing -pthread -fPIC -Wall -DGSWARN -DGSDIAGNOSE -Wno-import -O2 -I. -I/usr/lib/gcc/x86_64-linux-gnu/5/include/ -rdynamic -fgnu-runtime -L/usr/local/lib -L/usr/lib -lobjc -lm -o test
大家可以将它保存为build.sh文件,然后通过bash命令即可编译构建了。我们从上述编译选项中可以看到,这里没有连接任何关于GNUStep的库,而仅仅只是连接了libobjc的库。