Objective-C 浅析

来源:互联网 发布:c语言二级基础知识 编辑:程序博客网 时间:2024/05/21 03:28
1、简介
从今天开始接触IOS开发,而后则接触PHP开发,接触是为了更好的理解开发。IOS的开发前提是Objective-C,本篇简单总结其知识点。
什么是Objective-C,它是C的面向对象的扩展。
2、知识点
1、Objective-C类定义有两个部分:接口部分和实现部分。
接口部分:
@interface className:superclassName
{
Instance variable declarations
}
Method declarations
@end

实现部分:类的方法的实现。
2、NSObject
唯一根类,几乎所有的Objective-C类都是直接或间接的是NSObject的子类,它定义了类工厂方法alloc,负责为那些需要与Objective-C的内存管理系统交互的对象实例和实例方法分配内存。
3、消息
Objective-C使用一种不同的方法,叫做消息。
[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. [receiver message]  
receiver执行一个方法的对象
message 方法的名称
在任何情况下,在方括号中代码都意味着你是在给一个对象或者一个类型发送一个消息(即一个方法调用)。
4、编译器指令
以‘@’字符开头的单词是编译器指令,而不是执行代码。
5、关键字
id  保存指向对象的指针的类型( id类型意味着变量myObject可以是任意类型的对象。所以,当你编译这段代码时,它的实际类型以及它所实现的方法编译器是不知道的);在Objective-C中,所有的对象变量都是指针类型。id类型已经被预定义为指针类型,所以不需要加一个星号。 
nil是一个定义的常量,指向没有对象的指针。
nil消息表示没有操作:它们不做任何事情,并且继续执行下一行代码。

在Objective-C中,nil对象的作用等同于很多其他语言的NULL指针;不同的地方在于,在nil上调用方法不会导致程序崩溃或抛出异常。我们不用在调用一个对象的方法之前检查该对象是否为空。如果你调用了一个nil对象的方法并且该方法有返回值的话,你会得到一个nil返回值。 

BOOL,YES,NO 布尔型 
SEL selector的缩写,选择器,保存一个Objective-C方法名表示的一种类型。
IMP  一个typedef,用于‘一个指针,它指向接收参数id、sel以及可能的其它参数并且返回id的函数’
Class 保存一个类的引用
6、Cocoa数字类型
NSInteger
NSUInteger
CGFloat
NSLog
7、开发文件
.l UNIX man页面的文件
.m 程序源文件
.pch 预编译的头文件
.h 程序头文件
8、消息转发
NSInvocation是封装为对象的一个Objective-C消息表达式,它定义了设置和获取接受者、选择器以及封装的消息表达式的参数方法以及获取返回值的方法。
9、內省
运行时支持在运行时发现对象的各种属性,这个过程叫內省。
10、@class执行
一个类类型
11、copy
NSObject的copy实现对象的拷贝。
浅复制、深赋值、可变复制(NSArray、NSDictionary、NSSet)、不可变赋值(NSNumber、NSColor)。
12、内存管理
在堆上分配内存时,不再需要就释放。
malloc -》free
13、消息细节
1、嵌套 
任何消息参数都可以用返回相应类型的一个消息表达式来替换。

消息传递的关键是,编译器构建每个类和对象时所采用的数据结构。
每个类都包含以下两个必要元素: 
- 一个指向父类的指针。  
- 一个调度表(dispatch table)。
该调度表将类的selector与方法的实际内存地址关联起来。  
每个对象都有一个指向所属类的指针isa。通过该指针,对象可以找到它所属的类,也就找到了其全部父类。

2、向nil发送消息
向一个nil接收者发送一条消息没有任何效果

3、向self发送消息
一个对象的方法调用同一对象的另一个方法,必须使用变量self作为消息接受者。

4、覆盖并向super发送消息
类可以通过在其@implementation部分提供一个不同的实现,来覆盖在其超类中定义的方法。
14、选择器
编译器指令 @selector() 将一个方法名称转换为一个SEL
一条消息的方法名部分有时候叫做选择器或方法选择器,因为,运行时使用它来选择要执行哪一个接收者方法。
15、效率
编译器将你的方法转换为C函数的时候,它在参数列表的前面添加了self和_cmd参数,通过其IMP调用函数时,要填充这些参数。直接函数调用快数倍。
16、类类型
类型为class的变量,用作指向类对象的指针。通过使用类名作作为接收者来调用类方法从而获得指向类对象的一个指针。
17、单体
一个只有单个共享的实例的类。单体类通常用来包装操作系统服务,或者用于检测面板这样的UI项。
18、框架
框架是包的一种类型,动态的载入共享的资源。总称存在frameworkName.framework的形式。
1、Objective-C或C框架
2、Cocoa框架
apple用来表示用Objective-C技术编写的mac程序的名称。它是一个伞式框架,包含3个主要框架(Foundation、Appkit、Core Data);
3、AppKit框架
构建GUI应用程序所需要的类
4、Core Foundation
一个低级层的C语言框架,对象有一个引用计数内存管理系统,对象拥有相同的内存布局则可自由转换。
5、Core Graphics
用于Quartz 2D图形的低层级API。
6、Core Animation
动画框架
7、WebKit
8、ImageIO
9、Core Image
10、Core Audio
11、OpenGL
12、OpenAL
19、可变类和不可变类
基本的类是不可变的,但用于字符或用于字节的有可变与不可变之分。
20、类簇
将复杂性隐藏到一个简单的接口的后面的一种方式。公有可见的类是一个抽象类。
类:NSString、NSArray、NSDictionary、NSSet、NSNumber和NSData都是类簇。
21、NSString、NSMutableString
字符的字节的一个数组,后面跟着一个NULL字节。
如果我们在NSString对象上调用NSString类型对象不支持的方法,编译器就会发出警告。
C字符串可以和NSString互换
NSMutableString是可变的子类
22、集合类
NSArray、NSDictionary、NSSet以及其可变的子类,都是集合类。
23、NSArray
C数组,无边界检查
24、NSDictionary
提供一种方式来处理键值对集合
25、NSSet
对象集合实现一个数学集合
26、NSNumber
存储并返回你喜欢的所有数字类型
27、NSNull
没有对象
28、NSData
字节数据、处理文件或图片等。
29、NSURL
url用来指定文件资源和网络资源的方向前进。
30、隐式循环
使用NSArray的makeObjectsPerformSelectior创建隐式循环
31、NSEnumerator
接收集合中的对象,并将其传递出来,每次一个,并且使用nextObject方法,发送完毕返回nil。
32、异常
阻止程序继续执行的不正常条件。使用编译器指令@try、@catch、@finally处理
33、分类
分类允许我们向一个已有的类添加方法,而不用子类化它,并且不必访问类的源代码。
34、扩展
扩展允许你通过在类的声明文件中添加一个接口部分,从而在公有视野之外声明方法。提供一个内部的setter方法来访问你希望公有且只读的实例变量。
35、@property语句
[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @property(attributes)type name;  
attributes描述了如何编写访问器
[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #import <Cocoa/Cocoa.h>     
  2.      
  3. @interface Photo : NSObject {     
  4.     NSString* caption;     
  5.     NSString* photographer;     
  6. }     
  7. @property (retain) NSString* caption;     
  8. @property (retain) NSString* photographer;     
  9.      
  10. @end   

Category类别
[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #import <Cocoa/Cocoa.h>     
  2.                  
  3. @interface NSString (Utilities)     
  4. - (BOOL) isURL;     
  5. @end   
36、assign、retain、copy
这些属性影响到合成的setter如何构建。
37、readwrite和readonly
读写、只读
38、nonatomic
没有声明为nonatomic的特性默认都是atomic的。如果指定nonatomic,编译器则合成访问器而不考虑线程安全性。
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1.   readwrite(默认)或者  readonly:设置属性是可读写的(拥有  getter/setter)或是只读的(只有  getter);   
  2.    assign(默认),retain  或  copy:设置属性的存储方式;   
  3.    nonatomic:不生成线程安全的代码,默认是生成的(没有  atomic  关键字);   
  4.    getter=…,setter=…:改变访问器默认的名字  
39、协议
协议是一组预定义的方法,一个类可以选择来实现它们。
protocol-协议,就是使用了这个协议后就要按照这个协议来办事,协议要求实现的方法就一定要实现。  
delegate-委托,顾名思义就是委托别人办事,就是当一件事情发生后,自己不处理,让别人来处理。


当一个A view 里面包含了B view  b view需要修改a view界面,那么这个时候就需要用到委托了。  
需要几个步骤 
 1。首先定一个协议 
 2。a view实现协议中的方法 
3。b view设置一个委托变量  
4。把b view的委托变量设置成a view,意思就是 b view委托a view办事情。  
5。事件发生后,用委托变量调用a view中的协议方法      
有很多时候是需要在B类口中来完成一些对A类的操作, 这时就需要A设个代理让B来完成了,这个在应用中很常见也很实用。            

 关于正式协议: 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1.  .  @protocol protocolName      
  2. @optional -(void)delegateMethodA      
  3. @required -(void)delegateMethodB     
  4.  . // other methods    . ...      
  5. @end    
正式协议类似于java的借口或抽象类。
@optional 的方法,可实现也可不实现,但@required 的方法必需实现。默认为@required。   
委托代理就是在在一个对象a内部设置一个 id类型的实例变量,然后将另外一个对象b赋值给a的这个实例变量,这样就可用通过操作实例变量a的这个id变量来调用b对象的方法。

采用一个协议的类,必须实现协议的必需方法,可以自由的实现或不实现协议的任何可选的方法。
其中@required指令声明的方法是必需的。
40、TablePrinter
为其它对象提供一个表格打印任务服务
TablePrinterDataSource 协议的任何对性爱那个打印出一个表格
41、引用计数
内存管理提供了两种替代系统:引用计数和自动垃圾收集。
原理:每个对象存储了一个计数,它是使用该对象的其它对象的数目。
1,alloc, allocWithZone,new(带初始化)
   为对象分配内存,retainCount为“1”,并返回此实例
2,release
   retainCount 减“1”,减到“0”时调用此对象的dealloc方法
3,retain
   retainCount 加“1”
4,copy,mutableCopy
   复制一个实例,retainCount数为“1”,返回此实例。所得到的对象是与其它上下文无关的,独立的对象(干净对象)。
5,autorelease
   在当前上下文的AutoreleasePool栈顶的autoreleasePool实例添加此对象,由于它的引入使Objective-C(非GC管理环境)由全手动内存管

理上升到半自动化。AutoreleasePool使Objective-C成为内存管理半自动化语言。
在设置器里面使用autorelease方法会更加安全一些,因为要改变的变量的新旧两个值可能指向的是同一个对象。而你可能不希望立刻释放实际上你要保留的对象。
42、所有权
你创建了一个对象,你拥有它。
通过向其他人所创建的对象发送一条retain消息,你拥有它。
当你使用完时,必须发送release消息来释放所有权。
当不再使用时,必须销毁它。
43、垃圾收集
通过一个运行程序来找到不再使用的对象并销毁他们,并将他们的字节返给堆。)。 
. 释放一个对象的引用实际上有两种方法:release 和 autorelease。标准的release会立刻释放对象的引用。autorelease会等一会儿才释放,但是引用实际上会一直存在,直到当前方法结束(除非你添加自定义的代码来明确的改变它)。 
dealloc方法在一个对象从内存中删除时被调用。通常在这个方法里面释放所有对象里的实例变量。
分配的(alloc)对象,或者是保留(retain)在一些地方的对象,都需要给他们发送一个release消息。
44、强引用和弱引用
在引用计数之下,一个对象通过保留另一个对象,形成对其的强引用,这确保了被保留的对象保持活的状态,直到保留对象
使用完它并释放它。相反,一个对象通过保留另一个对象的指针而不保留对象本身,形成对另一个对象的弱引用。
45、XCode、Objective-C、Cocoa
XCode:你可以把它看成是一个开发环境,就好像Visual Studio或者Netbeans或者SharpDevelop一样的玩意。你可以将Interface Builder认为是Visual Studio中用来画界面的那部分功能单独提出来的程序。    
Objective-C:这是一种语言,就好像c++是一种语言,Java是一种语言,c#是一种语言,莺歌历史也是一种语言一样。    
Cocoa:是一大堆函数库,就好像MFC、.NET、Swing这类玩意,人家已经写好了一堆现成的东西,你只要知道怎么用就可以了。 
46、#import、@interface
#import     
你可以把它认为是#include,一样的。但是最好用#import,记住这个就行了。  
#import指令会自动防止将同一个文件导入多次。   
@interface 接口指令
类的接口(interface)通常存放在类似ClassName.h的文件中,定义实例变量(protected)和公用(public)方法。 
  类的实现存放在ClassName.m这样的文件中,它包含了这些方法的实际实现代码。它通常还定义了客户类不能访问的私有(private)实体变量、方法。
47、方法调用
调用对象的方法:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. output = [object methodWithOutput];     
  2. output = [object methodWithInputAndOutput:input];    
调用类的方法:(创建对象)
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. id myObject = [NSString string];     
  2. 或  
  3. NSString* myString = [NSString string];     
嵌套调用:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. [NSString stringWithFormat:[prefs format]];    
多输入参数:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. 声明:  
  2. -(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;     
  3. 调用:  
  4. BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO];    
48、访问器
点语法只能使用在设置器(setter)和获取器(getter)上,而不能用于普通方法。
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. setter  
  2. [photo setCation:@”Day at the Beach”];    
  3. output = [photo caption];    
点操作符
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. photo.caption = @”Day at the Beach”;     
  2. output = photo.caption;     
只有当访问器不存在的时候,@synthesize才会自动生成访问器,所以,即使是使用@synthesize声明了一个属性,你仍然可以实现自定义的getter和setter。编译器只会自动生成你没有自定义的方法。
49、创建对象
创建自动释放的对象
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. NSString* myString = [NSString string];    
手工alloc创建
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. NSString* myString = [[NSString alloc] init];     
注:[NSString alloc] 是NSString类本身的alloc方法调用。这是一个相对低层的调用,它的作用是分配内存及实例化一个对象。
            [[NSString alloc] init] 调用新创建对象的init方法。init方法通常做对象的初始化设置工作,比如创建实例变量。
50、内存管理
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //string1 将被自动释放     
  2. NSString* string1 = [NSString string];     
  3.      
  4. //必须在用完后手工释放     
  5. NSString* string2 = [[NSString alloc] init];     
  6. [string2 release];     
51、alloc
alloc方法调用,作用是分配内存及实例化一个对象。调用新创建对象的init方法,通常做对象的初始化设置工作,比如创建实例变量。
如果你通过手工alloc的方式创建一个对象,之后你需要release这个对象。同样,你也不能手工释放(release)一个能自动释放(autoreleased)的对象,因为这将会使你的应用程序崩溃。
52、IBOutlet、IBAction
如果你希望在Interface Builder中能看到这个控件对象,那么在定义的时候前面加上IBOutlet,在IB里就能看到这个对象的outlet,如果你希望在Interface Builder里控制某个对象执行某些动作,就在方法前面加上(IBAction)。
相关面试:
1、obj-c有多重继承么?不是的话有什么替代方法? 
cocoa中所有的类都是NSObject的子类  多继承在这里是用protocol委托代理来实现的  你不用去考虑繁琐的多继承,虚基类的概念.  ood的多态特性  在obj-c中通过委托来实现.  
2、obj-c有私有方法么?私有变量呢   
objective-c -类里面的方法只有两种, 静态方法和实例方法. 这似乎就不是完整的面向对象了,按照OO的原则就是一个对象只暴露有用的东西. 

如果没有了私有方法的话, 对于一些小范围的代码重用就不那么顺手了. 在类里面声名一个私有方法  

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @interface Controller :  
  2.  NSObject { NSString *something; }   
  3.  (void)thisIsAStaticMethod;  
  4. (void)thisIsAnInstanceMethod;  
  5.  @end   
  6.   
  7. @interface Controller  
  8.  (private) - (void)thisIsAPrivateMethod;   
  9. @end   

 @private可以用来修饰私有变量  在Objective‐C中,所有实例变量默认都是私有的,所有实例方法默认都是公有的 。

3、c和obj-c如何混用 
1、obj-c的编译器处理后缀为m的文件时,可以识别obj-c和c的代码,处理mm文件可以识别obj-c,c,c++代码,但cpp文件必须只能用c/c++代码,而且cpp文件include的头文件中,也不能出现obj-c的代码,因为cpp只是cpp 。   
2、在mm文件中混用cpp直接使用即可,所以obj-c混cpp不是问题 。
3、在cpp中混用obj-c其实就是使用obj-c编写的模块是我们想要的。
4、目标-动作机制 
目标是动作消息的接收者。一个控件,或者更为常见的是它的单元,以插座变量(参见"插座变量"部分)  的形式保有其动作消息的目标。 动作是控件发送给目标的消息,或者从目标的角度看,它是目标为了响应动作而实现的方法。 程序需要某些机制来进行事件和指令的翻译。这个机制就是目标-动作机制。 
5、#import和#include的区别,@class代表什么?
@class一般用于头文件中需要声明该类的某个实例变量的时候用到,在m文件中还是需要使用#import而#import比起#include的好处就是不会引起重复包含。
6、谈谈Object-C的内存管理方式及过程?
1、当你使用new,alloc和copy方法创建一个对象时,该对象的保留计数器值为1.当你不再使用该对象时,你要负责向该对象发送一条release或autorelease消息.这样,该对象将在使用寿命结束时被销毁.

2、当你通过任何其他方法获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,你不需要执行任何操作来确保该对象被清理.如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它.

3、如果你保留了某个对象,你需要(最终)释放或自动释放该对象.必须保持retain方法和release方法的使用次数相等.
7、内存管理 Autorelease、retain、copy、assign的set方法和含义?
1、你初始化(alloc/init)的对象,你需要释放(release)它。例如:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. NSMutableArray aArray = [[NSArray alloc] init]; 后,需要 [aArray release];  
2、你retain或copy的,你需要释放它。例如:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. [aArray retain] 后,需要 [aArray release];  
3、被传递(assign)的对象,你需要斟酌的retain和release。例如:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. obj2 = [[obj1 someMethod] autorelease];  
对象2接收对象1的一个自动释放的值,或传递一个基本数据类型(NSInteger,NSString)时:你或希望将对象2进行retain,以防止它在被使用之前就被自动释放掉。但是在retain后,一定要在适当的时候进行释放。
关于索引计数(Reference Counting)的问题
retain值 = 索引计数(Reference Counting)
NSArray对象会retain(retain值加一)任何数组中的对象。当NSArray被卸载(dealloc)的时候,所有数组中的对象会 被 执行一次释放(retain值减一)。不仅仅是NSArray,任何收集类(Collection Classes)都执行类似操作。例如 NSDictionary,甚至UINavigationController。Alloc/init建立的对象,索引计数为1。无需将其再次retain。
[NSArray array]和[NSDate date]等“方法”建立一个索引计数为1的对象,但是也是一个自动释放对象。所以是本地临时对象,那么无所谓了。如果是打算在全Class中使用的变量(iVar),则必须retain它。缺省的类方法返回值都被执行了“自动释放”方法。(*如上中的NSArray),在类中的卸载方法“dealloc”中,release所有未被平衡的NS对象。(*所有未被autorelease,而retain值为1的)
8、浅拷贝和深拷贝区别是什么
简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误。
9、C和obj-c 如何混用
1、obj-c的编译器处理后缀为m的文件时,可以识别obj-c和c的代码,处理mm文件可以识别obj-c,c,c++代码,但cpp文件必须只能用c/c++代码,而且cpp文件include的头文件中,也不能出现obj-c的代码,因为cpp只是cpp。
2、在mm文件中混用cpp直接使用即可,所以obj-c混cpp不是问题。
3、在cpp中混用obj-c其实就是使用obj-c编写的模块是我们想要的。
如果模块以类实现,那么要按照cpp class的标准写类的定义,头文件中不能出现obj-c的东西,包括#import cocoa的。实现文件中,即类的实现代码中可以使用obj-c的东西,可以import,只是后缀是mm。

如果模块以函数实现,那么头文件要按c的格式声明函数,实现文件中,c++函数内部可以用obj-c,但后缀还是mm或m。


总结:只要cpp文件和cpp include的文件中不包含obj-c的东西就可以用了,cpp混用obj-c的关键是使用接口,而不能直接使用 实现代 码,实际上cpp混用的是obj-c编译后的o文件,这个东西其实是无差别的,所以可以用。obj-c的编译器支持cpp

10、Objective-C中类别和类扩展的区别
category和extensions的不同在于后者可以添加属性。另外后者添加的方法是必须要实现的。

extensions可以认为是一个私有的Category。
11、我们说的Objective-C是动态运行时语言是什么意思?
多态。 主要是将数据类型的确定由编译时,推迟到了运行时。
这个问题其实浅涉及到两个概念,运行时和多态。
简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;
那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。
也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。
因此也可以说,运行时机制是多态的基础?
12、Objective-C堆和栈的区别?
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因 此,能从栈获得的空间较小。


堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。


碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出

分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
0 0
原创粉丝点击