在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
作者:wangzz
转载请注明出处
如果觉得文章对你有所帮助,请通过留言或关注微信公众帐号wangzzstrive来支持我,谢谢!
作为C语言的超集,面向对象成为Objective-C与C语言的最大区别,因此,对象是Objective-C中最重要的部分之一。目前面向对象的语言有很多,Objective-C中的对象又和其他语言中的对象有什么区别呢?下面来简单介绍Objective-C中对象的实现。 谁都知道,所有的对象都是由其对应的类实例化而来,殊不知类本身也是一种对象,先不要对这句话感到惊讶。 2、类对象(class object) ②类对象和实例对象的区别 总之,类对象是一个功能完整的对象,所以也能被动态识别(dynamically typed),接收消息,从其他类继承方法。特殊之处在于它们是由编译器创建的,缺少它们自己的数据结构(实例变量),只是在运行时产生实例的代理。 3、元类对象(metaclass object) 记住,当一个消息发送给任何一个对象, 方法的检查 从对象的 isa 指针开始,然后是父类。实例方法在类中定义, 类方法 在元类和根类中定义。(根类的元类就是根类自己)。在一些计算机语言的原理中,一个类和元类层次结构可以更自由的组成,更深元类链和从单一的元类继承的更多的实例化的类。Objective-C 的类方法 是使用元类的根本原因,在其他方面试图在隐藏元类。例如 [NSObject class] 完全相等于 [NSObject self],所以,在形式上他还是返回的 NSObject->isa 指向的元类。 Objective-C语言是一组实用的折中方案。 还有些不明白? 下面这个图标可能会有些帮助:
4、类对象和元类对象的相关方法 ①object_getClass跟随实例的isa指针,返回此实例所属的类,对于实例对象(instance)返回的是类(class),对于类(class)则返回的是元类(metaclass), ④使用objc_allocateClassPair可在运行时创建新的类与元类对,使用class_addMethod和class_addIvar可向类中增加方法和实例变量,最后使用objc_registerClassPair注册后,就可以使用此类了。看到动态语言牛逼的地方了吗,可以在需要时更改已经定义好的类!Objective-C的类别方法估计底层就是这么实现的,只是不知道为什么类别不能增加实例变量,有高手请留言。
Objective-C为我们提供了两种初始化对象的方法:Objective-C2.0以后可用的new方法和两段构造法。既然要比较这两种初始化方法,就从它们本身的异同出发吧。 一、两段构造法 这是Objective-C特有的对象创建方法,书写形式如下: NSString*s=[[NSString alloc] init]; 所谓的两段构造,就是指将alloc和init分开来写,这和大多数其它语言(如C、C++、Java、JavaScript)都不一样。先来看看alloc和init都干了什么吧: 1、alloc方法 当对象创建时,cocoa会从应用程序的虚拟地址空间上为该对象分配足够的内存。cocoa会遍历该对象所有的成员变量,通过成员变量的类型来计算所需占用的内存。 2、init方法 大部分情况下,我们都不希望所有成员变量都是零,所以 ①init方法会做真正的初使化工作,让对象的成员变量的值符合我们程序逻辑中的初始化状态。例如,NSMutableString可能就会额外再申请一块字符数组,用于动态修改字符串。 ②返回真正可以使用的指向该对象的指针 init还有一个需要注意的问题,某些情况下,init会造成alloc的原本空间不够用,而进行第二次分配内存空间。所以下面的写法是错的: [s init];// 这儿init返回的地址可能会变。s原本的指针地址可能是无效的地址。 为此,苹果引入了一个编程规范,让大家写的时候将alloc 和init写在一行。所以上面的代码正确的写法是 二、new方法 可能是为了和其他语言保持一致,苹果后来也推出了new方法来初始化对象。作为类方法的new,只是简单地等价于 alloc + init,却不能指定init的参数,所以实际使用中很少见到。 有人可能要问,Objective-C的对象创建方法和大多数其它语言(如C、C++、Java、JavaScript)都不一样,是什么原因促使Objective-C做了这种设计? 1、历史原因 这里面多多少少就有历史的因素了。Objective-C是一门非常老的语言。如果你查阅文档,你会发现它和C++出生在同一时代(两种语言的发行年份都是1983年),都是作为C语言的面向对象的接班人被推出。当然,最终C++胜出。由于历史久远,Objective-C也无法有太多优秀的语言做参考,所以,有很多历史遗留的设计。 2、设计原则 简单看来,根据设计模式的Single Responsibility的设计原则,苹果觉得alloc和init是做的2件不同的事情,把这两件事情分开放在2个函数中,对于程序员更加清楚明了。更详细查阅文档后,我觉得这是由于历史原因,让苹果觉得alloc方法过于复杂,在历史上,alloc不仅仅是分配内存,还可以详细的指定该内存所在的内存分区(用NSZone表示)。 同时由于分配和初始化阶段是分开的,初始化方法的实现只需处理新实例的变量,并完全忽略有关分配的问题,简化了初始化方法的过程。 四、NSZone简介 早期苹果是建议程序员使用 allocWithZone来管理内存分配的,每个NSZone表示一块内存分区,+allocWithZone:(NSZone *)zone方法可以允许对象从指定分区分配内存。内存区是Cocoa的一个功能部件,它能使同时使用的对象或计算机的地址空间中相邻的对象保持在内存中,以此提高程序的性能。要解释对象在内存中的位置会如何影响性能,需要解释应用程序需要比物理内存更大的内存时会发生什么情况。 每个Cocoa应用程序都有很大的可寻址内存,当应用程序动态的分配内存时,即使计算机的所有物理内存都已经被占用,操作系统仍然会提供内存。要满足该分配要求,操作系统会使用页面调度(paging)或者交换(swapping)操作将一些物理内存中的内容复制到硬盘,之前正在使用的物理内存就可以被提供出来使用了,而之前的那些数据应经被写入硬盘。如果有需要先前复制到硬盘的那部分内存数据,操作系统会将另外一块物理内存复制到硬盘,并将先前的旧内存再度调回内存。即时内存在硬盘间调度,操作系统仍然能为每个应用程序映射地址空间到物理内存。操作系统的这一功能即是虚拟内存(virtual memory)。 由于从物理内存额硬盘中相互调度是很消耗时间的,因此,使用虚拟内存会影响性能。过多的页面调度会降低系统性能,这称为抖动(thrashing)。如果一起使用的两个或多个对象在内存中的位置很远,抖动发生的可能性将会大大增加,因此对象实例的内存分配的位置也很重要。 分区用于确保分配给同时使用的对象的内存位于相邻位置。当需要某个对象时,另外相邻的对象也基本会用到,需要的所有对象同时调入内存的可能性就更大,当不需要时,又可以都同时调出内存,Cocoa中的NSZone类型是指定标识内存区的C结构的对象,+allocWithZone:(NSZone *)zone方法允许NSZone变量从指定分区分配内存。已达到减少抖动的目的。可见当年苹果的设计师们的良苦用心!!! 只是,分区是一个十分底层的东西,而且,随着硬件设备的发展,物理内存的不断增大,以及操作系统内存分配函数复杂性的提高,使用分区的最初目的已经逐渐消失了。自从Mac OS X 10.5上引入了垃圾回收机制后,苹果就不建议程序员使用allocWithZone了,事实上,cocoa框架也会忽略+allocWithZone:(NSZone *)zone指定的分区。苹果在文档中也提到,+allocWithZone:(NSZone *)zone仅仅是一个历史遗留设计了。 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论