七、内存管理
1.objc的内存管理简介
(1)如果您通过分配和初始化(比如[[MyClass alloc] init])的方式来创建对象,您就拥有这个对象,需要负责该对象的释放。这个规则在使用NSObject的便利方法new时也同样适用。
(2)如果您拷贝一个对象,您也拥有拷贝得到的对象,需要负责该对象的释放。
(3)如果您保持一个对象,您就部分拥有这个对象,需要在不再使用时释放该对象。
(4)通过alloc,new, copy, retain的对象,retainCount+1;release或者autorelease后retainCount-1,如果retainCount为0时,对象会被消毁,dealloc方法被调用。
(5)谁拥有,谁管理;
2.什么是retain count?
答:引用计数(ref count或者retain count)。对象的内部保存一个数字,表示被引用的次数。
例如,某个对象被两个指针所指向(引用)那么它的retain count为2。需要销毁对象的时候,不直接调用dealloc,而是调用release。release会让retain count减1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。
3.自动释放池是什么,如何工作?
答:当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。
4.autorelease的对象是在什么时候被release的?
答:autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。
对于每一个Runloop,系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object(就是autorelease的对象)会被release。
5.那什么是一个Runloop呢?
一个UI事件,Timer call,delegate call,都会是一个新的Runloop。
6.以下每行代码执行后,person对象的retain count分别是多少
Person *person = [[Person alloc] init]; count 1
[person retain]; count 2
[person release];count 1
[person release];retain count = 1;
7.下面代码中obj2是否需要dealloc?
ClassA *obj1 = [[ClassA alloc] init];
ClassA *obj2 = obj1;
[obj1 hello]; //输出hello
[obj1 release];
[obj2 hello]; //程序能否执行到这一行?
[obj2 release];
答不需要他和obj2指向的是同一块空间
8.看下面的程序,第一个NSLog会输出什么?这时str的retainCount是多少?第二个和第三个呢?为什么?
NSMutableArray* ary = [[NSMutableArray array] retain];
NSString *str = [NSString stringWithFormat:@"test"];
[str retain];
[ary addObject:str];
NSLog(@”%@%d”,str,[str retainCount]);
[str retain];
[str release];
[str release];
NSLog(@”%@%d”,str,[str retainCount]);
[ary removeAllObjects];
NSLog(@”%@%d”,str,[str retainCount]);
答:
str的retainCount创建+1,retain+1,加入数组自动+1
3
retain+1,release-1,release-1
2
数组删除所有对象,所有数组内的对象自动-1
1
9.autorelease和垃圾回收机制(gc)有什么关系?
答:autorelase是用代码手写,在eventloop结束后被释放。
垃圾回收机制开启的话,你只用alloc,不用release,它会自动侦测一些你不用的对象,将它release掉。
可能在实现方式上或者说release的时机判断上有不同,但是效果都是自动relase这个对象。
10.IPhone OS有没有垃圾回收(gc)?
没有。iPhone开发的时候没有垃圾回收机制,OC支持gc,但只限制在Mac OS上。
11.这段代码有什么问题,如何修改
for (int i = 0; i < someLargeNumber; i++)
{
NSString *string = @”Abc”;
string = [string lowercaseString];
string = [string stringByAppendingString:@"xyz"];
NSLog(@“%@”, string);
}
答:同时生成了大量autorelease对象,不利于内存管理。但是如果放在子线程的话,而且没有开启垃圾回收机制的话,则会造成内存泄露。
修改一:
for(int i = 0; i
NSAutoreleasePool * pool1 = [[NSAutoreleasePool alloc] init];
NSString *string = @"Abc";
string = [string lowercaseString];
string = [string stringByAppendingString:@"xyz"];
NSLog(@"%@",string);
[pool1 drain];
}
修改二:
NSAutoreleasePool * pool1 = [[NSAutoreleasePool alloc] init];
for(int i = 0; i
NSString *string = @"Abc";
string = [string lowercaseString];
string = [string stringByAppendingString:@"xyz"];
NSLog(@"%@",string);
}
[pool1 drain];
一点说明:
修改一的autoreleasePool的建立方法是考虑在如果每次循环生成大量的autorelease对象,这些对象占用很大内存的情况下,循环了1000次,那么这些所有的内存加起来就已经够程序崩溃了,那么这时候加在循环里面可以避免程序崩溃。
但是如果程序循环1000次生成的内存也不是很多,则可以用方法二,提高效率。
[pool drain]和[pool release]在没有垃圾回收机制的情况下,他们的效果是一样的。但是如果做mac开发而且开启了垃圾回收机制的话,要用drain而不是release,因为那种情况下用release只是把pool这个对象清理了,而pool里面的对象是没有被清理掉的。
附:CoreFoundation部分
1)create,copy会形成own关系,不需要时应该CFRelease();
2) get方式得到的object,不形成own关系,不需要管理;
3)一个object作为参数传递到函数中后,receiver不与他形成own关系,也就是可能在任何时间被deallocate掉,所以需要CFRetain();
4)CFIndex count = CFGetRetainCount(obj);
5) myCFObj1 = myCFObj2-->引用拷贝,不会生成2个对象,应该使用CFStringCreatCopy()
6)符合的CF对象,copy时需要自己实现deep copy;
八、CocoaTouch
1.main.m中都发生了什么?
答:程序入口,创建自动释放池,创建应用程序对象,并启动其主循环,运行程序,释放程序运行期间得所有加入自动释放池的对象。
2.int retVal = UIApplicationMain(argc, argv, nil, nil);是什么意思?
答:对UIApplication对象进行了初始化,这个方法除了argc和argv参数外,另外这个函数还有两个字符串参数来识别UIApplication类和UIApplication代理类,在这里默认是2个nil,第一个参数为nil就默认把UIApplication类作为缺省值进行初始化,可以在这里不填nil而是使用自己定义的UIApplication子类。至于第二个参数nil就设置为nil就把模板生成的HelloWorldAppdelegate类作为默认值。
3.编写NSArray的setter和getter.
答:getter比较容易,直接返回NSArray的指针就可以了,
setter注意每个元素加进去得时候需要retain一次。
4.什么是Notification?答:观察者模式,controller向defaultNotificationCenter添加自己的notification,其他类注册这个notification就可以收到通知,这些类可以在收到通知时做自己的操作(多观察者默认随机顺序发通知给观察者们,而且每个观察者都要等当前的某个观察者的操作做完才能轮到他来操作,可用NotificationQueue的方式安排观察者的反应顺序,也可以在添加观察者中设定反映时间,取消观察需要在viewDidUnload跟dealloc中都要注销)。
5.多线程编程中,NSThread子线程与主线程的交互.
答:子线程内执行[self performSelectorOnMainThread:@selector(function:)withObject:];通知主线程执行相应方法。或者用全局变量的方式。
6.objective-c中是所有对象间的交互是如何实现的?(深圳皆凯科技有限公司笔试题)
答:在Objective-C中所有对象间的交互都是通过指针实现的,确切的说话,叫做发送消息。
8.objective-c中是如何实现线程同步的?
答:关键字@synchronized()
7.id声明的对象有什么特性?
答:id声明的对象具有运行时的特性,即可以指向任意类型的objcetive-c的对象;
8.cocoa touch框架
iPhone OS应用程序的基础Cocoa Touch框架重用了许多Mac系统的成熟模式,但是它更多地专注于触摸的接口和优化。UIKit为您提供了在iPhone OS上实现图形,事件驱动程序的基本工具,其建立在和Mac OS X中一样的Foundation框架上,包括文件处理,网络,字符串操作等。
Cocoa Touch具有和iPhone用户接口一致的特殊设计。有了UIKit,您可以使用iPhone OS上的独特的图形接口控件,按钮,以及全屏视图的功能,您还可以使用加速仪和多点触摸手势来控制您的应用。
各色俱全的框架除了UIKit外,Cocoa Touch包含了创建世界一流iPhone应用程序需要的所有框架,从三维图形,到专业音效,甚至提供设备访问API以控制摄像头,或通过GPS获知当前位置。Cocoa Touch既包含只需要几行代码就可以完成全部任务的强大的Objective-C框架,也在需要时提供基础的C语言API来直接访问系统。这些框架包括:
Core Animation
通过Core Animation,您就可以通过一个基于组合独立图层的简单的编程模型来创建丰富的用户体验。
Core Audio
Core Audio是播放,处理和录制音频的专业技术,能够轻松为您的应用程序添加强大的音频功能。
Core Data
提供了一个面向对象的数据管理解决方案,它易于使用和理解,甚至可处理任何应用或大或小的数据模型。
功能列表:框架分类
下面是Cocoa Touch中一小部分可用的框架:
音频和视频
Core Audio
OpenAL
Media Library
AV Foundation
数据管理
Core Data
SQLite
图形和动画
Core Animation
OpenGL ES
Quartz 2D
网络/li>
Bonjour
WebKit
BSD Sockets
用户应用
Address Book
Core Location
Map Kit
Store Kit
九、设计模式
1.MVC模式的理解
MVC设计模式考虑三种对象:模型对象、视图对象、和控制器对象。模型对象代表特别的知识和专业技能,它们负责保有应用程序的数据和定义操作数据的逻辑。视图对象知道如何显示应用程序的模型数据,而且可能允许用户对其进行编辑。控制器对象是应用程序的视图对象和模型对象之间的协调者。
2.描述一下iOS SDK中如何实现MVC的开发模式
MVC是模型、视图、控制开发模式,对于iOS SDK,所有的View都是视图层的,它应该独立于模型层,由视图控制层来控制。所有的用户数据都是模型层,它应该独立于视图。所有的ViewController都是控制层,由它负责控制视图,访问模型数据。
3.类工厂方法是什么
类工厂方法的实现是为了向客户提供方便,它们将分配和初始化合在一个步骤中,返回被创建的对象,并进行自动释放处理。这些方法的形式是+ (type)className…(其中className不包括任何前缀)。
工厂方法可能不仅仅为了方便使用。它们不但可以将分配和初始化合在一起,还可以为初始化过程提供对象的分配信息。
类工厂方法的另一个目的是使类(比如NSWorkspace)提供单件实例。虽然init…方法可以确认一个类在每次程序运行过程只存在一个实例,但它需要首先分配一个“生的”实例,然后还必须释放该实例。
工厂方法则可以避免为可能没有用的对象盲目分配内存。
4.单件实例是什么
Foundation和Application Kit框架中的一些类只允许创建单件对象,即这些类在当前进程中的唯一实例。举例来说,NSFileManager和NSWorkspace类在使用时都是基于进程进行单件对象的实例化。当向这些类请求实例的时候,它们会向您传递单一实例的一个引用,如果该实例还不存在,则首先进行实例的分配和初始化。单件对象充当控制中心的角色,负责指引或协调类的各种服务。如果类在概念上只有一个实例(比如
NSWorkspace),就应该产生一个单件实例,而不是多个实例;如果将来某一天可能有多个实例,您可以使用单件实例机制,而不是工厂方法或函数。
十、操作系统
1.线程与进程的区别和联系?
答:进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
2.列举几种进程的同步机制,并比较其优缺点。
答案:原子操作信号量机制自旋锁管程,会合,分布式系统
进程之间通信的途径
答案:共享存储系统消息传递系统管道:以文件系统为基础
进程死锁的原因
答案:资源竞争及进程推进顺序非法
死锁的4个必要条件
答案:互斥、请求保持、不可剥夺、环路
死锁的处理
答案:鸵鸟策略、预防策略、避免策略、检测与解除死锁
3.堆和栈的区别
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
十一、杂项
1.动态绑定
—在运行时确定要调用的方法
动态绑定将调用方法的确定也推迟到运行时。在编译时,方法的调用并不和代码绑定在一起,只有在消实发送出来之后,才确定被调用的代码。通过动态类型和动态绑定技术,您的代码每次执行都可以得到不同的结果。运行时因子负责确定消息的接收者和被调用的方法。运行时的消息分发机制为动态绑定提供支持。当您向一个动态类型确定了的对象发送消息时,运行环境系统会通过接收者的isa指针定位对象的类,并以此为起点确定被调用的方法,方法和消息是动态绑定的。而且,您不必在Objective-C代码中做任何工作,就可以自动获取动态绑定的好处。您在每次发送消息时,
特别是当消息的接收者是动态类型已经确定的对象时,动态绑定就会例行而透明地发生。
2.目标-动作机制
目标是动作消息的接收者。一个控件,或者更为常见的是它的单元,以插座变量(参见”插座变量”部分)的形式保有其动作消息的目标。
动作是控件发送给目标的消息,或者从目标的角度看,它是目标为了响应动作而实现的方法。
程序需要某些机制来进行事件和指令的翻译。这个机制就是目标-动作机制。
3.什么是键-值,键路径是什么
模型的性质是通过一个简单的键(通常是个字符串)来指定的。视图和控制器通过键来查找相应的属性值。在一个给定的实体中,同一个属性的所有值具有相同的数据类型。键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制。
键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对象性质序列。第一个键的性质是由先前的性质决定的,接下来每个键的值也是相对于其前面的性质。键路径使您可以以独立于模型实现的方式指定相关对象的性质。通过键路径,您可以指定对象图中的一个任意深度的路径,使其指向相关对象的特定属性。
4.什么时候用delegate,什么时候用Notification?
答:delegate针对one-to-one关系,并且reciever可以返回值给sender,
notification可以针对one-to-one/many/none,reciever无法返回值给sender.
所以,delegate用于sender希望接受到reciever的某个功能反馈值,notification用于通知多个object某个事件。
5.什么是KVC和KVO?
答:KVC(Key-Value-Coding)内部的实现:一个对象在调用setValue的时候,(1)首先根据方法名找到运行方法的时候所需要的环境参数。(2)他会从自己isa指针结合环境参数,找到具体的方法实现的接口。(3)再直接查找得来的具体的方法实现。
KVO(Key-Value-Observing):当观察者为一个对象的属性进行了注册,被观察对象的isa指针被修改的时候,isa指针就会指向一个中间类,而不是真实的类。所以isa指针其实不需要指向实例对象真实的类。所以我们的程序最好不要依赖于isa指针。在调用类的方法的时候,最好要明确对象实例的类名。
其它回答:
kvc就是key value coding,简而言之就是根据key值改变它的内容,我觉得很类似于dictionary。
kvo就是Key Value Observing,就是相当于你监控特定对象的特定key,当key对应的值发生改变时,会触发它的对应方法。
kvc实现(NSKeyValueCoding Protocol),最简单的方法就是setValue: forKey:以及valueForKey:方法。例如你实现一个Person类,有两个property name,age,生成一个新对象p,可以用[p setValue:@"Solomon" forKey:@"name"];这样的方法进行赋值。
kvo实现(NSKeyValueObserving Protocol),–addObserver:forKeyPath:options:context:对你想要监控的对象,想要监控的属性添加一个observe,当值改变时,会触发– willChangeValueForKey:等方法。
kvc kvo结合使用,可以当作对对象属性值进行监控的一个notification,它对值进行监控。而notification也同时可以对消息响应之类做出监控。
简单说KVC就是一个实现并扩展了setter/getter的方法集。
6.Notification和KVO有什么不同?
KVO就是给属性添加一个监控器,当值发生改变的时候,给你一个接口去处理它。textView用过吧,当textView的值改变时,不也有一个textViewDidChange:的delegate方法么?
它就是一个NSObject的protocol,本来就是一个很简单的东西,不要想的很复杂化。这些东西的提出只是为了更方便,而不是它有什么特殊的含义,正如MVC一样。
notification也就是在程序检测event的loop里面加上一个跟踪事件的判断,如果接收到了这个事件,则做自己想要去做的事情。比如一个uiview的对象接收到一个触摸事件,它在系统检测event的无限循环里接收到了它,然后返回一个uitouch的事件让你去处理,从根本上来说它和notification的性质一样的,虽然可能实现方式不尽相同,但是总归是跳不出这个圈子的。
当然,再往底层的东西,怎么去传递notification之类,苹果封装好了,你也不可能知道。就算你是苹果的程序员,由你来实现,也可能有不同的方式来达到同样的目的。而这个已经超出了sdk编程的范围。