objective-c 常见面试题:
1、OC 语言的基本特点
OC 语言是 C 语言的一个超集,只是在 C 的基础之上加上了面向对象(oop) 的特性;
OC 与 Java 语言相同都是单继承,这一点与 C++语言不同(多重继承);
OC 不支持命名空间机制,取而代之的是在类名之前添加前缀,以此来区分。
2、以下命名正确的是
(1)类 (Person、person、ObjectAndKeys、personAndOther) (2)对象 (objectAndKeys、Person、_person、$dog) (3)实例变量 (_dog、^age、name、Name、)
3、数据类型和表达式
(1) inta=5,b=2,c=2,result,result=a*b++-c; 求result的值和b 的值。
答:result 的值为 8,b 的值为 3。根据运算符的优先级(乘法比加法的优先 级高),首先计算 a*b,因为 b++,“++”在后面。因此,先计算 a*b,其结果为 10,计算完成后 b 的结果++,因此 b 的结果为 3。最后与 c 相减,result 的结果 为8。
(2)result = (b > a)? a++ : ((++c > a-b)? ++c : b++),求 result,a, b,c 的值,假设 a、b、c 的值分别为 1、2、3。(右结合性;运算符号优 先级->结合性->顺序)
答:运算符号相同,因此判断运算符号的结合性。即表达式为:result = (b > a)? a++ :(++c > a-b)? ++c : b++),然后我们判断表达是(b > a)是否为真。 因此,result 的结果为 1,a、b、c 的值分别为 2、2、3。
(3)int a = 5, b = 12, c = 3, result = 0, d = 5, e = 2,result = a -= b /= c += d %= e;求 result,a、b、c、d、e。
答:运算符号相同,因此,判断该运算符号的结合性。赋值运算符号为右结合, 因此,表达式从右开始计算。d %= 2,分解为 d = d % e,d 值为 1;计算 c += d, 同理,c值为4。再计算b /= c,b值为3;再计算a -= b,a值为2。最后将a 值赋值给 result。因此,result 的结果为 2,a、b、c、d、e 的结果分别是 2、3、 4、1、2。(复合赋值运算符效率更高)
(4)如果第三题中 d 为-5,求 result。 答:%(模运算符号)的符号取决与第一个数,因此,result 的值为-1,a、b、
c、d、e 的值分别为-1、6、2、-1、2。
(5)假设 a、b、c 的值分别是 4、5、6。那么 result = a < b < c,求 result 是多少?
答:result 值为 1。 (6)解释 id 类型
答:任意类型对象。程序运行时决定才对象的类型。 (7)解释 nil,发送消息时,程序是否会出现异常。
答:不会,在 OC 语言中可以 nil 发送消息,而程序不会抛出异常,其结果是 什么也不做。
4、流程控制语句
(1)switch 语句每一 case 都需要添加 break 语句吗?
答:switch 语句中的 break 语句不是必须的,此外,default 语句也不是必须
添加的。如果在某一个条件中添加(case 语句之后)break 语句,即当条件满足 时,跳出 switch 语句。
(2)do while 语句和 while 语句的区别,并写出几个死循环。
答:do while 语句至少执行一次循环体,而 while 语句括号中的表达式为真, 才执行循环体。
while(1){ }、for(;)
(3)switch 语句 if 语句区别与联系以及它的优势在哪里
答:均表示条件的判断,switch 语句表达式只能处理是整型、字符型和枚举类 型,而选择流程语句则没有这样的限制。但 switch 语句比选择流程控制语句效 率更高。
(4)int number = 26,k = 1,求 k 的值
do {
k *= number % 10;
number /= 10;
} while(number);
答:do while 语句的特点是,循环体至少执行一次。程序执行到表达式 k*=number%10,已知 number 为 26,又已知算术运算符比赋值运算符好优先 级别高,因此先计算 number%10,其结果为 6;已知 k 为 1,因此,k 的结果为 6。number/=10,number 的值 2。while 语句判断表达式是否为真,此时,number 为 2。继续执行循环体,此时 number、k 的值分别为 2、6,2%10 的结果仍为 2,再与 k 相乘,其 k 的结果为 12。程序执行到循环体第二行 number/10,此 时 number 已为 10,因此,number 的结果为 0。while 表达式内条件为假,循 环就此结束。因此,k 的值为 12。
5、写出以下方法类型、方法名称和返回值类型
(1)-(void)initWithName:(NSString *)name andAge:(int) age;
(2)+(Person *)personName:(NSString)name; (3)-(void)setName:(NSString *)name setAge:(int)age setDelegate:(id)delegate;
(4)-(NSString *)name;
(5)+ (Kingdom *)shareKingdom; (6)+ (Kingdom *)defaultKingdom;
6、创建一个这样的 Person 类,用类目的形式给 Person 添加一组方法(方法任意)、并且若干私有方法以及在 Person 类中添加一个协议(手写代码)
.h文件
#import @”Person.h”
@protocol PersonDelegate
- (void)thisRequiredMethod;
@optional
- (void)thisOptionalMethod; @end
@interface Person : NSObject { @private
NSString *_name;
NSInteger _age; }
- (void)test;
- (void)test1:(int)arg1;
- (void)test1:(int)arg1 test2:(int)arg2; @end
@interface Person (Create)
- (id)initWithName:(NSString *)aName;
- (id)initWithName:(NSString *)aName age:(int)age; + (id)personBorn;
@end
.m文件
@interface Person () - (void)private1;
- (void)private2; @end @implementation
- (void)test {}
- (void)test1:(int)arg1 {}
- (void)test1:(int)arg1 test2:(int)arg2 {} - (void)private1 {}
- (void)private2 {}
- (id)initWithName:(NSString *)aName { self = [super init];
if (self) {
}
return self;
}
- (id)initWithName:(NSString *)aName age:(int)age {
...... }
+ (id)personBorn {
Person *person = [[Person alloc] init]; return [person autorelease];
} @end
7、协议的基本概念和协议中方法默认为什么类型
答:OC 中的协议是一个方法列表,且多少有点相关。它的特点是可以被任何 类使用(实现),但它并不是类(这里我们需要注意),自身不会实现这样方法,
而是又其他人来实现。协议经常用来实现委托对象(委托设计模式)。 如果一个类采用了一个协议,那么它必须实现协议中必须需要实现的方法,在 协议中的方法默认是必须实现(@required),添加关键字@optional,表明一旦
采用该协议,这些“可选”的方法是可以选择不实现的。
8、#include 与#import 的区别、#import 与@class 的区别
答:#include 和#import 其效果相同,都是查询类中定义的行为(方法)。只 是后者不会引起交叉编译,确保头文件只会被导入一次。@class 的表明,只定 义了类的名称,而具体类的行为是不知道的,一般用于.h 文件,因此,@class 比#import 编译效率更高。此外@class 和#import 的主要区别在于解决引用死锁 的问题。
9、@public、@protected、@private 它们的含义与作用
( 1) @public: 答:对象的实例变量的作用域在任意地方都可以被访问
( 2) @protected: 答:对象的实例变量作用域在本类和子类都可以被访问
( 3) @private: 答:实例变量的作用域只能在本类(自身)中访问
(4)通过指针运算符(->)能够访问到 private 方法吗?OC 语言中还提供 了哪些方式能直接和间接的访问对象的实例变量?
答:不可以,可以通过合成存取器访问实例变量,也可自己定义 setter 和 getter 方法访问实例变量,KVC(key value coding)——键值编码,间接的方式访问实 例变量。
10、简述类目优点和缺点,如果覆盖本类或者父类的方 法,会出现什么问题?
答:(1)优点:不需要通过增加子类而增加现有类的行为(方法),且类目中 的方法与原始类方法基本没有区别;通过类目可以将庞大一个类的方法进行划 分,从而便于代码的日后的维护、更新以及提高代码的阅读性。
(2)缺点:无法向类目添加实例变量,如果需要添加实例变量,只能通过 定义子类的方式;类目中的方法与原始类以及父类方法相比具有更高级别的优先 级,如果覆盖父类的方法,可能导致 super 消息的断裂。因此,最好不要覆盖原 始类中的方法。
11、简述内存管理基本原则
答:(1)如果使用 alloc、copy(mutableCopy)或者 retian 一个对象时,你就 有义务,向它发送一条 release 或者 autorelease 消息。其他方法创建的对象,不 需要由你来管理内存。
(2)向一个对象发送一条 autorelease 消息,这个对象并不会立即销毁, 而是将这个对象放入了自动释放池,待池子释放时,它会向池中每一个对象发送 一条 release 消息,以此来释放对象。
(3)向一个对象发送 release 消息,并不意味着这个对象被销毁了,而是 当这个对象的引用计数为 0 时,系统才会调用 dealloc 方法,释放该对象和对象 本身它所拥有的实例。
12、在 objective c 中是否支持垃圾回收机制? 答:OC是支持垃圾回收机制的(Garbage collection简称GC),但是apple的
移动终端中,是不支持 GC 的,Mac 桌面系统开发中是支持的。 13、什么是 ARC 技术?与 GC 是否相同?
答:ARC 是 Automatic Reference Counting 的简称,我们称之为自动引用计数,
是在IOS 5之后推出的新技术,它与GC的机制是不同的。我们在编写代码时, 不需要向对象发送 release 或者 autorelease 方法,也不可以调用 delloc 方法, 编译器会在合适的位置自动给用户生成 release 消息(autorelease),ARC 的特 点是自动引用技术简化了内存管理的难度。
14、什么是 retain count
答:每一个对象都默认有一个 retainCount 的属性,数值的多少表示现在有几
个实例正在引用它。当它为 0 时,系统会自动调用 dealloc 方法,将内存回收。 15、写出@property (nonatomic ,retain) Person *person;
@synthesize person 具体实现,并指出其中含义。
非原子性事物
- (void)setPerson:(Person *)person {
if (_person != person){ [_person release]; _person = [person retain]; }
}
- (Person *)person {
return _person; }
原子性事物
- (void)setPerson:(Person *)person {
@synchronized(self) {
if (_person != person){
[_person release]; _person = [person retain]; }
} }
- (Person *)person {
@synchronized(self) {
return _person; }
}
16、深、浅复制的基本概念以及他们的区别,可以用图 来加以说明。
17、堆和栈的区别
答:(1)栈区(stack)由编译器自动分配释放 ,存放方法(函数)的参数值, 局部变量的值等。先进后出。
(2)堆区(heap)一般由程序员分配释放, 若程序员不释放,程序结束 时由OS回收 。
(3)全局区(静态区)(static),全局变量和静态变量的存储是放在一块 的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始 化的静态变量在相邻的另一块区域。程序结束后有系统释放。
(4)文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。 (5)程序代码区—存放函数体的二进制代码。
18、用户自定义了一个对象,如何实现拷贝(可变和不 可变拷贝)
答:必须实现 copying 和 mutableCopying 协议,表示返回一个不可变和可变的 对象。否则,程序将会出现异常。
- (id)copyWithZone:(NSZone *)zone
{
Person *person = [[self Class] allocWithZone:zone]; person ->age = self.age;
person ->name = self.name;
return person;
}
- (id)mutableCopyWithZone(NSZone *)zone;
19、以下代码有问题吗?如果有,会出现什么问题
- (void) setName:(NSString *)name {
self.name = name; }
答:引起重复调用(自己调用自己)。
20、定义属性时,什么时候用 assign、retain、copy 以及 它们的之间的区别
答:(1)assign:普通赋值,一般常用于基本数据类型,常见委托设计模式, 以此来防止循环引用。(我们称之为弱引用,weak)
(2)retain:保留计数,获得到了对象的所有权。引用计数在原有基础上 加1。
(3)copy:一般认为,是在内存中重新开辟了一个新的内存空间,用来
存储新的对象,和原来的对象是两个不同的地址,引用计数分别 1。但是当 copy 对象为不可变对象时,那么 copy 的作用相当于 retain。因为,这样可以节约内 存空间。
21、解释以下关键字,static、self、super 用实例说明
答 static: 静态全局变量,持久性作用、存储区域在静态区域,它的生命周期 和应用进行绑定。程序结束时,由系统自动回收。
self:当前消息的接收者。 super:向父类发送消息。
22、解释 self = [super init]方法
答:容错处理,当父类初始化失败,会返回一个 nil,表示初始化失败。由于 继承的关系,子类是需要拥有父类的实例和行为的,因此,我们必须先初始化父 类,然后再初始化子类。
23、当我们释放对象时,为什么需要调用[super dealloc] 方法?
答:(1)因为,子类是继承自父类,那么子类中有一些实例变量(对象),是 继承子父类的,因此,我们需要调用父类方法,将父类所拥有的实例进行释放。
(2)先将子类所拥有的实例进行释放,然后再释放父类的。 24、objective-c 有私方法么?私有变量呢?
答:是有的,我们称之为延展。私有变量也是有的(@private)。 25、以下每行代码执行后,person 对象的 retain count 分
别是多少?
Person *person = [[Person alloc] init]; // 1
[person retain]; [person release]; [person release];
// 2 // 1
// 0
26、在某个方法中 self.name = _name 、name = _name 他 们有区别吗,为什么?
答:是有区别的,前者是存在内存管理的,它会对_name 对象进行保留或者拷 贝操作,而后者是普通赋值。
27、假设我们写了一个类的合成存取器,@property (nonatomic, copy) NSString *name;@synthesize name;
(1)NSString *aName = [NSString stringWithFormat:@”a”];
person.name = aName 此时 name 的引用计数是几,为什么,这么做
有什么好处?
答:它的引用技术是 2,相当于 retain 操作。
(2)NSMutableString*aName = [NSMutableString stringWithFormat:@”a”]; 同上
答:它的引用技术是 1,真正意义上的拷贝。 (3)返回这一个字符串的类型,是可变的吗?如果不是,为什么,我们 又如何做?
答:不可变,因为合成存取器中用的 copy。如果,我们需要返回一个可变 的字符串时,那么必须自己实现 setter 和 getter 方法。
28、自动释放池是什么,如何工作
答:自动释放池是 NSAutorelease 类的一个实例,当向一个对象发送 autorelease 消息时,该对象会自动入池,待池销毁时,将会向池中所有对象发 送一条 release 消息,释放对象。[pool release]; [pool drain]表示的是池本身
不会销毁,而是池子中的临时对象都被发送 release,从而将对象销毁。 29、为什么 delegate(代理)属性都是 assign 而不是 retain
的? 答:防止循环引用,以至对象无法得到正确的释放。
30、iOS 开发中数据持久性,有哪几种。 答:文件写入、对象归档、sqlite3 数据库、coredata
31、对象归档的基本概念,以及它的特点是什么?
答:归档为对象的数据持久化提供了一种解决方法,它特点是给归档的对象进 行加密,增强了数据的安全性。此外,自定义类的对象归档必须实现 NSCoding 协议。
32、什么是谓词?
答:cocoa 中提供了一个 NSPredicate 的类,该类主要用于指定过滤器的条件,
每一个对象通过谓词进行筛选,判断条件是否匹配。
33、什么是 KVC 和 KVO?以及它们之间的关系是什么
答:(1)KVC(键值编码)是一种间接访问对象实例变量的机制,该机制可以 不通过存取方法就可以访问对象的实例变量。非对象类型的变量将被自动封装或 者解封成对象。此外,使用 KVC 能够简化代码。我们需要注意 KVC 有两个较为 明显的缺点,一旦使用 KVC 你的编译器无法检查出错误,即不会对设置的键、 键路径进行错误检查,且执行效率要低于(虽然效率已经很高,你已经感觉不到) 合成存取器方法和自定的 setter 和 getter 方法。因为使用 KVC 键值编码,它必 须先解析字符串,然后在设置或者访问对象的实例变量。
(2)KVO(键值观察)是一种能使得对象获取到其他对象属性变化的通知
机制。
(3)实现 KVO 键值观察模式,被观察的对象必须使用 KVC 键值编码来修
改它的实例变量,这样才能被观察者观察到。因此,KVC 是 KVO 的基础或者说 KVO 的实现是建立在 KVC 的基础之上的。
34、在 objective c 中如何实现 KVO
答:(1)注册观察者(这里我们需要注意,观察者和被观察者不会被保留也不 会被释放)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
(2)接收变更通知
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
(3)移除对象的观察者身份
- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
35、当我们释放我们的对象时,为什么需要调用[super dealloc]方法,它的位置又是如何的呢?
答:因为子类的某些实例是继承自父类的,因此需要调用[super dealloc]方法, 来释放父类拥有的实例,其实也就是子类本身的。一般来说我们优先释放子类拥 有的实例,最后释放父类所拥有的实例。
36、以下代码会出项问题吗?如果有,我们又该如何修 改?
@property (nonatomic, copy) NSMutableString *name; @synthesize name = _name;
self.name = [NSMutableString stringWithFormat:@"..xyz"]; [self.name insertString:@"abc" atIndex:0];
答:不可变字符串不可以被修改,可以通过自定义 set 方法,将字符串的拷贝 改为可变的拷贝。
37、当我们将一个对象加入自动释放池时,该对象何时 被销毁
答:我们在 application kit 应用程序中,自动释放池中的临时对象被销毁的时 间时,一个事件循环结束后。注意自动释放池没有被释放,而是被排空了,向池 发送了 drain 消息。
38、当我们调用一个静态方法时,需要对对象进行 release 吗?
答:不需要,静态方法(类方法)创建一个对象时,对象已被放入自动释放池。 在池被释放时,很有可能被销毁。
39、什么叫键路径?
答:在一个给定的实体中,同一个属性的所有值具有相同的数据类型。 键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制。 键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对 象性质序列。第一个键的性质是由先前的性质决定的,接下来每个键的值也是相
对于其前面的性质。键路径使您可以以独立于模型实现的方式指定相关对象的性 质。通过键路径,您可以指定对象图中的一个任意深度的路径,使其指向相关对 象的特定属性。
40、以下代码存在内存泄露吗?如果有,如何去修改
self.object = [NSObject alloc] init]; self.object = [NSObject object] retain]; self.object = [NSObject object];
- (void)dealloc {
self.object = nil;
[super dealloc]; }
41、循环引用是什么,如何解决这样的问题
答:对象 a 创建并引用到了对象 b;对象 b 创建并引用到了对象 c;对象 c 创 建并引用到了对象 b。这时候 b 和 c 的引用计数分别是 2 和 1。
当 a 不再使用 b,调用 release 释放对 b 的所有权,因为 c 还引用了 b,所以 b 的引用计数为 1,b 不会被释放。
b 不释放,c 的引用计数就是 1,c 也不会被释放。从此,b 和 c 永远留在内存 中。
这种情况,必须打断循环引用,通过其他规则来维护引用关系。我们常见的 delegate 往往是 assign 方式的属性而不是 retain 方式 的属性, 赋值不会增加引用计数,就是为了防止 delegation 两端产生不必要的循环引用。
42、isMemberOfClass 和 isKindOfClass 联系与区别
答:两者都能检测一个对象是否是某个类的成员, 两者之间的区别是:
isKindOfClass 不但可以用来确定一个对象是否是一个类的成员,也可以用来确 定一个对象是否是派生自该类的类的成员 ,而 isMemberOfClass 做不到后一点。
如 ClassA 派 生 自 NSObject 类 , ClassA *a = [ClassA alloc] init]; [a isKindOfClass:[NSObject class]] 可以检查出 a 是否是 NSObject 派生类 的成员,但 isMemberOfClass 做不到。