微信二维码

二维码 扫二维码马上关注
扫码咨询
objective - c内部类

Objective-C有点老派,但它确实影响了Swift的开发和设计,所以它的遗产肯定会继续存在。现在也不是所有的新应用程序都是用Swift开发的。继续使用Objective-C有很好的理由,包括易于访问底层数据结构、简单性和控制。

Objective-C是一种面向对象的C语言,它有一些与我们习惯的结构稍微不同的结构(我在看你们的协议)。让我们来看看Objective-C是如何实现类的,这是任何面向对象语言的主要设计元素之一。

我们来看一个简单的Objective-C类:

@interface Printer : NSObject
 

 
@property NSString *str_to_print;
 

 
- (void) printMsg;
 
- (void) printString: (NSString*) message;
 

 
@end
 

 

 
@implementation Printer
 
- (void) printMsg {
 
printf("%s\n", [self.str_to_print UTF8String]);
 
}
 

 
- (void) printString: (NSString*) message {
 
self.str_to_print = message;
 
[self printMsg];
 
}
 

 
@end

Objective-C类定义了一个类接口,然后是一个实现(这也是Objective-C使用协议的原因之一,在其他语言中,协议被称为接口)。然后将类本身编译成一组定义类方法和数据元素的结构。

拆卸这个程序,让我们看看类对象的创建。当我们在Objective-C中创建一个对象时,我们使用这样的语法:Printer *obj = [[Printer alloc] init]。这将通过alloc()方法分配一个新对象,然后通过init()方法初始化它。如果我们查看创建对象的程序反汇编,我们会看到如下内容:

mov rsi, qword [objc_cls_ref_Printer]
 
mov rcx, qword [0x100001200]
 
mov rdi, rsi
 
mov rsi, rcx
 
mov qword [rbp+var_20], rax
 
call imp___stubs__objc_msgSend
 
mov rsi, qword [0x100001208]
 
mov rdi, rax
 
call imp___stubs__objc_msgSend

注意这两条调用指令。第一个调用alloc(),第二个调用init()。RSI寄存器保存所调用方法的地址,而RDI寄存器保存调用对象的地址(在本例中,是打印机类,然后是打印机对象)。第一个调用是我们感兴趣的—调用alloc()。我们特别感兴趣的是,它正在传递这个objc_cls_ref_Printer地址:

objc_cls_ref_Printer:
 

 
0000000100001218 dq _OBJC_CLASS_$_Printer

这实际上是一个蹦床类的定义:

_OBJC_CLASS_$_Printer:
 
0000000100001250 struct __objc_class {
 
    _OBJC_METACLASS_$_Printer, // metaclass
 
    _OBJC_CLASS_$_NSObject, // superclass
 
    __objc_empty_cache, // cache
 
    0x0, // vtable
 
    __objc_class_Printer_data // data
 
}

这就是事情开始变得有趣的地方。我们的类定义有一个隐式超类,NSObject类(记住,我们没有指定这个类,是编译器为我们指定的)。我们还有一个缓存指针,一个虚表指针,还有一个指向元类的指针。

元类的定义是这样的:

_OBJC_METACLASS_$_Printer:
 
0000000100001228 struct __objc_class {
 
    _OBJC_METACLASS_$_NSObject, // metaclass
 
    _OBJC_METACLASS_$_NSObject, // superclass
 
    __objc_empty_cache, // cache
 
    0x0, // vtable
 
    __objc_metaclass_Printer_data // data
 
}

到目前为止,我们已经定位了类定义数据结构和某种元类定义。但是这些东西是什么意思呢?实际的类方法在哪里定义的呢?c++也有表的概念。它们是一样的吗?Objective-C中也能有虚方法吗?

好的,我们将在下次跟踪更多的类数据。我们确实知道一些事情:Objective-C确实有虚方法,但它们和c++中的不一样;Objective-C和c++一样支持继承,vtable结构也以类似的方式使用;vtable是一个函数地址数组,但是所指向的函数对于给定的类定义不是惟一的。

程序中的所有类都将使用相同的vtable结构,在特定情况下可以使用该结构来绕过消息分派。这个vtable是在程序启动时创建的,没有类依赖于特定的vtable配置。Vtable指针可以指向父类Vtable,或者如果没有为Vtable中的条目标识特定的函数,则用objc_msgSend(.)替换该条目。

因此,vtable背后的思想与c++中类似,因为它用于动态函数分派,但是表的实际使用有很大的不同(即它并不是继承的关键)。这源于在Objective-C中使用消息传递而不是函数调用语义。

更多精彩内容,请关注元吉优惠券网:专注阿里云代金券阿里云服务器报价腾讯云代金券的免费更新领取!
更多精彩内容推荐:

Genesis之过滤器讲解
向Swift扩展添加存储属性的最简单方法
架构决策的多样性
阿里云ECS服务器简介
阿里云域名和网站服务
阿里云电商图片文字识别


在线客服
热线电话

扫一扫 微信加好友