iOS面试题

1.属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?
1). readwrite 是可读可写特性;需要生成getter方法和setter方法时;
2). readonly 是只读特性 只会生成getter方法 不会生成setter方法 ;不希望属性在类外改变时;
3). assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时,一般用于基础数据类型;
4). retain 表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1,也就是说release旧值,retain新值;一般用于OC对象;
5). copy 表示赋值特性,setter方法将传入对象复制一份;需要完全一份新的变量时,也就是release旧值,copy新值;一般用于NSString\NSMutableString\block;
6).nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用nonatomic。
2.#import 跟#include、@class有什么区别?#import<> 跟 #import”"又什么区别?
1).#import和#include都能完整地包含某个文件的内容,#import能防止同一个文件被包含多次。
2). @class和#import
作用上的区别:
#import会包含引用类的所有信息(内容), 包括引用类的变量和方法;
@class仅仅是告诉编译器有这么一个类, 具体这个类里有什么信息, 完全不知;
效率上的区别:
如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍 , 编译效率非常低;
相对来讲,使用@class方式就不会出现这种问题了
3). #import <> 用来包含系统自带的文件,#import “”用来包含自定义的文件。
3.OC有多继承吗?没有的话用什么代替?
OC中没有多继承,可以用委托代理Protocol来实现。
4.Objective-C如何对内存管理的?内存管理的原则是?
Objective-C的内存管理主要有三种方式ARC(自动引用计数)、MRC(手动内存计数)、autorelease(自动释放池)。
每个对象都有一个引用计数器,每个新对象的计数器是1,当对象的计数器减为0时,就会被销毁。
内存管理原则(配对原则):只要出现了 new/alloc/retain,就一定配对出现一个release/autorelease。
5、Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?
线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:;
6、浅复制和深复制的区别?
浅复制:只复制指向对象的指针,而不复制引用对象本身。
深复制:复制引用对象本身。
意思就是说我有个A对象,复制一份后得到A_copy对象后,对于浅复制来说,A和A_copy指向的是同一个内存资源,复制的只不过是是一个指针,对象本身资源还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改,这其实违背了我们复制拷贝的一个思想。深复制就好理解了,内存中存在了两份独立对象本身。
用通俗的话讲就是:浅复制好比你和你的影子,你完蛋,你的影子也完蛋;深复制好比你和你的克隆人,你完蛋,你的克隆人还活着。
7、分类的作用?分类和继承的区别?
分类可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改,并且如果分类和原来类中的方法产生名称冲突,则分类将覆盖原来的方法,因为分类具有更高的优先级。
继承可以增加,修改或者删除方法,并且可以增加属性;但是分类只能添加方法,不能删除修改,也不能增加属性。
8、frame和bounds有什么不同?
frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父亲的坐标系统)
bounds指的是:该view在本身坐标系统中 的位置和大小。(参照点是本身坐标系统)
9、HTTP协议中,POST和GET的区别是什么?
1).GET 方法:

  • GET 方法提交数据不安全,数据置于请求行,客户端地址栏可见;
  • GET 方法提交的数据大小有限
  • GET 方法不可以设置书签
    2).POST 方法:
  • POST 方法提交数据安全,数据置于消息主体内,客户端不可见
  • POST 方法提交的数据大小没有限制
  • POST 方法可以设置书签
    10、视图控制器的生命周期方法调用顺序?
    iOS面试题_第1张图片

    11、block和代理的区别,哪个更好?
    代理回调更面向过程,block更面向结果。如果需要在执行的不同步骤时被通知,你就要使用代理。如果只需要请求的消息或者失败的详情,应该使用block。block更适合与状态无关的操作,比如被告知某些结果,block之间是不会相互影响的。但是代理更像一个生产流水线,每个回调方法是生产线上的一个处理步骤,一个回调的变动可能会引起另一个回调的变动。要是一个对象有超过一个的不同事件,应该使用代理。一个对象只有一个代理,要是某个对象是个单例对象,就不能使用代理。要是一个对象调用方法需要返回一些额外的信息,就可能需要使用代理。
    12、自动释放池常见面试代码
for (int i = 0; i < 10; ++i) 
{    
      NSString *str = @"Hello World";    
      str = [str stringByAppendingFormat:@" - %d", i];    
      str = [str uppercaseString];    NSLog(@"%@", str);
}

问:以上代码存在什么样的问题?如果循环的次数非常大时,应该如何修改?

  • 解决办法1:如果i比较大,可以用@autoreleasepool {}解决,放在for循环外,循环结束后,销毁创建的对象,解决占据栈区内存的问题
  • 解决方法2:如果i玩命大,一次循环都会造成自动释放池被填满,自动释放池放在for循环内,每次循环都将上一次创建的对象release。
    13、iOS怎么做数据的持久化?
    1)、plist属性列表
  • 适用对象:仅仅是Foundation框架中自带的一些类,比如NSString、、NSArray、NSDictionary、NSSet、NSNumber、NSData
  • 调用对象的writeToFile...方法就可以写入文件
  • 调用对象的...WithContentsOfFile方法就可以从文件中读取对象内容
    2)、偏好设置(NSUserDefault)
  • 本质还是plist属性列表的方式进行存储
  • 存取非常简单(不关心文件夹和文件名)
  • 缺点:只能存储到一个文件中(不能存放大批量数据)
    3)、NSCoding
  • 能将任何遵守了NSCoding协议的对象塞进文件中
  • - (void)encodeWithCoder:(NSCoder *)encoder 将对象归档的时候会调用(将对象写入文件之前会调用)这个方法, 在这个方法说清楚:a、那些属性需要存储 b、怎样存储这些属性;
  • - (id)initWithCoder:(NSCoder *)decoder当从文件中解析对象的时候调用这个方法,在这个方法说清楚:a、那些属性需要解析(读取) b、怎样解析(读取)这些属性;
  • 如果父类也有属性需要归档或者读档,必须调用super的encodeWithCoder:和initWithCoder:方法。
    14、APNS的推送机制
    首先我们看一下苹果官方给出的对iOS推送机制的解释。如下图:
    iOS面试题_第2张图片
    Provider就是我们自己程序的后台服务器,APNS是Apple Push Notification Service的缩写,也就是苹果的推送服务器。
    上图可以分为三个阶段:
    第一阶段:应用程序的服务器端把要发送的消息、目的iPhone的标识打包,发给APNS。
    第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发送到iPhone。
    第三阶段:iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知。
    15、控制器View的加载过程?
    当程序访问了控制器的View属性时会先判断控制器的View是否存在,如果存在就直接返回已经存在的View;如果不存在,就会先调用loadView这个方法;如果控制器的loadView方法实现了,就会按照loadView方法加载自定义的View;如果控制器的loadView方法没有实现就会判断storyboard是否存在;如果storyboard存在就会按照storyboard加载控制器的View;如果storyboard不存在,就会创建一个空视图返回。
    16、UITableView的数据源方法和代理方法?
    数据源常见方法:
1.有多少组
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
2.第section组头部控件有多高
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
3.第section组有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
4.indexPath这行的cell有多高
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
5.indexPath这行的cell长什么样子
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
 6.第section组头部显示什么控件
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

代理方法:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView   //右侧索引
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath   //行点击事件
NSIndexPath *path = [self.tableView indexPathForSelectedRow];     //获得被选中的indexPath可以得到section,row
[self.tableView reloadRowsAtIndexPaths:[self.tableView indexPathsForSelectedRows] withRowAnimation:UITableViewRowAnimationNone];    //刷新table指定行的数据       
[self.tableView reloadData];       //刷新table所有行的数据

17、UITableViewCell表格优化?
UITableViewCell对象的重用原理:当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用。当UITableView要求dataSource返回UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的UITableViewCell,dataSource会用新的数据配置这个UITableViewCell,然后返回给UITableView,重新显示到窗口中,从而避免创建新对象。
还有一个非常重要的问题:有时候需要自定义UITableViewCell(用一个子类继承UITableViewCell),而且每一行用的不一定是同一种UITableViewCell(如短信聊天布局),所以一个UITableView可能拥有不同类型的UITableViewCell,对象池中也会有很多不同类型的UITableViewCell,时可能会得到错误类型的UITableViewCell那么UITableView在重用UITableViewCell。解决方案:UITableViewCell有个NSString *reuseIdentifier属性,可以在初始化UITableViewCell的时候传入一个特定的字符串标识来设置reuseIdentifier(一般用UITableViewCell的类名)。当UITableView要求dataSource返回UITableViewCell时,先通过一个字符串标识到对象池中查找对应类型的UITableViewCell对象,如果有,就重用,如果没有,就传入这个字符串标识来初始化一个UITableViewCell对象。
18、在一个对象的方法里面:self.name = @"object";name =@"object";有什么不同吗?
self.name = @"object";会调用对象的setName()方法,name =@"object";会直接把@"object"赋值给当前对象的name 属性。
19、为什么很多内置类如UITableViewController的delegate属性都是assign而不是retain的?
会引起循环引用
所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:
• 对象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两端产生不必要的循环引用。如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a, 如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用delegate模式时,也要注意这点。
20、什么是Notification?
观察者模式,controller向defaultNotificationCenter添加自己的notification,其他类注册这个notification就可以收到通知,这些类可以在收到通知时做自己的操作(多观察者默认随机顺序发通知给观察者们,而且每个观察者都要等当前的某个观察者的操作做完才能轮到他来操作,可以用NotificationQueue的方式安排观察者的反应顺序,也可以在添加观察者中设定反映时间,取消观察需要在viewDidUnload 跟dealloc中都要注销)。
21、单例模式的作用?程序中有哪些常见的单例?单例的实现步骤?

  • 单例模式的作用是解决“应用中只有一个实例”的一类问题。
  • dispatch_once函数是由GCD提供的,它的作用是在整个应用程序生命周期中只执行一次代码块。dispatch_once_t是由GCD提供的结构体,使用时需要将GCD地址传给dispatch_once函数。dispatch_once函数能够记录该代码是否被调用过。
  • dispatch_once函数不仅意味着代码仅会被执行一次,而且还意味着此运行还是线程同步的。也就是说,当我们使用dispatch_once函数时,就不需要再使用诸如@synchronized之类的语句。
  • 应用案例:UIApplication、NSUserDefaults、NSNotificationCenter、NSFileManager、NSBundle
  • UIApplication:
    UIApplication类的实例提供了应用程序的集中控制点来保持应用的状态。UIApplication实例总是分配给应用程序委托对象(UIApplicationDelegate),通过应用程序委托对象来响应低内存、应用启动、后台运行和应用终止等事件。
  • NSUserDefaults:
    单例类NSUserDefaults可以很方便的读取应用设置项目。
  • NSNotificationCenter:
    单例类NSNotificationCenter提供信息广播通知,它采用观察者模式的通知机制。
  • NSFileManager:
    NSFileManager提供了访问文件系统的通用操作,可以定位、创建、复制文件和文件夹。
  • NSBundle:
    NSBundle提供了动态加载(或卸载)可执行代码、定位资源文件以及资源文件本地化、访问系统文件等功能。
  • 要实现一个Singleton Class, 至少需要做以下四个步骤:
    1). 为Singleton Object实现一个静态实例, 初始化, 然后设置成nil.
    2). 实现一个实例构造方法(通常命名为 sharedInstance 或者 sharedManager)检查上面声名的静态实例是否为nil, 如果是则新建并返回一个本类实例.
    3). 重写 allocWithZone: 方法来保证当其他人直接使用 alloc 和 init 试图获得一个新实例的时候不会产生一个新的实例.
    4). 适当的实现 copyWithZone:, release, retain, retainCount 和 autorelease.
    22、block使用时的注意点?
    Block可以使用在定义之前声明的局部变量;
int i = 10;
void(^myBlock)() = ^{
     NSLog(@"%d", i);
};
i = 100;
myBlock();

注意:

  • 在定义Block时,会在Block中建立当前局部变量内容的副本(拷贝)
  • 后续再对该变量的数值进行修改,不会影响Block中的数值
  • 如果需要在block中保持局部变量的数值变化,需要使用__block关键字
  • 使用__block关键字后,同样可以在Block中修改该变量的数值
  • 不能直接用点语法调用self的方法,会造成循环引用,要用中括号调用。
    23、@private、@protected、@public、@package类型的成员变量的作用域?
  • @private:只能在当前类的对象方法中访问;
  • @protected:可以在当前类以及子类的实现中直接访问,默认类型;
  • @public:任何地方都可以直接访问对象的成员变量;
  • @package:同一个“体系内”(框架)可以访问;
    24、这个写法会出什么问题:@property (copy) NSMutableArray *array;?
    @property 的setter方法设置成copy以后,array这个指针指向的是一个不可变数组,那么当使用点语法为给array赋值时,就会发生“unrecognized selector sent to instance”错误,程序就会崩溃。

**这里附上另一个更详细的面试题地址:https://github.com/findM/iOSInterviewQuestions

你可能感兴趣的:(iOS面试题)