Objective-C Without any Framework
2021-07-11 18:46:24
Objective-C
145

不包含任何框架的Objective-C

Objective-C Without any Framework

从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 <objc/runtime.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
 * 定义一个纯粹的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 <objc/runtime.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/** 
 * 定义一个纯粹的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 <MyProt>
{
@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;
}

各位在编译的时候可以参考下面的shell文件。这里面所需要注意的是在自己系统下C运行时库的位置可能与本demo有所不同,需要大家自己调整一下路径。

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的库。 ————————————————

原文链接:https://blog.csdn.net/zenny_chen/article/details/53379976