iOS基础-高级进阶面试题

本文深入探讨了iOS开发中的Objective-C语言特性,包括OC的基本特点、命名规范、数据类型与表达式、流程控制语句、方法类型与返回值、内存管理原则、协议与代理、深浅复制、堆栈区别、多线程、网络数据传输格式、内存优化工具、Cocoa Touch框架、以及蓝牙连接等核心知识点。同时,文章还涵盖了面试中常见的iOS开发题目,如内存管理、协议实现、流程控制、数据类型运算等,旨在帮助开发者提升面试技能和理解iOS开发的深层次概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)agesetDelegate:(id)delegate; 

(4)-(NSString *)name;     (5)+ (Kingdom *)shareKingdom; (6)+(Kingdom *)defaultKingdom; 

6、创建一个这样的 Person 类,用类目的形式给 Person 添加一组方法(方法任意)、并且若干私有方法以及在 Person 类中添加一个协议(手写代码) 

.h文件
#import @”Person.h” 

@protocol PersonDelegate<NSObject> @required

- (void)thisRequiredMethod;

@optional 

- (void)thisOptionalMethod;@end 

@interface Person : NSObject{ @private 

NSString *_name; 

NSInteger _age; } 

-  (void)test;

-  (void)test1:(int)arg1;

-  (void)test1:(int)arg1test2:(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)arg1test2:(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 = [[Personalloc] init]; return [person autorelease]; 

} @end 

7、协议的基本概念和协议中方法默认为什么类型?通知、Block概念、使用环境、三者的区别?

协议:OC 中的协议是一个方法列表。它的特点是可以被任何类使用(实现),但它并不是类(这里我们需要注意),自身不会实现这样方法,而是由其他人来实现。

协议经常用来实现委托对象(委托设计模式)。 如果一个类采用了一个协议,那么它必须实现协议中必须需要实现的方法,在 协议中的方法默认是必须实现(@required),添加关键字@optional,表明一旦采用该协议,这些“可选”的方法是可以选择不实现的。

三者区别:

    代理是一对一的,对用一个协议,一个对象只能设置一个代理,所以单例对象就不能用代理;代理更注重过程的信息的传输,比如发起一个网络请求,可能想要知道此时请求是否已经开始、是否收到了数据、数据是否已经接收完成、数据接收是否失败;代理的方法是分离开的,并不会引用上下文,可以有效的避免循环引用;声明代理时为了防止循环引用应该使用assign或者weak;

    block(闭包)它和代理一样,一般都是一对一之间交互通信,他能够直接存储一个代码块的实现部分,而不需要去定义一个函数;写法简单、不需要写protocol、函数等等;比较注重结果的传输,比如对于一个事件,只想知道成功或者失败,并不知道进行了多少或者额外的一些信息;需要注意防止循环引用(block在多次调用时,需要长期存储,就很容易引起循环引用问题);

    通知它可以一对多,在远距离传值时比较有优势;在通知注册完回调方法之后,需要对注册进行取消操作,也就是移除通知,如果通知回调了已经被回收的类里面的方法,就会带来调用上的问题,莫名的闪退;

8、#include 与#import 的区别、#import 与@class 的区别 

#include c语言中引入一个头文件,但是可能出现交叉编译,

OC里面已经没有这个方式引入头文件了,统一使用#import

#import在OC中引入自己创建的头文件           

#import””是引入自己创建类的头文件             

#import<>是引入系统类的头文件                

#import不会出现交叉编译

@class对一个类进行声明,告诉编译器有这个类,但是类的定义什么的都不知道.

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 = [personretain]; } 
}

-  (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 = [[selfClass] 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: 静态全局变量,持久性作用、存储区域在静态区域,它的生命周期和应用进行绑定。程序结束时,由系统自动回收。

优点: 1、节省内存。静态变量只存储一处,但供所有对象使用。         

2、它的值是可以更新的。

3、可提高时间效率。只要某个对象对静态变量更新一次,所有的对象都能访问更新后的值。

self:当前消息的接收者。

super:向父类发送消息。 

22、解释 self = [super init]方法 

答:容错处理,当父类初始化失败,会返回一个 nil,表示初始化失败。由于继承的关系,子类是需要拥有父类的实例和行为的,因此,我们必须先初始化父类,然后再初始化子类。 

23、当我们释放对象时,为什么需要调用[superdealloc] 方法?               

(1)因为,子类是继承自父类,那么子类中有一些实例变量(对象),是继承子父类的,因此,我们需要调用父类方法,将父类所拥有的实例进行释放。 

(2)先将子类所拥有的实例进行释放,然后再释放父类的。

24、objective-c 有私方法么?私有变量呢? 

是有的,我们称之为延展。私有变量也是有的(@private)。

25、以下每行代码执行后,person 对象的retain count 分别是多少?

Person *person = [[Personalloc] init]; // 1               

[person retain]; [personrelease]; [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)objectchange:(NSDictionary *)change context:(void *)context; 

(3)移除对象的观察者身份
- (void)removeObserver:(NSObject*)observer 

forKeyPath:(NSString*)keyPath; 

35、当我们释放我们的对象时,为什么需要调用[superdealloc]方法,它的位置又是如何的呢? 

答:因为子类的某些实例是继承自父类的,因此需要调用[super dealloc]方法, 来释放父类拥有的实例,其实也就是子类本身的。一般来说我们优先释放子类拥有的实例,最后释放父类所拥有的实例。 

36、以下代码会出项问题吗?如果有,我们又该如何修改? 

@property (nonatomic, copy)NSMutableString *name; @synthesize name = _name; 

self.name = [NSMutableStringstringWithFormat:@"..xyz"]; [self.name insertString:@"abc"atIndex:0]; 

答:不可变字符串不可以被修改,可以通过自定义 set 方法,将字符串的拷贝改为可变的拷贝。 

37、当我们将一个对象加入自动释放池时,该对象何时被销毁? 

答:我们在 application kit 应用程序中,自动释放池中的临时对象被销毁的时间时,一个事件循环结束后。注意自动释放池没有被释放,而是被排空了,向池 发送了 drain 消息。 

38、当我们调用一个静态方法时,需要对对象进行release 吗? 

答:不需要,静态方法(类方法)创建一个对象时,对象已被放入自动释放池。在池被释放时,很有可能被销毁。 

39、什么叫键路径? 

答:在一个给定的实体中,同一个属性的所有值具有相同的数据类型。 键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制。键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对 象性质序列。第一个键的性质是由先前的性质决定的,接下来每个键的值也是对于其前面的性质。键路径使您可以以独立于模型实现的方式指定相关对象的性 质。通过键路径,您可以指定对象图中的一个任意深度的路径,使其指向相关对 象的特定属性。 

40、以下代码存在内存泄露吗?如果有,如何去修改 

self.object = [NSObjectalloc] init]; self.object = [NSObject object] retain]; self.object = [NSObjectobject]; 

- (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 做不到。

43.简述OC中内存管理机制

   1.1 OC的内存管理机制是自动引用计数,内存管理的原则是谁开辟谁释放,有retain的地方就要有release

   1.2 内存管理分为ARC和MRC,在MRC下我们需要手动管理内存,需要使用到retain/copy/release/autorelease等方法实现内存管理。ARC下则一般不需要我们手动管理,系统会在适当的位置加上内存管理关键字。

   1.3 retain是引用计数+1, 在内存管理中, 要记得内存管理原则: 谁开辟谁释放, 有retain就要有release.  release是引用计数-1. alloc匹配的是dealloc, alloc是开辟内存空间, dealloc是销毁所开辟的内存, 有开辟就要有销毁.

44.readwrite,readonly,assign,retain,copy,nonatomic 、atomic、strong、weak属性的作用?

readWrite读写特性,可读可写.

readonly只读, 只有getter, 没有sette

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值