从 C++ 到 Objective-C(14):内存管理(续)
- 格式:doc
- 大小:47.00 KB
- 文档页数:4
C语言内存使用详解C语言是一种低级语言,开发者可以直接控制内存使用。
了解C语言内存使用的机制和技巧对于编写高效、安全和可靠的程序至关重要。
本文将详细介绍C语言内存使用的知识和技术,并提供一些实用的建议。
在C语言中,内存是以字节为单位进行管理的,通常将内存分为栈和堆两种。
栈是一种自动分配和自动释放内存的数据结构。
它的特点是后进先出(LIFO),即最后分配的内存最先释放。
栈主要用于存储局部变量、函数参数和函数调用的上下文信息。
在函数调用结束后,分配给局部变量的内存会自动释放。
堆是一种动态分配内存的数据结构,程序员可以手动分配和释放内存。
堆的管理需要调用系统提供的函数,如malloc(和free(。
堆主要用于存储动态分配的数据,如数组、结构体和指针。
程序员需要手动管理堆内存,确保及时释放不再使用的内存,否则会造成内存泄漏。
为了更好地使用内存,提高程序的性能和可靠性,下面是一些C语言内存使用的技巧和注意事项:1.使用局部变量:局部变量是保存在栈上的,它们的生命周期与函数的调用关系密切相关。
局部变量不仅可以节约内存,还可以提高程序的执行效率。
2.合理分配静态变量和全局变量:静态变量和全局变量在程序执行过程中一直存在,它们的生命周期不受函数调用的影响。
过多的静态变量和全局变量会占用大量的内存,影响程序的性能。
3. 动态分配内存时要检查返回值:在调用malloc(等动态分配内存的函数时,要检查返回值是否为NULL。
如果返回值为NULL,表示没有足够的内存可用。
处理内存分配失败的情况至关重要,可以提前终止程序或采取其他恰当的措施。
4. 及时释放不再使用的内存:动态分配的内存在不再使用时要及时释放,以避免内存泄漏。
使用free(函数将内存返回给系统,以供其他程序使用。
5.防止指针错误:指针是C语言中非常重要的概念,但也容易出现指针错误,如空指针引用、越界访问等。
使用指针时要特别小心,确保指针正确地指向有效的内存区域。
c语言进阶1.内存管理a. 未初始化的全局变量(.bss)b. 初始化的全局变量(.data)c. 常量数据(.rodata):常量不一定放在.rodata里,有的立即数直接和指令编码在一起,存放在代码段(.text)d. 代码段(.text)f. 栈(stack):用于存放临时变量和函数参数。
栈向下增长(低地址)。
g. 堆(heap):内存分配了不释放,被称为内存泄漏(Memory Leak)2.内存分配方式a.从静态存储区分布:内存在编译时就已经分配好,这块内存在程序的整个运行期间都存在,如全局变量,static 变量等。
b.在栈上创建:函数内部局部变量,函数执行结束时被释放。
c.在堆上创建:动态内存分配。
malloc 和new的时候。
要free 和delete3.野指针a. 指针变量没有被初始化b. 指针p被free或者delete之后,没有置位NULL4.指针和数组对比a. 数组要么在静态存储区被创建,要么在栈上被创建。
数组名对应着一块内存(而不是指向),其地址和容量在生命期内保持不变,数组内容可以保持不变b. 指针可以指向任意类型的内存块。
c. sizeof(a):得到数组真实的大小; sizeof(p):指针变量的字节数5.预处理a. 宏定义b. 文件包含;<>表示在包含文件目录中区查找 ""表示首先在当前源文件目录中去查找,若未找到则去包含目录中去找c. 条件编译6.函数库的提供形式:静态链接库和动态链接库a. 静态链接库:b. 动态链接库:效率更高。
不是将库函数的代码直接复制进可执行程序中,只是做个链接标记。
当应用程序在内存中执行,运行时环境发现它调用了一个动态库中的库函数时,会加载这个动态库到内存中,以后不管有多少个应用程序在同时使用该库函数,该库函数在内存中只有一份。
7.链接属性a. 程序从源代码到最终可执行程序:预编译、编译(将源代码翻译成xx.o)、汇编和链接b. 内存映像:代码段(test)和rodata段;数据段(data)和bss 段;堆;文件映射区(进程打开了文件后,将这个文件的内容从硬盘读取到进程的文件映射区,以后就直接在内存中操作这个文件);栈;内核映射区(将操作系统的内核程序映射到这个区域)8.加载运行代码a. 单独个人写的C语言程序没法直接在内存中运行,需要一定的外部协助,这段协助的代码叫做加载运行代码,这段代码的主要作用是给全局变量赋值,清bss段(现象:C语言中未初始化的全局变量默认为0)b. 在裸机下写的代码:定义了一个全局变量初始化为0.但是实际不为0.应在裸机的start.s中加入清bss段代码9.存储类相关的关键字auto:修饰局部变量。
objective-c语法Objective-C是一种编程语言,它是C语言的扩展,增加了面向对象编程的特性。
Objective-C的语法结构与C语言类似,但增加了一些Objective-C特有的关键字和语法元素。
以下是一些Objective-C的基本语法元素:1.头文件和import指令:Objective-C使用头文件(.h)来声明类、方法和协议。
import指令用于导入所需的头文件。
例如:2.类和对象:Objective-C的类定义使用@interface指令和@end指令。
类实例(对象)是使用@implementation指令和@end指令定义的。
例如:3.对象创建和初始化:可以使用alloc和init方法来创建和初始化Objective-C对象。
例如:4.对象方法和消息:Objective-C中,对象方法使用@selector指令定义。
消息传递机制通过@protocol 指令和@implementation指令定义,这使得Objective-C支持动态方法绑定。
例如:5.类方法和静态方法:Objective-C中,类方法和静态方法使用+和-指令定义。
类方法和静态方法可以在类定义之外使用,这在创建单例对象时非常有用。
例如:6.属性和访问器:Objective-C中,可以使用@property指令定义属性。
属性声明包括读写权限(getter 和setter方法)、默认值和数据类型。
例如:7.协议:Objective-C使用@protocol指令定义协议。
协议定义了一组方法和属性,可以被任何遵循协议的类实现。
例如:8.异常处理:Objective-C使用NSException类进行异常处理。
可以使用@try、@catch和@finally 指令来捕获和处理异常。
例如:9.消息和选择器:Objective-C使用@selector指令定义消息。
可以使用NSSelectorFromString方法从字符串生成选择器。
Objective-C中的引⽤计数导⾔Objective-C语⾔使⽤引⽤计数来管理内存,也就是说,每个对象都有个可以递增或递减的计数器。
如果想使某个对象继续存活,那就递增其引⽤计数;⽤完了之后,就递减其计数。
计数为0,就表⽰没⼈关注此对象了,于是,就可以把它销毁。
从Mac OS X 10.8开始,“垃圾收集器”(garbage collector)已经正式废弃了,以Objective-C代码编写Mac OS X程序时不应再使⽤它,⽽iOS则从未⽀持过垃圾收集。
因此,掌握引⽤计数机制对于学好Objective-C来说⼗分重要。
Mac OS X程序已经不能再依赖垃圾收集器了,⽽iOS系统不⽀持此功能,将来也不会⽀持。
已经⽤过ARC的⼈可能会知道:所有与引⽤计数有关的⽅法都⽆法编译,然⽽现在先暂时忘掉这件事。
那些⽅法确实⽆法⽤在ARC中,不过本⽂就是要从Objective-C的⾓度讲解引⽤计数,⽽ARC实际上也是⼀种引⽤计数机制,所以,还是要谈谈这些在开启ARC功能时不能直接调⽤的⽅法。
⼯作原理在引⽤计数架构下,对象有个计数器,⽤以表⽰当前有多少个事物想令此对象继续存活下去。
这在Objective-C中叫做“保留计数”(retain count),不过也可以叫“引⽤计数”(reference count)。
NSObject协议声明了下⾯三个⽅法⽤于操作计数器,以递增或递减其值:1)retain 递增保留计数。
2)release 递减保留计数。
3)autorelease 待稍后清理“⾃动释放池”(autorelease pool)时,再递减保留计数。
上图是对象创建及保留计数操作的效果图。
上图对象图中,ObjectB与ObjectC都引⽤了ObjectA。
若ObjectB与ObjectC都不再使⽤ObjectA,则其保留计数降为0,于是便可摧毁了。
还有其他对象想令ObjectB与ObjectC继续存活,⽽应⽤程序⾥⼜有另外⼀些对象想令那些对象继续存活。
基于ObjectiveC的Mac应用程序设计与开发在当今数字化时代,Mac 应用程序的设计与开发已经成为了一项备受关注的技术领域。
作为苹果公司推出的操作系统 macOS 的主要开发语言之一,Objective-C 在 Mac 应用程序设计与开发中扮演着重要的角色。
本文将深入探讨基于 Objective-C 的 Mac 应用程序设计与开发过程,包括开发环境搭建、基本语法、界面设计、数据存储等方面的内容。
1. 开发环境搭建要进行基于 Objective-C 的 Mac 应用程序设计与开发,首先需要在 Mac 电脑上搭建相应的开发环境。
苹果公司提供了 Xcode 这一集成开发环境(IDE),开发者可以在 Xcode 中进行 Objective-C 语言的编码、调试和构建应用程序。
同时,Xcode 还提供了 Interface Builder 工具,可用于设计 Mac 应用程序的用户界面。
2. Objective-C 基本语法Objective-C 是一种面向对象的编程语言,其语法继承自 C 语言并添加了 Smalltalk 风格的消息传递机制。
在 Objective-C 中,类是构建应用程序的基本单元,对象是类的实例。
以下是 Objective-C 中常用的语法要点:类与对象:Objective-C 中通过 @interface 和@implementation 关键字定义类,通过 @property 关键字声明属性。
使用 alloc 和 init 方法创建对象。
方法:Objective-C 中使用减号(-)表示实例方法,加号(+)表示类方法。
方法由方法名、参数列表和返回类型组成。
消息传递:Objective-C 使用方括号([])来向对象发送消息,调用对象的方法。
内存管理:Objective-C 使用引用计数(Reference Counting)来管理内存,开发者需要手动管理对象的内存。
3. 界面设计Mac 应用程序的用户界面设计是吸引用户的重要因素之一。
c++ 内存管理机制C++ 是一种编程语言,它支持面向对象编程和泛型编程。
这种编程语言在内存管理方面提供了强大的支持,使得程序员不必关心内存管理细节。
这篇文章将介绍 C++ 内存管理机制的基本概念、原理和实现方法。
一、内存管理机制的基本概念内存管理机制是指在程序运行时对内存进行分配、释放、复制等操作的过程。
在 C++ 中,内存管理分为两种:动态内存管理和静态内存管理。
动态内存管理是指程序在运行时根据需要分配或释放内存。
这种内存由程序员控制。
动态内存的分配和释放可以通过 malloc、new、calloc、realloc 等函数来完成。
静态内存管理是指程序在编译时就已经将内存分配好了。
C++ 中的全局变量和静态变量都是静态内存,它们的内存分配是由编译器完成的。
在程序运行时,程序无法修改或释放静态内存。
二、内存管理的原理内存管理的原理是基于计算机的硬件和操作系统的支持。
计算机的内存是通过地址来寻址的,每个内存单元都有一个唯一的地址。
操作系统为程序提供了访问内存的接口。
程序通过这些接口来请求分配内存,释放内存和管理内存。
内存分配的关键在于内存的管理,即如何跟踪哪些内存被分配,哪些内存被释放,哪些内存是可用的。
C++ 使用堆和栈来管理内存。
堆是动态内存分配和回收的区域。
当程序需要分配一块内存时,它可以使用 new 或malloc 函数来请求分配一块地址连续的内存空间。
堆可以分为多个内存块,每个内存块都可以独立地分配和释放。
程序员需要手动管理堆内存,即在使用完内存后,需要调用delete 或 free 函数将内存释放。
栈则用于管理静态内存。
当程序声明一个变量时,它会被分配到栈上。
栈是一种后进先出 (LIFO) 的数据结构。
当程序需要释放一块栈内存时,它只需要将栈指针向上移动即可。
三、内存管理的实现方法C++ 使用指针来管理内存。
指针是一种变量,它保存了内存块的地址。
指针可以指向堆中的动态内存块,也可以指向栈中的静态内存块。
/*1.类是一组具有相同特性和行为的某一类事和物的集合.2.对象是类的实例化.3.计算机语言的目的:模拟和解决生活中的问题.4.#include和#import的区别?答:(1)#import是oc中导入头文件的关键字,而且只需导入一次就行.(2)#include是c\c++导入头文件的关键字,include导入头文件容易引起交叉编译.5.创建对象的语法:(1)类名*对象名=[[类名alloc]init];(2)类名*对象名=[类名new];6.oc中的方法:(减号为实例方法,加号为类方法)-|+(返回类型)方法名:参数列表{//方法体}(1)器就是方法的意思;(2)void意思是无返回值;Student为类名,意思为有Student类型的返回值(3)with后面的name是属性的名称,newName 是属性name的参数名称初始化器:-|+(Student*)initWithName:(NSString*)newName{self=[super init];//继承父类NSObject 的init(初始化)方法self->name=newName;return self;}便利构造器:-|+(Student*)studentWithName:(NSString*)newName{Student*student=[[Student alloc]init];//实例化变量student=newName;return student;}7.属性#interface@property(nonatomic,retain)NSString *name;(声明)等价于:(setter方法:设置器)-(void)setAge:(int)newAge{age=newAge;}#implemention@synthesize name;(合成)等价于:(getter方法:访问器)-(int)getAge{return age;}8.点语法(1)=@"liming";等价于:[stu setName:@"liming"];(2)NSlog(@"%@",[]);等价于:NSlog(@"%@",[stu getName]);9.property有关语法(1)readwrite是可读可写特征;需要生成getter 方法和setter方法.(2)readonly是只读特性只会生成getter方法不会生成setter方法.(3)assign赋值特性,setter方法将传入参数赋值给实例变量.(4)retain表示持有特性,setter方法将传入参数先保留,再赋值,并且变量retaincount+1;(5)copy通过copy将对象复制一份,之前的对象会释放.(6)nonatomic表示单线程安全.(7)atomic表示多线程安全.10.变量作用域(1)@protected表示只有该类和其子类可以访问该类的变量.(2)@public表示无论是自身还是子类的对象,还是其它类型的对象均能访问该类的变量.(3)@private表示只有该类本身的对象可以访问该类的变量.11.面向对象三大特征:(1)封装:隐藏内部实现,稳定外部接口.(2)继承:子类继承父类的所有非私有的方法和属性.(3)多态:不同类型的对象,对同一方法名的不同响应.注意:要点一:oc中不允许多继承(一个类有多个父类),但允许多重继承(一个类的父类也拥有其自身的父类).要点二:一个类调用方法时,先从本类方法找,找不到再从父类找,一直往上直到找到NSObject类中.12.self:是指向当前对象的指针.super:调用父类的方法.id:是一个指向任何一个继承了NSObject类的对象.13.开闭原则和里氏替换原则(1)开闭原则(OCP Open Closed Principle):核心是:对扩展开放,对修改关闭.改变一个软件时,应该通过扩展的方式来改变软件,而不应该修改原有的代码来实现变化.(2)里氏替换原则(LSP Liskov Substitution Principle):(is A)原则:任何基类可以出现的地方,子类一定可以出现.a.继承必须确保超类所拥有的性质子类中仍然成立.b.子类对象总是可以替换父类对象.14.内存管理(1)黄金法则:凡是对象使用了alloc,new,copy和retain,那么必须有相对应的release和autorelease.引用计数机制:对象创建后,运行时系统通过对象维护的一个计数器来描述有多少个其他对象在使用自己,当计数器为0时,释放该对象占用的内存空间(该对象调用dealloc方法)。
学习Objective-C入门教程1,前言相信iPhone不久就要在国内发布了,和我们在国内可以通过正规渠道买得到的iPodTouch一样,iPhone也是一个激动人心的产品。
iPhone发布的同时,基于iPhone的程序也像雨后春笋一样在iTunes里面冒出来。
你将来也许会考虑买一个iPhone,体验一下苹果的富有创意的种种应用;你也许会考虑向iTunes的社区的全世界的人们展示一下你非凡的创意,当然也可以通过你的创意得到一些意想不到的收益。
OK,你也许迫不及待的准备开发了。
但是先等一下,让我们回忆一下最初的电影是怎么拍摄的。
这个很重要,因为和iPhone的开发比较类似。
在最初因为器材比较原始,所以拍摄电影需要很高的技术,那个时候的电影的导演基本上是可以熟练操作摄影器材的人。
随着器材的完善,使用也简单起来。
于是器材的使用不是决定一个电影的质量的唯一的因素,取而代之的是故事或者说电影的创意。
iPhone的开发也是这样。
当然从入门到掌握的过程来说任何事情都是开始比较难,随着掌握的程度的加深,你将会觉得开发iPhone应用程序是一件简单而且轻松的事情,到了那个时候,你的主要的制胜武器就不是开发技术,而是你的创意了。
对于你来说,我在这里写的东西都是有关“摄影器材”也就是介绍如何使用iPhone的平台来开发应用程序。
iPhone的开发语言是Objective-C。
Objective-C是进行iPhone开发的主要语言,掌握了Objective-C的基本语法以及数据结构之后,你需要熟悉一下iPhone的SDK。
笔者很难做到在一篇文章里面把所有的东西都介绍清楚,所以笔者打算分成两个主题,一个是Objective-C,一个是iPhone开发。
本系列将侧重于Objective-C。
当然,任何一种开发语言都无法脱离于运行环境,Objective-C也不例外。
所以在本系列当中也会穿插的介绍一些SDK里面的一些特性,主要是数据结构方面,比如说NSString,NSArray等等。
oc中英文对照Objective-C中英文对照Objective-C是一种面向对象的编程语言,广泛用于iOS和macOS 应用程序的开发。
它将C语言与面向对象编程相结合,提供了丰富的功能和特性。
下面是一份Objective-C中的常用词汇对照表,其中包含了中文和英文的对应词汇,以帮助开发者更好地理解和使用Objective-C。
1. 关键字(Keywords)关键字是在Objective-C中具有特殊含义的单词,用于表示语言的各种语法结构和功能。
- 类定义:Class- 继承:Inheritance- 实例变量:Instance Variables- 方法:Methods- 属性:Properties- 协议:Protocol- 实现:Implementation- 接口:Interface- 静态:Static- 共有:Public- 私有:Private- 受保护:Protected- 抽象:Abstract- 选择器:Selector- 异步:Asynchronous- 同步:Synchronous2. 类与对象(Classes and Objects)Objective-C是一种面向对象的编程语言,类与对象是其核心概念。
- 类:Class- 对象:Object- 实例变量:Instance Variable- 类方法:Class Method- 对象方法:Instance Method- 初始化:Initialization- 实例化:Instantiation- 继承:Inheritance- 多态:Polymorphism- 封装:Encapsulation- 抽象类:Abstract Class- 类别:Category- 选择器:Selector- 代理:Delegate- 消息发送:Message Sending- 内存管理:Memory Management3. 数据类型(Data Types)Objective-C支持各种常见的数据类型,用于存储和操作数据。
从C++ 到Objective-C(14):内存管理(续)
作者: DevBean 日期: 2011 年03 月30 日发表评论 (5)查看评论
autorelease 池
上一节中我们了解到autorelease 的种种神奇之处:它能够在合适的时候自动释放分配的内存。
但是如何才能让便以其之道什么时候合适呢?这种情况下,垃圾收集器是最好的选择。
下面我们将着重讲解垃圾收集器的工作原理。
不过,为了了解垃圾收集器,就不得不深入了解autorelease 的机制。
所以我们要从这里开始。
当对象收到autorelease 消息的时候,它会被注册到一个“autorelease 池”。
当这个池被销毁时,其中的对象也就被实际的销毁。
所以,现在的问题是,这个池如何管理?
答案是丰富多彩的:如果你使用Cocoa 开发GUI 界面,基本不需要做什么事情;否则的话,你应该自己创建和销毁这个池。
拥有图形界面的应用程序都有一个事件循环。
这个循环将等待用户动作,使应用程序响应动作,然后继续等待下一个动作。
当你使用Cocoa 创建GUI 程序时,这个autorelease 池在事件循环的一次循环开始时被自动创建,然后在循环结束时自动销毁。
这是合乎逻辑的:一般的,一个用户动作都会触发一系列任务,临时变量的创建和销毁一般不会影响到下一个事件。
如果必须要有可持久化的数据,那么你就要手动地使用retain 消息。
另一方面,如果没有GUI,你必须自己建立autorelease 池。
当对象收到autorelease 消息时,它能够找到最近的autorelease 池。
当池可以被清空时,你可以对这个池使用release 消息。
一般的,命令行界面的Cocoa 程序都会有如下的代码:
int main(int argc, char* argv[])
{
NSAutoreleasePool* pool =[[NSAutoreleasePool alloc] init];
//...
[pool release];
return0;
}
注意在Mac OS X 10.5 的NSAutoreleasePool 类新增加了一个drain 方法。
这个方法等价于:当垃圾收集器可用时做release 操作;否则则触发运行垃圾收集。
这对编写在两种情况下都适用的代码时是很有用的。
注意,这里实际上是说,现在有两种环境:引用计数和垃圾回收。
Mac OS 的新版本都会支持垃圾收集器,但是iOS 却不支持。
在引用计数环境下,NSAutoreleasePool 的release 方法会给池中的所有对象发送release 消息,如果对象注册了多次,就会多次给它发release。
drain 和release 在应用计数环境下是等价的。
在垃圾收集的环境下,release 不做任何事情,drain 则会触发垃圾收集。
使用多个autorelease 池
在一个程序中使用多个autorelease 池也是可以的。
对象收到autorelease 消息时会注册到最近的池。
因此,如果一个函数需要创建并使用很大数量临时对象,为了提高性能,可以创建一个局部的autorelease 池。
这种情况下,这些临时变量就可以及时的被销毁,从而在函数返回时就将内存释放出来。
autorelease 的注意点
使用autorelease 可能会有一些误用情况,需要我们特别注意。
∙首先,非必要地发送多个autorelease 类似发送多个release 消息,在内存池清空时会引起内存错误;
∙其次,即使release 可以由autorelease 替代,也不能滥用autorelease。
因为autorelease 要比正常的release 消耗资源更多。
另外,不必要的推迟release 操作
无疑会导致占用大量内存,容易引起内存泄露。
autorelease 和retain
多亏了autorelease,方法才能够创建能够自动释放的对象。
但是,长时间持有对象是一种很常见的需求。
在这种情形下,我们可以向对象发送retain 消息,然后在后面手动的release。
这样,这个对象实际上可以从两个角度去看待:
∙从函数开发者的角度,对象的创建和释放都是有计划的;
∙从函数调用者的角度,使用了retain 之后,对象的生命期变长了(使用retain 将使其引用计数器加1),为了让对象能够正确地被释放,调用者必须负责将计数器再
减1。
我们来理解一下这句话。
对于一个函数的开发者,如果他不使用autorelease,那么,他使用alloc 创建了一个对象并返回出去,那么,他需要负责在合适的时候对这个对象做release 操作。
也就是说,从函数开发者的角度,这个对象的计数器始终是1,一次release 是能够被正常释放的。
此时,函数调用者却使用retain 将计数器加1,但是开发者不知道对象的计数器已经变成2 了,一次release 不能释放对象。
所以,调用者必须注意维护计数器,要调用一次release 将其恢复至1。
Convenience constructor, virtual constructor
将构造对象的过程分成alloc 和init 两个阶段,有时候显得很罗嗦。
好在我们有一个convenience constructor 的概念。
这种构造函数应该使用类名做前缀,其行为类似init,同时要实现alloc。
但是,它的返回对象需要注册到一个内部的autorelease 池,如果没有给它发送retain 消息时,这个对象始终是一个临时对象。
例如:
// 啰嗦的写法
NSNumber* zero_a =[[NSNumber alloc] initWithFloat:0.0f];
...
[zero_a release];
...
// 简洁一些的
NSNumber* zero_b =[NSNumber numberWithFloat:0.0f];
...
// 不需要 release
根据我们前面对内存管理的介绍,这种构造函数的实现是基于autorelease 的。
但是其底层代码并不那么简单,因为这涉及到对self 的正确使用。
事实上,这种构造函数都是类方法,所以self 指向的是Class 类型的对象,就是元类类型的。
在初始化方法,也就是一个实例方法中,self 指向的是这个类的对象的实例,也就是一个“普通的”对象。
编写错误的这种构造函数是很容易的。
例如,我们要创建一个Vehicle 类,包含一个color 数据,编写如下的代码:
// The Vehicle class
@interface Vehicle :NSObject
{
NSColor* color;
}
-(void) setColor:(NSColor*)color;
// 简洁构造函数
+(id) vehicleWithColor:(NSColor*)color;
@end
其对应的实现是:
// 错误的实现
+(Vehicle*) vehicleWithColor:(NSColor*)color
{
// self 不能改变
self =[[self alloc] init]; // 错误!
[self setColor:color];
return[self autorelease];
}
记住我们前面所说的,这里的self 指向的是Class 类型的对象。
// 比较正确的实现
+(id) vehicleWithColor:(NSColor*)color
{
id newInstance =[[Vehicle alloc] init]; // 正确,但是忽略了有子类的情况
[newInstance setColor:color];
return[newInstance autorelease];
}
我们来改进一下。
Objective-C 中,我们可以实现virtual constructor。
这种构造函数通过内省的机制来了解到自己究竟应该创建哪种类的对象,是这个类本身的还是其子类的。
然后它直接创建正确的类的实例。
我们可以使用一个class 方法(注意,class 在Objective-C 中不是关键字);这是NSObject 的一个方法,返回当前对象的类对象(也就是meta-class 对象)。
@implementation Vehicle
+(id) vehicleWithColor:(NSColor*)color
{
id newInstance =[[[self class] alloc] init]; // 完美!我们可以在运行时识别出类[newInstance setColor:color];
return[newInstance autorelease];
}
@end
@interface Car : Vehicle {...}
@end
...
// 创建一个 red Car
id car =[Car vehicleWithColor:[NSColor redColor]];。