自动释放池是什么,如何工作
答案:当您向一个对象发送一个autorelease 消息时,Cocoa就会将该对象 的一个引用放入到最新的自动释放池。
它仍然是个正当的对象,因此 自动释放池定义的作用域内的其它对象可以向它发送消息。当 程序 执行到作用域
结束的位置时,自动释放池就会被释放,池中的所有对 象也就被释放。 ojc-c 是 通过一种"referring counting
"(引用计数)的方式来管理内 存的, 对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰 到有copy,retain
的时候引用计数都会加一, 每当碰到release和 autorelease的时候引用计数就会减一,如果此对象的计数变为了0,
就会被系统销毁. 2. NSAutoreleasePool 就是用来做引用计数的管理工作的,这个东西 一般不用你管的.
3. autorelease和release没什么区别,只是引用计数减一的时机不 同而已,autorelease会在对象的使用真正结束
的时候才做引用计数 减1.
readwrite,readonly,assign,retain,copy,nonatomic 属性的作用
答案: @property是一个属性访问声明,扩号内支持以下几个属性:
1,getter=getterName,setter=setterName,设置setter与getter的方法名
2,readwrite,readonly,设置可供访问级别
3,assign,setter方法直接赋值,不进行任何retain操作,为了解决原类 型与环循引用问题
4,retain,setter方法对参数进行release旧值再retain新值,所有实现 都是这个顺序(CC上有相关资料)
5,copy,setter方法进行Copy操作,与retain处理流程一样,先旧值 release,再Copy出新的对象,
retainCount为1。这是为了减少对上下文 的依赖而引入的机制。
6,nonatomic,非原子性访问,不加同步,多线程并发访问会提高性能。 注意,如果不加此属性,
则默认是两个访问方法都为原子型事务访问。锁 被加到所属对象实例级。
ViewController 的 loadView, viewDidLoad, viewDidUnload 分别是在什么时候调用的?
在自定义ViewController的时候这几个 函数里面应该做什么工作?
答案: viewDidLoad在view 从nib文件初始化时调用,loadView在controller的view为nil时调用。
此方法在编程实现view时调用,view 控制器默认会注册memory warning notification,
当view controller的任何view 没有用的时候,viewDidUnload会被调用,在这里实现将 retain 的
view release,如果是retain的IBOutlet view 属性则不要在这里release, IBOutlet会负责release 。
类工厂方法是什么
答案:类工厂方法的实现是为了向客户提供方便,它们将分配和初始化合在一个步骤中,返回 被创建的对象,
并 进行自动释放处理。这些方法的形式是+ (type)className... (其中 className不包括任何前缀)。 工厂
方法可能不 仅仅为了方便使用。它们不但可以将分配和初始化合在一起,还可以 为 初始化过程提供对 象的分
配信息。 类工厂方法的 另一个目的是使类(比如NSWorkspace)提供单件实例。虽 然init...方法 可以确认一
个类在每次程序运行过程只存在一个实例,但它需要首先分配一个“生的”实 例,然后还必须释放该实例。 工厂
方法则可以避免为可能没有用的对象盲目分配内存。
为什么很多内置类如UITableViewController的delegate属性都是assign而不是 retain的?
答:会引起循环引用。 这里delegate我们只是想得到实现了它delegate方法的对象,然后拿到这个对象的
指针就 可以了, 我们不期望去改变它或者做别的什么操作,所以我们只要用assign拿到它的指针就可以了。
而 用retain的话,计数器加1。我们有可能在别的地方期望释放掉delegate这个对象, 然后通过一些判断
比如说它是否已经被释放,做一些操作。但是 实际上它retainCount还 是1,没有被释放掉,要在UITableViewControlle
r的dealloc里面才被释放掉(这里我只是 举个 例子,一般retain的对象都是在dealloc里被释放)。这里就
会造成一些问题出现。 而如果你确定不会有冲突的问题出现的话,或者你也希望用到delegate的这个对象,
直到 你不用它为止,那么用retain也未尝不可,只是需要最后release一次。
.main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
答:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int *ptr=(int *)(&a+1);
则ptr实际是&(a[5]),也就是a+5
原因如下:
&a是数组指针,其类型为 int (*)[5];
而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。
a是长度为5的int数组指针,所以要加 5*sizeof(int)
所以ptr实际是a[5]
但是prt与(&a+1)类型是不一样的(这点很重要)
所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样
a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,
a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].
以下为Windows NT下的32位C++程序,请计算sizeof的值
void Func ( char str[100] )
{
sizeof( str ) = ?
}
void *p = malloc( 100 );
sizeof ( p ) = ?
答:这题很常见了,Func ( char str[100] )函数中数组名作为函数形参时,在函数体内,
数组名失去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,
可以作自增、自减等操作,可以被修改。Windows NT 32位平台下,指针的长度(占用内存的大小)
为4字节,故sizeof( str ) 、sizeof ( p ) 都为4。
- (void)*getNSString(const NSString * inputString)
{
inputString = @"This is a main test\n";
return ;
}
-main(void)
{
NSString *a=@"Main";
NSString *aString = [NSString stringWithString:@"%@",getNSString(a)];
NSLog(@"%@\n", aString);
}
答:最后问输出的字符串:NULL,output在函数返回后,内存已经被释放。
用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
答:#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在这想看到几件事情:
#define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)
懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。
写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个。
答:#define MIN(A,B) ((A) <= (B) ? (A) : (B))
这个测试是为下面的目的而设的:
标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方
法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比 if-then-else 更优化的代码,了解这个用法是很重要的。 懂得在宏中小心地把参数用括号括起来 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事? least = MIN(*p++, b);
结果是:
((*p++) <= (b) ? (*p++) : (*p++))
这个表达式会产生副作用,指针p会作三次++自增操作。
static 关键字的作用:
(1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,
因此其值在下次调用时仍维持上次的值;
(2)在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明
它的模块内;
(4)在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
#import跟#include的区别,@class呢?
@class一般用于头文件中需要声明该类的某个实例变量的时候用到,在m文件中还是需要使用#import
而#import比起#include的好处就是不会引起交叉编译
MVC模式的理解
MVC设计模式考虑三种对象:模型对象、视图对象、和控制器对象。模型对象代表特别的知识和专业技能,
它们负责保有应用程序的数据和定义操作数据的逻辑。视图对象知道如何显示应用程序的模型数据,而且
可能允许用户对其进行编辑。控制器对象是应用程序的视图对象和模型对象之间的协调者。
列举几种进程的同步机制,并比较其优缺点。
答案: 原子操作 信号量机制 自旋锁 管程,会合,分布式系统
进程之间通信的途径
答案:共享存储系统消息传递系统管道:以文件系统为基础
进程死锁的原因
答案:资源竞争及进程推进顺序非法
死锁的4个必要条件
答案:互斥、请求保持、不可剥夺、环路
死锁的处理
答案:鸵鸟策略、预防策略、避免策略、检测与解除死锁
堆和栈的区别
答案:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,
容易产生memory leak。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的
最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),
如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,
而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。
对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
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.
目标-动作机制
答案:
目标是动作消息的接收者。一个控件,或者更为常见的是它的单元,以插座变量(参见"插座变量"部分)
的形式保有其动作消息的目标。
动作是控件发送给目标的消息,或者从目标的角度看,它是目标为了响应动作而实现的方法。
程序需要某些机制来进行事件和指令的翻译。这个机制就是目标-动作机制。
objc的内存管理
答案: 如果您通过分配和初始化(比如[[MyClass alloc] init])的方式来创建对象,您就拥 有这个对象,
需要负责该对象的释放。这个规则在使用NSObject的便利方法new 时也同样适用。 如果您拷贝一个对象,
您也拥有拷贝得到的对象,需要负责该对象的释放。 如果您保持一个对象,您就部分拥有这个对象,需要
在不再使用时释放该对象。 反过来,如果您从其它对象那里接收到一个对象,则您不拥有该对象,也不应
该释放它(这个规则有少数 的例外,在参考文档中有显式的说明)。
单件实例是什么
答案:
Foundation 和 Application Kit 框架中的一些类只允许创建单件对象,即这些类在当前进程中的唯一实例。
举例来说,NSFileManager 和NSWorkspace 类在使用时都是基于进程进行单件对象的实例化。当向这些类
请求实例的时候,它们会向您传递单一实例的一个引用,如果该实例还不存在,则首先进行实例的分配和初始化
。单件对象充当控制中心的角色,负责指引或协调类的各种服务。如果类在概念上只有一个实例(比如 NSWorkspace),
就应该产生一个单件实例,而不是多个实例;如果将来某一天可能有多个实例,您可以使用单件实例机制,
而不是工厂方法或函数。
线程与进程的区别和联系?
答案:
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。?
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,
在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,
但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,
但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,
只能用线程,不能用进程。
定义属性时,什么情况使用copy,assign,和retain?
答案:assign用于简单数据类型,如NSInteger,double,bool。retain和copy用于对象。copy用于当a指向一个对象,
b也想指向同样的对象的时候,如果用assign,a如果释放,再调用b会crash,如果用copy 的方式,a和b各自有自己的内存,
就可以解决这个问题。retain 会使计数器加1,也可以解决assign的问题。另外:atomic和nonatomic用来决定编译器
生成的getter和setter是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。?加了
atomic,setter函数会变成下面这样:
if (property != newValue)
{
[property release];
property = [newValue retain];
}
对象是在什么时候被release的?
答案:
引用计数为0时。
autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了
当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。对于每一个Runloop,
系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,
在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object(就是autorelease的对象)
会被release。那什么是一个Runloop呢? 一个UI事件,Timer call, delegate call, 都会是一个新的Runloop。
IPhone OS有没有垃圾回收?
没有。iPhone开发的时候没有垃圾回收机制。
ViewController 的 loadView, viewDidLoad, viewDidUnload 分别是在什么时候调用的?
在自定义ViewController的时候这几个函数里面应该做什么工作?
答案:由init、loadView、viewDidLoad、viewDidUnload、dealloc的关系说起。
init方法:在init方法中实例化必要的对象(遵从LazyLoad思想)。init方法中初始化ViewController本身
loadView方法:当view需要被展示而它却是nil时,viewController会调用该方法。不要直接调用该方法。如果手工维护views,必须重载重写该方法;如果使用IB维护views,必须不能重载重写该方法。loadView和IB构建view,你在控制器中实现了loadView方法,那么你可能会在应用运行的某个时候被内存管理控制调用。 如果设备内存不足的时候, view 控制器会收到didReceiveMemoryWarning的消息。 默认的实现是检查当前控制器的view是否在使用。 如果它的view不在当前正在使用的view hierarchy里面,且你的控制器实现了loadView方法,那么这个view将被release, loadView方法将被再次调用来创建一个新的view。
viewDidLoad方法:viewDidLoad 此方法只有当view从nib文件初始化的时候才被调用。重载重写该方法以进一步定制view,在iPhone OS 3.0及之后的版本中,还应该重载重写viewDidUnload来释放对view的任何索引;viewDidLoad后调用数据Model。
viewDidUnload方法:当系统内存吃紧的时候会调用该方法(注:viewController没有被dealloc)。内存吃紧时,在iPhone OS 3.0之前didReceiveMemoryWarning是释放无用内存的唯一方式,但是OS 3.0及以后viewDidUnload方法是更好的方式。在该方法中将所有IBOutlet(无论是property还是实例变量)置为nil(系统release view时已经将其release掉了)在该方法中释放其他与view有关的对象、其他在运行时创建(但非系统必须)的对象、在viewDidLoad中被创建的对象、缓存数据等 release对象后,将对象置为nil(IBOutlet只需要将其置为nil,系统release view时已经将其release掉了)
一般认为viewDidUnload是viewDidLoad的镜像,因为当view被重新请求时,viewDidLoad还会重新被执行viewDidUnload中被release的对象必须是很容易被重新创建的对象(比如在viewDidLoad或其他方法中创建的对象),不要release用户数据或其他很难被重新创建的对象
dealloc方法:viewDidUnload和dealloc方法没有关联,dealloc还是继续做它该做的事情。