ios基础面试题

1。  include与#import的区别、#import与@class的区别

include程序员

include <> :用于对系统文件的引用,编译器会在系统文件目录下去查找该文件。web

#include "xx.h":用于对用户自定义的文件的引用,编译器首先会去用户目录下查找,而后去安装目录,最后去系统目录查找。

注:使用include要注意重复引用的问题:服务器

class A,class B都引用了class C,class D若引用class A与class B,就会报重复引用的错误。网络

import

功能与include基本相同,不过它避免了重复引用的问题。因此在OC中咱们基本用的都是import。session

@class
@class就是告诉编译器有这个类存在,可是类是如何实现的不用告诉编译器.若.m文件用到了这个类,仍是要在.m文件汇总import这个类的。
既然这样,为何不直接在头文件中import呢,举个例子:
class A引用了class B,class B引用了class C…. , class A,B,C…的头文件又import了不少文件,那么 import了A的话,编译器就须要编译大量的文件,编译时间就会增长。
并发

难道头文件中都是用@class吗?固然不是,有时也是须要#import的,那么何时该用什么呢?
(1)通常若是有继承关系的用#import,如B是A的子类那么在B中声明A时用#import;
异步

(2) 另外就是若是有循环依赖关系,如:A->B,B->A这样相互依赖时,若是在两个文件的头文件中用#import分别声明对方,那么就会出现头文件循环利用的错误,这时在头文件中用@class声明就不会出错;async

(3)还有就是自定义代理的时候,若是在头文件中想声明代理的话如@interface SecondViewController:UIViewController时应用#import否则的话会出错误,注意XXXXDelegate是自定义的。编辑器

区别:模块化

当咱们在代码中使用两次#include的时候会报错:由于#include至关于拷贝头文件中的声明内容,因此会报重复定义的错误

可是使用两次#import的话,不会报错,因此他能够解决重复导入的问题,他会作一次判断,若是已经导入一次就不导入了

2、#import与@class的区别
1.import会包含这个类的全部信息,包括实体变量和方法,而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑,后面会再告诉你。

2.在头文件中, 通常只须要知道被引用的类的名称就能够了。 不须要知道其内部的实体变量和方法,因此在头文件中通常使用@class来声明这个名称是类的名称。 而在实现类里面,由于会用到这个引用类的内部的实体变量和方法,因此须要使用#import来包含这个被引用类的头文件。

3.在编译效率方面考虑,若是你有100个头文件都#import了同一个头文件,或者这些文件是依次引用的,如A–>B, B–>C, C–>D这样的引用关系。当最开始的那个头文件有变化的话,后面全部引用它的类都须要从新编译,若是你的类有不少的话,这将耗费大量的时间。而是用@class则不会。

4.若是有循环依赖关系,如:A–>B, B–>A这样的相互依赖关系,若是使用#import来相互包含,那么就会出现编译错误,若是使用@class在两个类的头文件中相互声明,则不会有编译错误出现。

因此,通常来讲,@class是放在interface中的,只是为了在interface中引用这个类,把这个类做为一个类型来用的。 在实现这个接口的实现类中,若是须要引用这个类的实体变量或者方法之类的,仍是须要import在@class中声明的类进来.

举个例子说明一下。

在ClassA.h中

import ClassB.h 至关于#include整个.h头文件。若是有不少.m文件#import ClassA.h,那么编译的时候这些文件也会#import ClassB.h增长了不必的#import,浪费编译时间。在大型软件中,减小.h文件中的include是很是重要的。

若是
只是 ClassB 那就没有include ClassB.h。仅须要在须要用到ClassB的.m文件中 #import ClassB.h

那么何时能够用呢?
若是ClassA.h中仅须要声明一个ClassB的指针,那么就能够在ClassA.h中声明
@ClassB

ClassB *pointer;

假设,有两个类:ClassA和ClassB,两个之间相互使用到,即构成了circular dependency(循环依赖)。若是在头文件里面只用#import把对方的头文件包含进来(构成circular inclusions,循环包含),则编译器会报错:

Expected specifier-qualifier-list before ‘ClassA’

或者

Expected specifier-qualifier-list before ‘ClassB’

为了不循环包含,在ClassA.h文件里面用@class classB把classB包含进来,一样,在ClassB.h文件里面用@class ClassA把ClassA包含进来。@class指令只是告诉编译器,这是个类,保留个空间来存放指针就能够了。

接下来,极可能在ClassA.m和ClassB.m中会有访问包含进来对象的成员的状况,这时必须让编译器知道更多信息,好比那个类有些什么方法能够调用,就必须用#import,再次把用到的类包含进来,告诉编译器所须要的额外信息。

不然,编译器会警告:

warning: receiver ‘ClassA’ is a forward class and corresponding @interface may not exist

还有另外一种状况,使用有Categories的类,要在.h头文件里用#import把Categories包含进来。

总之,使用原则是:

头文件里面只#import超类 消息文件里面#import须要发消息过去的类 其余地方就用@class转向声明
 

import会包含这个类的全部信息,包括实体变量和方法(.h文件中),而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,后面会再告诉你。
在头文件中, 通常只须要知道被引用的类的名称就能够了。 不须要知道其内部的实体变量和方法,因此在头文件中通常使用@class来声明这个名称是类的名称。 而在实现类里面,由于会用到这个引用类的内部的实体变量和方法,因此须要使用#import来包含这个被引用类的头文件。

三。深、浅拷贝的基本概念以及他们的区别

一.通俗的说:
copy, mutableCopy
@interface A {
B *b;
}
浅拷贝只是拷贝对象自己,不会对里面的子对象进一步拷贝
深拷贝会对子对象以及子对象的子对象进一步拷贝
二.深刻理解:
浅拷贝并不复制数据,只复制指向数据的指针,所以是多个指针指向同一份数据。 深拷贝会复制原始数据,每一个指针指向一份独立的数据。经过下面的代码, 能够清楚地看出它们的区别:

struct Test{
char ptr;
};
void shallow_copy(Test &src, Test &dest){
dest.ptr = src.ptr;
}
void deep_copy(Test &src, Test &dest){
dest.ptr = (char)malloc(strlen(src.ptr) + 1);
memcpy(dest.ptr, src.ptr);
}
浅拷贝在构造和删除对象时容易产生问题,所以使用时要十分当心。如无必要, 尽可能不用浅拷贝。当咱们要传递复杂结构的信息,而又不想产生另外一份数据时, 可使用浅拷贝,好比引用传参。浅拷贝特别须要注意的就是析构时的问题, 当多个指针指向同一分内存时,删除这些指针将致使屡次释放同一内存而出错。

实际状况下是不多使用浅拷贝的,而智能指针是浅拷贝概念的加强。 好比智能指针能够维护一个引用计数来代表指向某块内存的指针数量, 只有当引用计数减至0时,才真正释放内存。

大部分时候,咱们用的是深拷贝,特别是当拷贝的结构不大的时候。

一.什么状况使用 weak 关键字?

1)在ARC中,在有可能出现循环引用的时候,每每要经过让其中一端使用weak来解决,好比:delegate代理属性

2)自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性通常也使用weak;固然,也可使用strong。在下文也有论述:《IBOutlet连出来的视图属性为何能够被设置成weak?》

二.不一样点:

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

2)assigin 能够用非OC对象,而weak必须用于OC对象

@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的

@property 的本质是什么?

@property = ivar + getter + setter;

下面解释下:

“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)。

“属性” (property)做为 Objective-C 的一项特性,主要的做用就在于封装对象中的数据。 Objective-C 对象一般会把其所须要的数据保存为各类实例变量。实例变量通常经过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。这个概念已经定型,而且经由“属性”这一特性而成为Objective-C 2.0的一部分。 而在正规的 Objective-C 编码风格中,存取方法有着严格的命名规范。 正由于有了这种严格的命名规范,因此 Objective-C 这门语言才能根据名称自动建立出存取方法。其实也能够把属性当作一种关键字,其表示:

编译器会自动写出一套存取方法,用以访问给定类型中具备给定名称的变量。 因此你也能够这么说:

@property = getter + setter;

例以下面这个类:

@interface Person : NSObject
@property NSString firstName;
@property NSString 
lastName;
@end
上述代码写出来的类与下面这种写法等效:

@interface Person : NSObject

(NSString )firstName;
(void)setFirstName:(NSString 
)firstName;
(NSString )lastName;
(void)setLastName:(NSString 
)lastName; @end ivar、getter、setter 是如何生成并添加到这个类中的?
“自动合成”( autosynthesis)

完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫作“自动合成”( autosynthesis)。须要强调的是,这个过程由编译 器在编译期执行,因此编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter、setter 以外,编译器还要自动向类中添加适当类型的实例变量,而且在属性名前面加下划线,以此做为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为 _firstName与_lastName。也能够在类的实现代码里经过 @synthesize语法来指定实例变量的名字.

@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = myLastName;
@end
我为了搞清属性是怎么实现的,曾经反编译过相关的代码,大体生成了五个东西:

1)OBJCIVAR$类名$属性名称 :该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。

2)setter与getter方法对应的实现函数

3)ivar_list :成员变量列表

4)method_list :方法列表

5)prop_list :属性列表

也就是说咱们每次在增长一个属性,系统都会在ivar_list中添加一个成员变量的描述,在method_list中增长setter与getter方法的描述,在属性列表中增长一个属性的描述,而后计算该属性在对象中的偏移量,而后给出setter与getter方法对应的实现,在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值,为了可以读取正确字节数,系统对象偏移量的指针类型进行了类型强转.
补充:
Objective-C运行时定义了几种重要的类型。
Class:定义Objective-C类
Ivar:定义对象的实例变量,包括类型和名字。
Protocol:定义正式协议。
objc_property_t:定义属性。叫这个名字多是为了防止和Objective-C 1.0中的用户类型冲突,那时候尚未属性。
Method:定义对象方法或类方法。这个类型提供了方法的名字(就是选择器)、参数数量和类型,以及返回值(这些信息合起来称为方法的签名),还有一个指向代码的函数指针(也就是方法的实现)。
SEL:定义选择器。选择器是方法名的惟一标识符。
IMP:定义方法实现。这只是一个指向某个函数的指针,该函数接受一个对象、一个选择器和一个可变长参数列表(varargs),返回一个对象

5.GCD底层三种队列

  • Serial:一个接着一个执行
  • Concurrent:可让多个任务并发执行(自动开启多个线程同时执行任务)
  • Main dispatch queue:是系统建立好准们用来存储在主线程中执行任务的队列,添加到主队列中的任务只能在主线程中执行,使用的时候只须要直接去拿就能够了;主队列只能够异步执行,不然会形成死锁;
  • 三种队列的使用方法都是:a.定制任务,b.将任务添加到队列中,GCD会自动将队列的任务取出,放到对应的线程中执行,任务的执行遵循队列的FIFO原则;
    dispatch_queue_t queue = dispatch_get_main_queue();
    for (int i =0;i<10;i++){
    dispatch_async(queue,^{
    [self longtimeOperation:i];
    });
    }

    6.CALayer和UIView的关系

    • UIView和UIView简单点说就是MVC的关系,UIView是CALayer就是M,负责绘图单元的就是V;由于听从单一职责原则,CALayer负责存储数据,UIView就是负责管理CALayer树的职责就是存储数据的树,以供你修改,作动画和渲染等使用。UIView树的话,先看UIView继承了什么,UIView继承了UIResponder,UIResponder是负责一些触摸事件的类,因此UIView树主要的职责就是作触摸的传递,这个工做能够分为2部分,1部分是寻找你点击到的那个view,另一部分是把你的触摸方法传递下去。在这里的话UIView树实际上是充当了责任链职责。

      7.@synthesize和@dynamic有什么区别?

    • @synthesize是告诉编译器自动生成get和set方法,而@dynamic则是告诉编译器不须要生成属性的get和set方法,对应的方法会由咱们程序员手动去写,避免编译的时候由于不能找到属性对应的get和set方法而报错的状况。
    • 在Xcode 6.0以后,每次使用@property申明属性,其实都默认执行了@sysnthesize,若是不须要自动生成get set方法,能够在属性对应的.m文件中加上 “@dynamic 属性名”便可.

      8.请简单叙述AFNetWorking的实现原理:

    • 核心是NSURLConnection和NSOperation:NSURLConnection是Foundation URL加载系统的基石,一个NSURLConnection异步地加载一个NSURLRequest对象,调用delegate的NSURLResponse/NSHTTPURLResponse方法,请求的数据NSData被发送到服务器或者从放服务器读取;delegate还能够用来处理其余重定向响应以及存储在共享的NSURLCache上.
      NSOperation是一个抽象类,AFNetWorking的核心就是将他们结合在一块儿,AFURLConnectionOperation 做为 NSOperation 的子类,遵循 NSURLConnectionDelegate 的方法,能够从头至尾监视请求的状态,并储存请求、响应、响应数据等中间状态。
    • 以后升级版本AFNetWorking2.0:关键在于兼容了NSURLSession使用来替代了NSURLConnection的类,可是NSURLConnection并无被抛弃,
    • 模块化 - 对于 AFNetworking 的主要批评之一是笨重。虽然它的构架使在类的层面上是模块化的,但它的包装并不容许选择独立的一些功能。随着时间的推移,AFHTTPClient尤为变得不堪重负(其任务包括建立请求、序列化 query string 参数、肯定响应解析行为、生成和管理 operation、监视网络可达性)。 在 AFNetworking 2.0 中,你能够挑选并经过 CocoaPods subspecs 选择你所须要的组件
    • 介绍一下NSURLConnectionOperation:AFURLConnectionOperation - NSOperation 的子类,负责管理 NSURLConnection 而且实现其 delegate 方法。
      AFHTTPRequestOperation - AFURLConnectionOperation 的子类,用于生成 HTTP 请求,能够区别可接受的和不可接受的状态码及内容类型。2.0 版本中的最大区别是,你能够直接使用这个类,而不用继承它,缘由能够在“序列化”一节中找到 AFHTTPRequestOperationManager - 包装常见 HTTP web 服务操做的类,经过AFHTTPRequestOperation 由 NSURLConnection 支持。
    • AFURLSessionManager - 建立、管理基于 NSURLSessionConfiguration 对象的NSURLSession 对象的类,也能够管理 session 的数据、下载/上传任务,实现 session 和其相关联的任务的 delegate 方法。由于 NSURLSession API 设计中奇怪的空缺,任何和NSURLSession 相关的代码均可以用 AFURLSessionManager 改善。 AFHTTPSessionManager - AFURLSessionManager 的子类,包装常见的 HTTP web 服务操做,经过 AFURLSessionManager 由 NSURLSession 支持。 总的来讲:为了支持新的 NSURLSession API 以及旧的未弃用且还有用的NSURLConnection,AFNetworking 2.0 的核心组件分红了 request operation 和 session 任务。AFHTTPRequestOperationManager 和 AFHTTPSessionManager 提供相似的功能,在须要的时候(好比在 iOS 6 和 7 之间转换),它们的接口能够相对容易的互换。
相关文章
相关标签/搜索