iOS面试题

作者: 曾令伟 | 来源:发表于2016-06-15 15:41 被阅读289次

    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、视图控制器的生命周期方法调用顺序?

    11、block和代理的区别,哪个更好?

    代理回调更面向过程,block更面向结果。如果需要在执行的不同步骤时被通知,你就要使用代理。如果只需要请求的消息或者失败的详情,应该使用block。block更适合与状态无关的操作,比如被告知某些结果,block之间是不会相互影响的。但是代理更像一个生产流水线,每个回调方法是生产线上的一个处理步骤,一个回调的变动可能会引起另一个回调的变动。要是一个对象有超过一个的不同事件,应该使用代理。一个对象只有一个代理,要是某个对象是个单例对象,就不能使用代理。要是一个对象调用方法需要返回一些额外的信息,就可能需要使用代理。

    12、自动释放池常见面试代码

    for(inti =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推送机制的解释。如下图:

    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*)tableView2.第section组头部控件有多高- (CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section3.第section组有多少行- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section4.indexPath这行的cell有多高- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath5.indexPath这行的cell长什么样子- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath6.第section组头部显示什么控件- (UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section

    代理方法:

    - (NSArray*)sectionIndexTitlesForTableView:(UITableView*)tableView//右侧索引- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath//行点击事件NSIndexPath*path = [self.tableViewindexPathForSelectedRow];//获得被选中的indexPath可以得到section,row[self.tableViewreloadRowsAtIndexPaths:[self.tableViewindexPathsForSelectedRows] withRowAnimation:UITableViewRowAnimationNone];//刷新table指定行的数据[self.tableViewreloadData];//刷新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可以使用在定义之前声明的局部变量;

    inti =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”错误,程序就会崩溃。

    3.写一个setter方法用于完成@property (nonatomic,retain)NSString *name,写一个setter方法用于完成@property(nonatomic,copy)NSString *name.

    @property (nonatomic, retain) NSString *name;

    - (void)setName:(NSString *)name

    {

    if (_name != name) {

    [_name release];

    _name = [name retain];

    }

    }

    @property(nonatomic, copy) NSString *name;

    - (void)setName:(NSString *)name

    {

    if (_name != name) {

    [_name release];

    _name = [name copy];

    }

    }

    4.对于语句NSString *obj = [[NSData alloc] init]; ,编译时和运行时obj分别是什么类型

    此题主要考察对运行时机制的理解,编译时是NSString类型 ,运行时是NSData类型.

    NSString *obj 对obj的类型没有任何的作用,只是告诉编辑器你要把obj当作NSString来使用,编辑器会给obj对象智能提醒和检测NSString的api,帮助你编写代码和语法检查,仅此而已。

    5.常见的object-c的数据类型有那些, 和C的基本数据类型有什么区别?

    1.常用OC类型:NSString、NSArray、NSDictionary、NSData、NSNumber等

    2.OC对象需要手动管理内存,C的基本数据类型不需要管理内存

    6.id 声明的变量有什么特性?

    id声明的变量能指向任何OC对象。

    参照第4题,id声明的变量,相当于告诉编辑器,

    此变量的类型暂无具体类型,就是id类型,具体什么类型运行时来决定。

    7.Objective-C如何对内存管理的,说说你的看法和解决方法?

    内存管理机制是每个iOS开发者需要了解的。

    1.每个对象都有一个引用计数器,每个新对象的计数器是1,当对象的计数器减为0时,就会被销毁

    2.通过retain可以让对象的计数器+1、release可以让对象的计数器-1

    3.还可以通过autorelease pool管理内存

    4.如果用ARC,编译器会自动生成管理内存的代码

    8.深拷贝和浅拷贝的区别?

    深拷贝:拷贝内容,产生新的对象。

    浅拷贝:拷贝指针,不会产生新的对象

    9.使用分类有什么优点?分类和继承的区别?

    分类可以在不修改原来类模型的基础上拓充方法

    分类只能扩充方法、不能扩充成员变量;继承可以扩充方法和成员变量

    继承会产生新的类

    10.分类和扩展的区别?

    分类是有名称的,类扩展没有名称

    分类只能扩充方法、不能扩充成员变量;类扩展可以扩充方法和成员变量

    类扩展一般就写在.m文件中,用来扩充私有的方法和成员变量(属性)

    11.KVC和KVO是什么?

    KVC是键值编码,可以通过一个字符串的key(属性名)修改对象的属性值

    KVO是键值监听,可以监听一个对象属性值的改变

    12.代理的目的是什么?

    两个对象之间传递数据和消息

    解耦,拆分业务逻辑

    13.在Objective C中什么是mutable和immutable类型

    mutable是可变类型,比如NSMutableArray,可以动态往里面添加元素

    immutable是不可变类型,比如NSArray,固定的存储空间,不能添加元素

    14.什么是单例

    单例:保证程序运行过程中,永远只有一个对象实例

    目的是:全局共享一份资源、节省不必要的内存开销

    常见单例:UIApplication、NSUserDefaults、NSNotificationCenter、NSFileManager、NSBundle等

    15.什么时候使用NSMutableArray,什么时候使用NSArray?

    当数组元素需要动态地添加或者删除时,用NSMutableArray

    当数组元素固定不变时,用NSArray

    16.什么情况下对象引用计数器会增加?

    当做retain或者copy操作的时候,都有可能增加计数器

    17.在Objective C中关键字atomic有什么作用?

    atomic是原子性

    atomic会对set方法的实现进行加锁

    18.Objective C有私有方法和私有变量吗?

    Objective C中没有private,public类似的关键字来控制类的权限,写在.h文件中,就是公共方法

    在.m文件中声明和实现的方法,对编辑器来说就是私有的

    19.堆和栈的区别?

    堆空间的内存是动态分配的,一般存放对象,并且需要手动释放内存

    栈空间的内存由系统自动分配,一般存放局部变量等,不需要手动管理内存

    20.@property中有哪些属性关键字?

    1.readwrite(默认):同时生成get方法和set方法的声明和实现

    2.readonly:只生成get方法的声明和实现

    3.assign(默认):set方法的实现是直接赋值,用于基本数据类型

    4.retain:set方法的实现是release旧值,retain新值,用于OC对象类型

    5.copy:set方法的实现是release旧值,copy新值,用于NSString、block等类型

    6.nonatomic:非原子性,set方法的实现不加锁(比atomic性能高)

    7.atomic(默认):原子性,会在set方法加上读写锁

    8.weak:弱指针类型,一般用于UI控件、id类型

    9、strong (就是retain):强指针类型  一般用于OC对象类型,除 (UI控件、代理类型、字符串对象)

    21.OC有多继承吗?没有的话用什么代替?

    OC是单继承,没有多继承,有时可以使用分类和协议来代替多继承。

    22.关键字const什么含义?

    const int a;

    int const a;

    const int *a;

    int const *a;

    int * const a;

    int const * const a;

    1> 前两个的作用是一样:a 是一个不可修改的常整型数,只读

    2> 第三、四个意味着 a 是一个指向常整型数的指针(整型数是不可修改的,但指针可以)

    3> 第五个的意思:a 是一个指向整型数的常指针(指针指向的整型数是可以修改的,但指针是不可修改的)

    4> 最后一个意味着:a 是一个指向常整型数的常指针(指针指向的整型数是不可修改的,同时指针也是不可修改的)

    23.static的作用?

    1.static修饰的函数是一个内部函数,只能在本文件中调用,其他文件不能调用

    2. static修饰的全局变量是一个内部变量,只能在本文件中使用,其他文件不能使用

    3. static修饰的局部变量只会初始化一次,并且在程序退出时才会回收内存

    24.自动释放池常见面试代码

    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

    25.@private、@protected、@public、@package类型的成员变量的作用域?

    - @private:只能在当前类的对象方法中访问;

    - @protected:可以在当前类以及子类的实现中直接访问,默认类型;

    - @public:任何地方都可以直接访问对象的成员变量;

    - @package:同一个“体系内”(框架)可以访问;

    26.这个写法会出什么问题:@property (copy) NSMutableArray *array;?

    @property 的setter方法设置成copy以后,array这个指针指向的是一个不可变数组,

    那么当使用点语法为给array赋值时,就会发生“unrecognized selector sent to instance”错误,程序就会崩溃。

    27.调用对象的release 方法会销毁对象吗?

    不会,调用对象的release 方法只是将对象的引用计数器-1,当对象的引用计数器为0的时候会调用了对象的dealloc 方法才能进行释放对象的内存。

    28.什么情况使用 weak 关键字,相比 assign 有什么不同?

    1>.什么情况使用weak关键字?

    - 在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性

    - 自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。

    2>.weak与assign的不同?

    - weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。 而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。

    - assigin 可以用非 OC 对象,而 weak 必须用于 OC 对象

    29.XIB与Storyboards的优缺点?

    - XIB:在编译前就提供了可视化界面,可以直接拖控件,也可以直接给控件添加约束,更直观一些,而且类文件中就少了创建控件的代码,确实简化不少,通常每个XIB对应一个类。

    - Storyboard:在编译前提供了可视化界面,可拖控件,可加约束,在开发时比较直观,而且一个storyboard可以有很多的界面,每个界面对应一个类文件,通过storybard,可以直观地看出整个App的结构。

    - XIB:需求变动时,需要修改XIB很大,有时候甚至需要重新添加约束,导致开发周期变长。XIB载入相比纯代码自然要慢一些。对于比较复杂逻辑控制不同状态下显示不同内容时,使用XIB是比较困难的。当多人团队或者多团队开发时,如果XIB文件被发动,极易导致冲突,而且解决冲突相对要困难很多。

    - Storyboard:需求变动时,需要修改storyboard上对应的界面的约束,与XIB一样可能要重新添加约束,或者添加约束会造成大量的冲突,尤其是多团队开发。对于复杂逻辑控制不同显示内容时,比较困难。当多人团队或者多团队开发时,大家会同时修改一个storyboard,导致大量冲突,解决起来相当困难。

    30.@synthesize和@dynamic分别有什么作用?

    - @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;

    - @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。

    - @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

    31.objc中的类方法和实例方法有什么本质区别和联系?

    类方法:

    - 类方法是属于类对象的

    - 类方法只能通过类对象调用

    - 类方法中的self是类对象

    - 类方法可以调用其他的类方法

    - 类方法中不能访问成员变量

    - 类方法中不能直接调用对象方法

    实例方法:

    - 实例方法是属于实例对象的

    - 实例方法只能通过实例对象调用

    - 实例方法中的self是实例对象

    - 实例方法中可以访问成员变量

    - 实例方法中直接调用实例方法

    - 实例方法中也可以调用类方法(通过类名)

    32.Objective-C 中是否支持垃圾回收机制?

    - OC是支持垃圾回收机制的(Garbage collection简称GC),但是apple的移动终端中,是不支持GC的,Mac桌面系统开发中是支持的.

    - 移动终端开发是支持ARC(Automatic Reference Counting的简称),ARC是在IOS5之后推出的新技术,它与GC的机制是不同的。我们在编写代码时, 不需要向对象发送release或者autorelease方法,也不可以调用delloc方法,编译器会在合适的位置自动给用户生成release消息(autorelease),ARC 的特点是自动引用技术简化了内存管理的难度.

    33.在 Objective-C 中如何实现 KVO?

    - 注册观察者(注意:观察者和被观察者不会被保留也不会被释放)

    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

    - 接收变更通知

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

    - 移除对象的观察者身份

    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

    - KVO中谁要监听谁注册,然后对响应进行处理,使得观察者与被观察者完全解耦。KVO只检测类中的属性,并且属性名都是通过NSString来查找,编译器不会检错和补全,全部取决于自己。

    1、KVC的底层实现?

    当一个对象调用setValue方法时,方法内部会做以下操作:

    ①检查是否存在相应key的set方法,如果存在,就调用set方法

    ②如果set方法不存在,就会查找与key相同名称并且带下划线的成员属性,如果有,则直接给成员属性赋值

    ③如果没有找到_key,就会查找相同名称的属性key,如果有就直接赋值

    ④如果还没找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。

    这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。

    2、__block和__weak修饰符的区别?

    1.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。

    2.__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。

    3.__block对象可以在block中被重新赋值,__weak不可以。

    3、block和代理的区别,哪个更好?

    代理回调更面向过程,block更面向结果。

    如果需要在执行的不同步骤时被通知,你就要使用代理。

    如果只需要请求的消息或者失败的详情,应该使用block。

    block更适合与状态无关的操作,比如被告知某些结果,block之间是不会相互影响的。

    但是代理更像一个生产流水线,每个回调方法是生产线上的一个处理步骤,一个回调的变动可能会引起另一个回调的变动。

    要是一个对象有超过一个的不同事件,应该使用代理。

    一个对象只有一个代理,要是某个对象是个单例对象,就不能使用代理。

    要是一个对象调用方法需要返回一些额外的信息,就可能需要使用代理。

    4、Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?

    线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;

    NSThread创建线程的三种方法:

    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"nil"];

    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是分离出来的子线程"];

    [self performSelectorInBackground:@selector(run:) withObject:@"我是后台线程"];

    在主线程执行代码,就调用performSelectorOnMainThread方法。

    如果想延时执行代码可以调用performSelector:onThread:withObject:waitUntilDone:方法;

    GCD:

    利用异步函数dispatch_async()创建子线程。

    在主线程执行代码,dispatch_async(dispatch_get_main_queue(), ^{});

    延迟执行代码(延迟·可以控制代码在哪个线程执行):

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{});

    NSOperationQueue:

    使用NSOperation的子类封装操作,再将操作添加到NSOperationQueue创建的队列中,实现多线程。

    在主线程执行代码,只要将封装代码的NSOperation对象添加到主队列就可以了。

    5、控制器view的生命周期?

    控制器view是懒加载,用到的时候才加载。

    生命周期方法调用顺序:

    loadView-->viewDidLoad-->viewWillAppear

    -->viewDidAppear-->viewWillDisappear-->viewDidDisappear

    -->didReceiveMemoryWarning(收到内存警告)

    -->viewWillUnload-->viewDidUnload

    6、控制器View的加载过程?

    当程序访问了控制器的View属性时会先判断控制器的View是否存在,如果存在就直接返回已经存在的View;

    如果不存在,就会先调用loadView这个方法;如果控制器的loadView方法实现了,就会按照loadView方法加载自定义的View;

    如果控制器的loadView方法没有实现就会判断storyboard是否存在;

    如果storyboard存在就会按照storyboard加载控制器的View;如果storyboard不存在,就会创建一个空视图返回。

    7、iOS开发中数据存储的方法?

    常见存储方法有三种:plist存储、偏好设置(NSUserDefaults)和归档。

    属性列表(plist)存储:

    适用对象:只有带有writeToFil方法的对象才能用这种方法,比如NSString、、NSArray、NSDictionary、NSSet、NSNumber、NSData等,不能存储自定义的对象

    存储方法:

    调用对象的writeToFile...方法就可以写入文件

    读取方法:

    调用对象的...WithContentsOfFile方法就可以从文件中读取对象内容

    偏好设置(NSUserDefaults):

    适用对象:使用NSUserDefaults,存储用户关于应用的偏好设置,本质上仍然是plist存储,不能存储自定义的OC对象

    存储方法:

    利用NSUserDefaults的setObject等方法进行存储

    读取方法:

    利用NSUserDefaults的objectForKey等方法进行读取

    归档(NSKeyedArchiver):

    适用对象:可以存储自定义的对象,只有遵守了NSCoding协议的对象才可以

    存储方法(归档):

    调用NSKeyedArchiver的archiverRootObject: toFile: 方法存储对象,archiverRootObject执行这个方法时,底层就会调用要存的对象的encodeWithCoder方法,

    调用encodeWithCoder目的就是想询问下要存对象的哪些属性

    读取方法(解档):

    调用NSKeyedArchiver的unarchiverObjectWithFile:方法,执行enachiverObjectWithFile:方法时就会调用initWithCoder:方法,

    调用initWithCoder:方法目的就是询问下要读的对象有那些属性要读,怎么读。

    注意:

    当有继承关系时,必须调用父类的归档解档方法,当有组合包含关系时,也必须实现所包含对象类的归档解档方法。

    8、为什么很多内置类如UITableViewController的delegate属性都是weak而不是strong?

    会引起循环引用的问题。

    UITableViewController内部有一个强指针tableView属性,

    tableView指针指向一个UITableView,UITableView内部有一个强指针subviews属性,

    指向一个装着UITableView全部的子控件的强指针数组,数组中又有强指针指向UITableView中存在的子控件。

    tableView内部有一个指针delegate,tableView的delegate就是控制器本身,二者相互引用,

    如果delegate属性是strong就会引起循环应用,造成内存泄露,因此必须有一个对象是弱指针,所以delegate是weak。

    9、UI控件为什么不用strong用weak?

    控制器有个强指针View属性,View属性指向内存中的一个UIView,UIView内部有一个强指针subviews属性,

    指向一个装着UIView全部的子控件的强指针数组,数组中又有强指针指向UIView中存在的子控件。

    所以,只要控制器在,View就在,View中的子控件就在,所以,ui控件没必要用强指针,用weak就可以。

    10、block使用时的注意点?

    Block可以使用在定义之前声明的局部变量。

    int i = 10;

    void(^myBlock)() = ^{

    NSLog(@"%d", i);

    };

    i = 100;

    myBlock();

    注意:

    在定义Block时,会在Block中建立当前局部变量内容的副本(拷贝)。

    后续再对该变量的数值进行修改,不会影响Block中的数值。

    以上代码输出结果为10。

    如果需要在block中保持局部变量的数值变化,需要使用__block关键字。使用__block关键字后,同样可以在Block中修改该变量的数值。

    将第一行代码改为: __block int i = 10;后,输出结果就变成了100;

    另外:block代码块中不能直接用点语法调用self的方法,会造成循环引用,要用中括号调用。

    11、如何对UITableView进行优化?

    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对象。

    12、UIView动画与核心动画的区别?

    UIView动画与核心动画的区别:

    1、核心动画只作用在CALayer上面,UIView是没有办法使用核心动画的

    2、核心动画看到的都是假象,并没有修改UIView的真实位置

    什么时候使用UIView动画?

    需要与用户进行交互的时候使用UIView动画,如果不需要与用户进行交互,两者都可以使用。

    什么时候使用核心动画?

    1、根据路径做动画,要使用核心动画(帧动画)

    2、做转场动画时,也要使用核心动画

    13、自定义视图中重写layoutsubView需要调用父类的layoutsubView吗,为什么?

    如果重写的控件是UIView不调用父类的layoutsubView也没关系,里面没有任何子控件,所以不会做什么事情。一般系统自带视图中有子控件的都会重写layoutSubviews方法,因此我们自定义系统自带控件并且重写layoutSubviews必须调用[super layoutSubviews],先布局系统自带子控件的位置和尺寸,才设置我们自己的控件位置和尺寸。否则会发现想用系统自带视图的子控件的时候,会出现意想不到的效果。

    14、应用程序的启动流程?

    1.执行Main

    2.执行UIApplicationMain函数.

    3.创建UIApplication对象,并设置UIApplicationMain对象的代理.

    UIApplication的第三个参数就是UIApplication的名称,如果指定为nil,它会默认为UIApplication.

    UIApplication的第四个参数为UIApplication的代理.

    4.开启一个主运行循环.保证应用程序不退出.

    5.加载info.plist.加载配置文件.判断一下info.plist文件当中有没有Main storyboard file base name里面有没有指定storyboard文件,如果有就去加载info.plist文件,如果没有,那么应用程序加载完毕.

    15、NSString 的时候用copy和strong的区别?

    OC中NSString为不可变字符串的时候,用copy和strong都是只分配一次内存,但是如果用copy的时候,需要先判断字符串是否是不可变字符串,如果是不可变字符串,就不再分配空间,如果是可变字符串才分配空间。如果程序中用到NSString的地方特别多,每一次都要先进行判断就会耗费性能,影响用户体验,用strong就不会再进行判断,所以,不可变字符串可以直接用strong。

    16、事件传递与响应的完整过程?

    在产生一个事件时,系统会将该事件加入到一个由UIApplication管理的事件队列中,

    UIApplication会从事件队列中取出最前面的事件,将它传递给先发送事件给应用程序的主窗口.

    主窗口会调用hitTest方法寻找最适合的视图控件,找到后就会调用视图控件的touches方法来做具体的事情.

    当调用touches方法,它的默认做法, 就会将事件顺着响应者链条往上传递,

    传递给上一个响应者,接着就会调用上一个响应者的touches方法

    17.ASIHttpRequest、AFNetWorking之间的区别?

    - ASIHttpRequest功能强大,主要是在MRC下实现的,是对系统CFNetwork API进行了封装,支持HTTP协议的CFHTTP,配置比较复杂,并且ASIHttpRequest框架默认不会帮你监听网络改变,如果需要让ASIHttpRequest帮你监听网络状态改变,并且手动开始这个功能。

    - AFNetWorking构建于NSURLConnection、NSOperation以及其他熟悉的Foundation技术之上。拥有良好的架构,丰富的API及模块构建方式,使用起来非常轻松。它基于NSOperation封装的,AFURLConnectionOperation子类。

    - ASIHttpRequest是直接操作对象ASIHttpRequest是一个实现了NSCoding协议的NSOperation子类;AFNetWorking直接操作对象的AFHttpClient,是一个实现NSCoding和NSCopying协议的NSObject子类。

    - 同步请求:ASIHttpRequest直接通过调用一个startSynchronous方法;AFNetWorking默认没有封装同步请求,如果开发者需要使用同步请求,则需要重写getPath:paraments:success:failures方法,对于AFHttpRequestOperation进行同步处理。

    - 性能对比:AFNetworking请求优于ASIHttpRequest;

    18.如何进行真机调试?

    1.首先需要用钥匙串创建一个钥匙(key);

    2.将钥匙串上传到官网,获取iOS Development证书;

    3.创建App ID即我们应用程序中的Boundle ID;

    4.添加Device ID即UDID;

    5.通过勾选前面所创建的证书:App ID、Device ID;

    6.生成mobileprovision文件;

    7.先决条件:申请开发者账号 99美刀

    19.APP发布的上架流程?

    1.登录应用发布网站添加应用信息;

    2.下载安装发布证书;

    3.选择发布证书,使用Archive编译发布包,用Xcode将代码(发布包)上传到服务器;

    4.等待审核通过;

    5.生成IPA:菜单栏->Product->Archive.

    20.能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

    - 不能向编译后得到的类中增加实例变量;

    - 能向运行时创建的类中添加实例变量;

    解释如下:

    因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表 和 instance_size 实例变量的内存大小已经确定,同时runtime 会调用 class_setIvarLayout 或 class_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量;

    运行时创建的类是可以添加实例变量,调用 class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。

    21.以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?

    RunLoop只能运行在一种mode下,如果要换mode,当前的loop也需要停下重启成新的。利用这个机制,ScrollView滚动过程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动:只能在NSDefaultRunLoopMode模式下处理的事件会影响ScrollView的滑动。

    如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。

    同时因为mode还是可定制的,所以:

    Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。代码如下:

    //将timer添加到NSDefaultRunLoopMode中

    [NSTimer scheduledTimerWithTimeInterval: target: selector:@selector(timerTick:) userInfo: repeats:];

    //然后再添加到NSRunLoopCommonModes里

    NSTimer *timer = [NSTimer timerWithTimeInterval: target: selector:@selector(timerTick:) userInfo: repeats:];

    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

    22.runloop和线程有什么关系?

    总的说来,Run loop,正如其名,loop表示某种循环,和run放在一起就表示一直在运行着的循环。实际上,run loop和线程是紧密相连的,可以这样说run loop是为了线程而生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分, Cocoa 和 CoreFundation 都提供了 run loop 对象方便配置和管理线程的 run loop (以下都以 Cocoa 为例)。每个线程,包括程序的主线程( main thread )都有与之相应的 run loop 对象。

    runloop 和线程的关系:

    - 主线程的run loop默认是启动的。

    iOS的应用程序里面,程序启动后会有一个如下的main()函数

    ( argc,  * argv[]) {

    @autoreleasepool {

    return UIApplicationMain(argc, argv, , NSStringFromClass([AppDelegate class]));

    }

    }

    重点是UIApplicationMain()函数,这个方法会为main thread设置一个NSRunLoop对象,这就解释了:为什么我们的应用可以在无人操作的时候休息,需要让它干活的时候又能立马响应。

    - 对其它线程来说,run loop默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要。

    - 在任何一个 Cocoa 程序的线程中,都可以通过以下代码来获取到当前线程的 run loop 。

    NSRunLoop *runloop = [NSRunLoop currentRunLoop];

    23.如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

    使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理的block。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );

    dispatch_group_t group = dispatch_group_create();

    dispatch_group_async(group, queue, ^{ /*加载图片1 */ });

    dispatch_group_async(group, queue, ^{ /*加载图片2 */ });

    dispatch_group_async(group, queue, ^{ /*加载图片3 */ });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{

    // 合并图片

    });

    24.HTTP协议的特点,关于HTTP请求GET和POST的区别?

    HTTP协议的特点:

    - HTTP超文本传输协议,是短连接,是客户端主动发送请求,服务器做出响应,服务器响应之后,链接断开。HTTP是一个属于应用层面向对象的协议,HTTP有两类报文:请求报文和响应报文。

    - HTTP请求报文:一个HTTP请求报文由请求行、请求头部、空行和请求数据4部分组成。

    - HTTP响应报文:由三部分组成:状态行、消息报头、响应正文。

    GET和POST的区别:

    - GET请求:参数在地址后拼接,没有请求数据,不安全(因为所有参数都拼接在地址后面),不适合传输大量数据(长度有限制,为1024个字节)。

    GET提交、请求的数据会附在URL之后,即把数据放置在HTTP协议头中。以?分割URL和传输数据,多个参数用&连接。如果数据是英文字母或数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。

    - POST请求:参数在请求数据区放着,相对GET请求更安全,并且数据大小没有限制。把提交的数据放置在HTTP包的包体中.

    - GET提交的数据会在地址栏显示出来,而POST提交,地址栏不会改变。

    传输数据的大小:

    - GET提交时,传输数据就会受到URL长度限制,POST由于不是通过URL传值,理论上书不受限。

    - POST的安全性要比GET的安全性高;

    - 通过GET提交数据,用户名和密码将明文出现在URL上,比如登陆界面有可能被浏览器缓存。

    25.如何理解MVC设计模式?

    MVC是一种架构模式,M表示MOdel,V表示视图View,C表示控制器Controller:

    - Model负责存储、定义、操作数据;

    - View用来展示书给用户,和用户进行操作交互;

    - Controller是Model和View的协调者,Controller把Model中的数据拿过来给View用。Controller可以直接与Model和View进行通信,而View不能和Controller直接通信。View与Controller通信需要利用代理协议的方式,当有数据更新时,Model也要与Controller进行通信,这个时候就要用Notification和KVO,这个方式就像一个广播一样,MOdel发信号,Controller设置监听接受信号,当有数据更新时就发信号给Controller,Model和View不能直接进行通信,这样会违背MVC设计模式。

    26.线程与进程的区别和联系?

    - 一个程序至少要有进城,一个进程至少要有一个线程。

    - 进程:资源分配的最小独立单元,进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

    - 线程:进程下的一个分支,是进程的实体,是CPU调度和分派的基本单元,它是比进程更小的能独立运行的基本单位,线程自己基本不拥有系统资源,只拥有一点在运行中必不可少的资源(程序计数器、一组寄存器、栈),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源。

    - 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。

    - 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

    - 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

    http://www.jianshu.com/p/1a796d0f13f4

    相关文章

      网友评论

        本文标题:iOS面试题

        本文链接:https://www.haomeiwen.com/subject/ljhpdttx.html