iOS面试题-常规概念

简要叙述OC语言的特点

是根据C语言所衍生出来的语言,继承了C语言的特性,是扩充C的面向对象编程语言,所以具有面向对象的语言特性,如:封装、多态、继承。
封装 是对象和类概念的主要特性。它是隐藏内部实现,提供外部接口。
继承 它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态 不同对象以自己的方式响应相同的消息的能力叫做多态。
总的来说面向对象是一种思想,目的可以让代码重用,接口重用,避免重复代码,逻辑更加清晰,更容易维护,提高编程效率.

另外OC具有动态特性:之所以叫做动态,是因为必须到运行时(runtime)才会做一些事情。(动态特性的三个方面:动态类型、动态绑定、动态加载)
(1)动态类型:
         动态类型,(id类型)在编译器编译的时候不能被识别出,在运行时(run time),程序运行的时候才会根据语境来识别。
         静态类型,与动态类型相对。在编译的时候就能识别出来,明确的基本类型都属于静态类型。(int、NSString等)
(2)动态绑定
          (关键词@selector)跳过编译,在运行时动态添加函数调用,运行时才决定调用什么方法,传递什么参数。
(3)动态加载
         根据需求动态地加载资源。

类别的作用?继承、类别和扩展在实现中有何区别?

类别的作用:(1)将类的实现分散到多个不同文件或多个不同框架中。
(2)创建对私有方法的前向引用。
(3)向对象添加非正式协议。
扩展:
只有头文件没有实现文件。只能扩展方法,不能添加成员变量。扩展的方法只能在原类中实现
类扩展只能针对自定义的类,不能给系统类增加类扩展;
由于局限性比较多,个人在开发中没有用过.
继承主要作用:
1. 重写父类方法
2. 在父类基础上增加属性,方法,协议
category 可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改。
并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。
继承可以增加,修改方法,并且可以增加属性。

OC中类变量的@protected,@private,@public,@package区别

@protected (默认)该类和所有子类中的方法可以直接访问这样的变量。
@private 该类中的方法可以访问,子类不可以访问。
@public   可以被所有的类访问
@package 本包内使用,跨包不可以
实际开发中基本都是默认,没有使用过其他的

#import、#include、@class、#import<>和#import""的区别

#include与#import都是导入头文件的关键字,完整地包含某个文件的内容,后者会自动导入一次,不会重复导入,不会引发交叉编译.
@class仅仅是声明一个类名,并不会包含类的完整声明,编译效率高.可避免循环依赖,且使用后带来的编译错误.
#import<>:用于对系统自带的头文件的引用,编译器会在系统文件目录下查找该文件。
#import"":用户自定义的文件用双引号引用,引用时编译器首先会在用户目录下查找,然后去安装目录中查找,最后在系统文件目录中查找。
另外:iOS7之后的新特性,可以使用@import 关键词来代理#import引入系统类库。
     使用@import引入系统类库,不需要到build phases中先添加添加系统库到项目中。

什么是设计模式?聊聊你所知道的设计模式。

1.代理模式
  当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。
  代理+协议的组合。实现1对1的反相传值操作。
2.观察者模式
  Notification通知中心,注册通知中心,任何位置可以发送消息,注册观察者的对象可以接收。
  KVO是典型的通知模式,观察某个属性的状态,状态发生变化时通知观察者。
3.MVC模式
  通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。
  Model View Control, 把模型 视图 控制器 层进行解耦合编写。
4.单例模式
  确保程序运行期某个类,只有一份实例,用于进行资源共享控制
  系统实例:[UIApplication sharedApplication]。
5.工厂模式
  通过一个类方法,批量的根据已有模板生产对象。
在实际开发中,代理、观察者和单例大多数情况都是为了数据传递.
而MVC可以使代码逻辑更加清晰,更容易维护.
工厂模式则可以让常用方法重用,使项目更容易维护.

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

1.@property的本质 = ivar (实例变量) + getter (取方法) + setter (存方法)
“属性”(property)有两大概念:实例变量(ivar)、存取方法(getter + setter)
2.ivar、 getter 、setter 是如何生成并添加到这个类中的
这是编译器自动合成的,通过@synthesize 关键字指定,若不指定,默认为@synthesize  propertyName = _propertyName;若手动实现了getter/setter 方法,则不会自动合成。
现在编译器已经默认为我们添加了@synthesize  propertyName = _propertyName;因此不再手动添加了,除非你真的要改变成员变量名字。
生成getter方法时,会判断当前属性名是否有“_”,比如声明属性为@property(nonatomic,copy)NSString *_name;那么所生成的成员变量名就会变成“_name”,如果我们要手动生成getter 方法,就要判断是否以“_”开头了。

@property常用属性及如何使用

读写属性: (readwrite/readonly/setter = /getter = )
引用计数:(assign/retain/copy/strong)
原⼦性: (atomic/nonatomic)
ARC其中属性默认是:readwrite,strong, atomic
atomic 是默认的属性,表示对象的操作属于原子操作(原子性指事务的一个完整操作。操作成功则提交,失败则回滚),主要是在多线程的环境下,提供多线程访问的安全。
  我们知道在多线程的下对对象的访问都需要先上锁访问后再解锁,保证不会同时有⼏个操作针对同⼀个对象。
  如果编程中不涉及到多线程,不建议使用,因为使用atomic比nonatomic更耗费系统资源。
  注意:atomic的作用只是给getter和setter加了个锁,atomic只能保证代码进入getter或者setter函数内部时是安全的,并不能保证整个对象是线程安全的。
  个人在实际开发中还真的没用过。
nonatomic 表示访问器的访问不是原⼦操作,不支持多线程访问安全,但 是访问性能⾼。

readwrite(默认):readwrite是默认值,表示该属性同时拥有setter和getter。
readonly: readonly表示只有getter没有setter。

retain 表⽰对NSObject和及其⼦子类对象release旧值,再retain新值,使对象的应⽤计数增加1。
  该属性只能使⽤用于obejective-c类型对象,不能用于Core Foundation对象。
assign 是基本数据类型的默认属性,setter方法将传入的参数赋值给实例变量,可以对基本数据类型(如CGFloat, NSInteger,Bool,int,代理,id对象)等使⽤。可以用非OC对象
  该方式会对象直接赋值而不会进行retain操作。
weak 是修饰的对象在释放之后,指针地址会被置为nil。所以现在一般弱引用就是用weak。常用于delegate,weak必须用于OC对象
strong 是在iOS引入ARC的时候引入的关键字,也是普通OC对象的默认属性,是retain的一个可选的替代。
  表示实例变量对传入的对象要有所有权关系,即强引用。
  strong跟retain的意思相同并产生相同的代码,但是语意上更好更能体现对象的关系。
copy 表示重新建立一个新的计数为1的对象,然后释放掉旧的值。常用于NSString,block

另外还有一些如Xcode 6.3新出的关键字nonnull,nullable,主要是为了与swift混编更加方便,注意只能修饰对象,不能修饰基本数据类型
nullable 表示对象可以是NULL或nil
nonnull  表示对象不应该为空
null_resettable: get:不能返回空, set可以为空(注意:如果使用null_resettable,必须 重写get方法或者set方法,处理传递的值为空的情况
null_unspecified:不确定是否为空

class:类属性,类使用类方法。

用@property声明的 NSString / NSArray / NSDictionary 经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?

用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,
他们之间可能进行赋值操作(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
1.因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本。
2.如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。
3.使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无意间篡改不可变类型对象原来的值。

为什么IBOutlet属性是weak的?

因为既然有外链那么视图在xib或者storyboard中肯定存在,视图已经对它有一个强引用了。 
IBoutlet 连线到控制器中作为视图的属性时用 weak 修饰就可以了, (用 strong 修饰也可以但是没有必要)

__weak,__block的区别

__weak与weak基本相同。前者用于修饰变量(variable),后者用于修饰属性(property)。__weak 主要用于防止block中的循环引用。
__block也用于修饰变量。它是引用修饰,所以其修饰的值是动态变化的,即可以被重新赋值的。__block用于修饰某些block内部将要修改的外部变量。

简单描述weak实现原理

现在都喜欢问一些实现原理,自己对底层的实现了解的不多,只能大概了解一下weak的实现原理.
Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。
weak 的实现原理可以概括一下三步:
1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,
  然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

id 声明的对象有什么特性?

 id 声明的对象具有运行时的特性,在程序运行时才确定对象的类型。
可以指向任意类型的OC的对象,与C中的void*万能指针相似。
运行效率低,不可以使用点语法。

id和nil代表什么(nil和NULL的区别)

id类型:是一个独特的数据类型,可以转换为任何数据类型,id类型的变量可以存放任何数据类型的对象,
  在内部处理上,这种类型被定义为指向对象的指针,实际上是一个指向这种对象的实例变量的指针
NULL是宏,是对于C语言指针而使用的,表示空指针
nil是宏,是对于Objective-C中的对象而使用的,表示对象为空.当向nil发送消息时,不会有异常,程序将继续执行下去;
Nil是宏,是对于Objective-C中的类而使用的,表示类指向空
NSNull是类类型,是用于表示空的占位对象,与JS或者服务端的null类似的含意

BOOL/bool/Boolean的区别

BOOL:
typedef signed char BOOL;
#define YES (BOOL)1
#define NO  (BOOL)0
bool:
C99标准定义了一个新的关键字_Bool,提供了布尔类型
#define bool _Bool
#define true 1  
#define false 0
Boolean:
typedef unsigned char Boolean;
enum DYLD_BOOL { FALSE, TRUE };

如表所示:

Name Typedef Header True Value False Value
BOOL signed char objc.h YES NO
bool _Bool (int) stdbool.h true false
Boolean unsigned char MacTypes.h TRUE FALSE
NSNumber __NSCFBoolean Foundation.h @(YES) @(NO)
CFBooleanRef struct CoreFoundation.h kCFBooleanTrue kCFBooleanFalse

OC的反射机制

Objective-C语言中的OC对象,都继承自NSObject类。这个类为我们提供了一些基础的方法和协议,我们可以直接调用从这个类继承过来方法。大部分的动态反射支持来自NSObject 类。NSObject是所有类(除了一些很少见的例外)的根类。所以基本常用到的类应该都可以支持反射。

//class反射:通过类名的字符串形式实例化对象
Class class = NSClassFromString(@"user"); 
User *user = [[class alloc] init];
//将类名变为字符串
Class class =[User class];
NSString *className = NSStringFromClass(class);

//SEL反射:通过方法的字符串形式实例化方法
SEL selector = NSSelectorFromString(@"setName");  
[stu performSelector:selector withObject:@"Song"];
//将方法变成字符串
NSStringFromSelector(@selector*(setName:));

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

这个问题算是很经典了,也经常见到
概念:
1、进程是具有一定独立功能的程序、它是系统进行资源分配和调度的一个独立单位,重点在系统调度和单独的单位,也就是说进程是可以独立运行的一段程序。    
2、线程是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源。
联系:
1、一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(通常说的主线程)。    
2、资源分配给进程,同一进程的所有线程共享该进程的所有资源
3、线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。    
4、处理机分给线程,即真正在处理机上运行的是线程。   
5、线程是指进程内的一个执行单元,也是进程内的可调度实体。

进程间常用通信方式

1、 URL Scheme
2、 Keychain
3、 UIPasteboard 剪切板功能
4、 UIDocumentInteractionController
5、 local socket
6、 AirDrop
7、 UIActivityViewController
8、 App Groups
9、Universal Link 通连

线程死锁的四个条件

(1)互斥条件:一个资源每次只能被一个线程使用。
(2)占有且等待(请求与保持条件):即线程已经至少保持了一个资源,但又提出了新的资源请求,
而该资源已经被其他线程占有,此时请求线程被阻塞,但对自己已获得的资源保持不放
(3)不可强行占有(不可剥夺条件):即线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,只能由获得该资源的线程自己主动释放
(4)循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。

串行并行 同步异步

队列是来管理线程的,线程里面放着很多的任务,来管理这些任务什么时候在哪些线程里去执行。
队列分为 [串行队列] 和 [并行队列] :
串行队列:队列中的线程按顺序执行(不会同时执行)
并行队列:队列中的线程会并发执行(同时执行)。
线程里面有非常多的任务(同步,异步)
同步任务: 优先级高,在线程中有执行顺序,不会开启新的线程
异步任务: 优先级低,在线程中执行没有顺序,看cpu闲不闲。在主队列中不会开启新的线程,其他队列会开启新的线程

iOS内存管理

在Objective-C的内存管理中,其实就是引用计数(reference count)的管理。且需要遵循黄金法则.
iOS内存管理方法有两种:手动引用计数(Manual Reference Counting)和自动引用计数(Automatic Reference Counting)。
从OS X Lion和iOS 5开始,不再需要程序员手动调用retain和release方法来管理Objective-C对象的内存,而是引入一种新的内存管理机制Automatic Reference Counting(ARC),
简单来说,它让编译器来代替程序员来自动加入retain和release方法来持有和放弃对象的所有权。
只有oc对象需要进行内存管理,因为创建对象时,指向对象的指针放在栈中,由系统维护,而指针指向的对象,则是放在堆中,需要开发人员维护
非oc对象类型比如基本数据类型不需要进行内存管理,会放到栈中.
对于内存管理可能产生的问题有:
野指针错误:访问了一块坏的内存(已经被回收的,不可用的内存),会有EXC_BAD_ACCESS错误
僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用(打开僵尸对象检测)。
空指针:没有指向任何东西的指针(存储的东西是0,null,nil),给空指针发送消息不会报错。

什么是ARC(ARC是为了解决什么问题诞生的)

ARC是Auto Reference Counting的缩写,即自动引用计数,由编译器在代码合适的位置中自动添加retain/Release/Autorelease/dealloc方法从而进行内存管理.
ARC几个要点:
在对象被创建时 retain count +1,在对象被release时 retain count -1.当retain count 为0 时,销毁对象。
程序中加入autoreleasepool的对象会由系统自动加上autorelease方法,如果该对象引用计数为0,则销毁。
那么ARC是为了解决什么问题诞生的呢?这个得追溯到MRC手动内存管理时代说起。
MRC下内存管理的缺点:
当我们要释放一个堆内存时,首先要确定指向这个堆空间的指针都被release了。(避免提前释放)
释放指针指向的堆空间,首先要确定哪些指针指向同一个堆,这些指针只能释放一次。(MRC下即谁创建,谁释放,避免重复释放)
模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁去释放。
多线程操作时,不确定哪个线程最后使用完毕

简述一下自动释放池底层怎么实现?

说道内存管理就要说一下自动释放池了,当你创建一个新的自动释放池时,它将被添加到栈顶,当一个对象收到发送autorelease消息时,
他被添加到当前线程的处于栈顶的自动释放池中,当自动释放池被回收时,他们从栈中被删除,并且会给池子里所有的对象都会做一次release操作。
经典例子:这段代码有什么问题?如何修改?
for (int i = 0; i < 1000000; i++) {
    NSString *str = @"Abc";
    str = [str lowercaseString];
    str = [str stringByAppendingString:@"xyz"];
    NSLog(@"%@",str);
 }
带来的问题就是在每执行一次循环,就会有一个str加到当前NSRunloop中的自动释放池中,只有当自动释放池被release的时候,
自动释放池中的标示了autorelease的这些数据所占用的内存空间才能被释放掉。由于循环次数过大,导致内存空间将被耗尽而没有被释放掉,所以就会出现内存忽然增高又忽然降低的情况,严重会导致内存溢出的现象。
解决添加一个局部的自动释放池,那么每执行一次循环就会释放一次,则不会造成内存泄露:
for (int i = 0; i < 1000000; i++) {
        @autoreleasepool {
            NSString *str = @"Abc";
            str = [str lowercaseString];
            str = [str stringByAppendingString:@"xyz"];
            NSLog(@"%@",str);
        }
}
新增的自动释放池可以减少内存用量,因为系统会在块的末尾把这些对象回收掉。而上述这些临时对象,正在回收之列。

堆和栈的区别?和队列

堆栈是两种数据结构,堆,先进先出;  栈,先进后出
按管理方式分
  对于栈来讲,是由系统编译器自动管理,不需要程序员手动管理
  对于堆来讲,释放工作由程序员手动管理,不及时回收容易产生内存泄露
按分配方式分
  堆是动态分配和回收内存的,没有静态分配的堆
  栈有两种分配方式:静态分配和动态分配
  静态分配是系统编译器完成的,比如局部变量的分配
  动态分配是有alloc函数进行分配的,但是栈的动态分配和堆是不同  的,它的动态分配也由系统编译器进行释放,不需要程序员手动管理。

队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表。
允许插入的一端称为队尾,允许删除的一端称为队头。
它是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,
和栈一样,队列是一种操作受限制的线性表。
队列是一种先进先出的数据结构,又称为先进先出的线性表,简称 FIFO(First In First Out)结构。

沙盒机制的理解和使用

处于安全考虑,iOS系统的沙盒机制规定每个应用都只能访问当前沙盒目录下面的文件(也有例外,比如在用户授权情况下访问通讯录,相册等)
特点:1.每个应用程序的活动范围都限定在自己的沙盒里
    2.不能随意跨越自己的沙盒去访问别的应用程序沙盒中的内容(iOS8已经部分开放访问extension)
    3.在访问别人沙盒内的数据时需要访问权限。
在开发中常常需要数据存储的功能,比如存取文件,归档解档等。
沙盒根目录结构:Documents、Library、tmp以及新加入的SystemData。
AppName.app目录:该目录iOS8之前是和Documents那几个在同一目录下,但iOS8之后移动到Container—>bundle—>Application中,它包含了应用程序本身的数据,包括资源文件和可执行文件等。
  程序启动以后,会根据需要从该目录中动态加载代码或资源到内存,这里用到了lazy loading的思想。
  由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。
Documents:这个目录存放用户数据。存放用户可以管理的文件;iTunes备份和恢复的时候会包括此目录。
  APP的数据库表; 必要的一些图标本地缓存; 重要的plist文件,如当前登录人的信息等可存放到此目录下.
Library 目录:这个目录下有两个子目录:
  Preferences 目录:NSUserDefaults的数据存放于此目录下。
  Caches 目录:用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。比如网络请求的数据。但是它缓存数据在设备低存储空间时可能会被删除,
Library可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份。
tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。该路径下的文件不会被iTunes备份。该目录下的东西随时有可能被系统清理掉
SystemData目录:最近查看目录才发现的,新加入的一个文件夹, 存放系统的一些东西.具体没太研究.

事件响应者链的概念

响应者链表示一系列的响应者对象.事件被交给由第一响应者对象处理,如果第一响应者不处理,事件被沿着响应者链向上传递,交给下一响应者(next responder).
一般来说,第一响应者是视图对象或者其子类对象,当其被触摸后事件被交由它处理,
如果它不处理,事件就会被传递给它的视图控制器对象(如果存在),然后就是它的父视图(superView)对象(如果存在),以此类推,直到顶层视图.
接下来会沿着顶层视图(top view)到窗口(UIWindow对象)再到程序(UIApplication对象).如果整个过程都没有响应这个事件,该事件就会被丢弃.
一般情况下,在响应者中只要由对象处理事件,事件就停止传递.但有时候可以在视图响应方法中根据一些条件判断来决定是否需要继续传递事件.
对于事件响应链在实际开发偶尔会用到,当遇到某个视图不能响应时首先就要考虑到响应链的传递问题,然后根据代码调整.
比较重要的函数:hitTest:withEvent:方法和pointInside方法
简单来说就是:事件的传递是从上到下(父控件到子控件),
事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。

可参考文章

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

1.@property有两个对应的词,一个是@synthesize,一个是@dynamic。
  如果@synthesize和@dynamic都没写,那么默认的就是@synthesize var = _var;
2.@synthesize的语义是如果你没有手动实现setter方法和getter方法,
  那么编译器会自动为你加上这两个方法。
3.@dynamic告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成。(当然对于readonly的属性只需提供getter即可)。假如一个属性被声明为@dynamic var,
  然后你没有提供setter方法和getter方法,编译的时候没问题,
  但是当程序运行到instance.var = someVar,由于缺setter方法会导致程序崩溃;
  或者当运行到 someVar = var时,由于缺getter方法同样会导致崩溃。
  编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
实际开发中我们常会用到重写getter方法来做懒加载,或者用setter方法来完成调用.很少会将两个同时重写.

类方法和实例方法有什么区别?

类方法:在OC类定义方法时以 + 开头的方法,又称为静态方法。
  它不用实例就可以直接调用的方法,一般是有返回值的,返回对应的实例(数组、字符串等),还有可能就是本身类的实例对象。
实例方法:在OC定义中以 - 开头的方法,原理是向某个对象发送一条消息,如果对象中有相应的消息就会做出回应,OC用的就是这种消息模式.
类方法:
类方法属于类对象
类方法只能通过类对象调用
类方法中的self是类对象
类方法可以调用其他类方法
类方法中不能访问成员变量
类方法不能直接调用对象方法
实例方法:
实例方法是属于实例对象的
实例方法只能呢通过实例对象调用
实例方法中的self是实例对象
实例方法中可以访问成员变量
实例方法中直接调用实例方法
实例方法中也可以调用类方法(通过类名)

浅拷贝和深拷贝的区别?

浅拷贝:只复制指向对象的指针,而不复制引用对象本身。只是新创建了类的空间,然后将属性的值复制一遍;
  对于属性所指向的内存空间并没有重新创建;因此通过浅拷贝的新旧两个对象的属性其实还是指向一块相同的内存空间 
深拷贝:复制引用和对象本身。不仅仅新创建了类的空间,还新创建了每一个属性对应的空间,所以深拷贝也称为完全拷贝;
  通过深拷贝得来的新对象和旧对象,两个对象的属性都是指向各自的内存空间,不再共享空间
另外可变对象复制(copy,mutableCopy)的都是深拷贝;不可变对象copy是浅拷贝,mutableCopy是深拷贝。但是注意copy返回的都是不可变对象,如果对copy返回值去调用可变对象的接口就会crash. 

GET和POST区别

参数携带方式不同:GET是以?拼接的方式,POST是在body中
参数长度限制:GET一般限制1024字符,POST一般没有限制
GET:获取资源,他是安全的,幂等的,可缓存的
POST:处理资源,他是非安全的,非幂等的,非可缓存的
安全性:不引起 server 端任何状态变化。如GET、HEAD、OPTIONS
幂等性:同一个请求方法执行多次和执行一次的效果完全相同。如:GET、PUT、DELETE
可缓存行:请求是否可以缓存。如:GET、HEAD

UDP和TCP的区别是什么?

TCP(传输控制协议):面向连接:三次握手,四次挥手
传输可靠:通过停止等待协议,保证数据无差错、不丢失、不重复、按序到达
面向字节流
流量控制:滑动窗口协议,简单的说一下就是接收端在发送 ACK 的时候会带上缓冲区的窗口大小,但是一般在窗口达到一定大小才会更新窗口
拥塞控制:慢开始,拥塞避免;快恢复,快重传

UDP(用户数据报协议):无连接、传输不可靠、用于传输少量数据(数据包模式)、面向报文、速度快。

TCP/IP建立连接和断开连接的过程?

- 在TCP/IP 协议中,TCP协议提供可靠的连接服务,采用三次握手建立连接;
- 第一次握手:建立连接时,客户端发送连接请求到服务器,并进入SYN_SEND状态,等待服务器确认;
- 第二次握手:服务器收到客户端连接请求,向客户端发送允许连接应答,
  此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的允许连接应答,向服务器发送确认,客户端和服务器进入通信状态,
  完成三次握手。
 (所谓的三次握手,就是要有三次连接信息的发送、接收过程。
   TCP连的建立需要进行三次连接信息的发送、接收。)

当客户端和服务端的连接想要断开的时候,要经历四次挥手的过程,步骤如下:
1.先由客户端向服务端发送 FIN 结束报文。
2.服务端会返回给客户端 ACK 确认报文 。此时,由客户端发起的断开连接已经完成。
3.服务端会发送给客户端 FIN 结束报文 和 ACK 确认报文。
4.客户端会返回 ACK 确认报文到服务端,至此,由服务端方向的断开连接已经完成。

几个概念:URG:紧急指针(urgent pointer)有效。
ACK:确认序号有效。
PSH:接收方应该尽快将这个报文交给应用层。
RST:重置连接。
SYN:发起一个新连接。
FIN:释放一个连接。
三次握手.jpg

四次挥手.jpg

为什么TCP连接建立需要三次握手

“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。
如:“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,
而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。
本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。
于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。
由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。
但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。
采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。
server由于收不到确认,就知道client并没有要求建立连接。
综合:只需要三次握手,可以提高连接的速度与效率。

为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

 这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,我们也未必全部数据都发送给对方了,
所以我们不可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,
因此,我们的ACK和FIN一般都会分开发送。

HTTPS 建立流程

HTTPS协议 = HTTP协议 + SSL/TLS协议
SSL的全称是Secure Sockets Layer,即安全套接层协议,是为网络通信提供安全及数据完整性的一种安全协议。
TLS的全称是Transport Layer Security,即安全传输层协议。
即HTTPS是安全的HTTP。
HTTPS连接过程大致可分为八步:

1、客户端访问HTTPS连接。
客户端会把安全协议版本号、客户端支持的加密算法列表、随机数C发给服务端。

2、服务端发送证书给客户端
服务端接收密钥算法配件后,会和自己支持的加密算法列表进行比对,如果不符合,则断开连接。
否则,服务端会在该算法列表中,选择一种对称算法(如AES)、一种公钥算法(如具有特定秘钥长度的RSA)和一种MAC算法发给客户端。
服务器端有一个密钥对,即公钥和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人。
在发送加密算法的同时还会把数字证书和随机数S发送给客户端

3、客户端验证server证书
会对server公钥进行检查,验证其合法性,如果发现发现公钥有问题,那么HTTPS传输就无法继续。

4、客户端组装会话秘钥
如果公钥合格,那么客户端会用服务器公钥来生成一个预主秘钥(Pre-Master Secret,PMS),并通过该预主秘钥和随机数C、S来组装成会话秘钥

5、客户端将预主秘钥加密发送给服务端
是通过服务端的公钥来对预主秘钥进行非对称加密,发送给服务端

6、服务端通过私钥解密得到预主秘钥
服务端接收到加密信息后,用私钥解密得到主秘钥。

7、服务端组装会话秘钥
服务端通过预主秘钥和随机数C、S来组装会话秘钥。
至此,服务端和客户端都已经知道了用于此次会话的主秘钥。

8、数据传输
客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。
同理,服务端收到客户端发送来的密文,用服务端密钥对其进行对称解密,得到客户端发送的数据。

总结:
会话秘钥 = random S + random C + 预主秘钥

HTTPS连接建立过程使用非对称加密,而非对称加密是很耗时的一种加密方式

后续通信过程使用对称加密,减少耗时所带来的性能损耗

其中,对称加密加密的是实际的数据,非对称加密加密的是对称加密所需要的客户端的密钥。
HTTPS.png

NSCache NSDictionary区别

1.NSCache是线程安全的,NSMutableDictionary线程不安全
2.当内存不足时NSCache会自动释放内存(所以从缓存中取数据的时候总要判断是否为空)
3.NSCache可以指定缓存的限额,当缓存超出限额自动释放内存
4.NSCache 并不会“拷贝”键,而是会“保留”它。

OC你了解的锁有哪些

互斥锁会在访问被加锁数据时,会休眠等待,当数据解锁,互斥锁会被唤醒。
自旋锁遇到被加锁数据时,会进入死循环等待,当数据解锁,自旋锁马上访问。

OSSpinLock:自旋锁,不安全 iOS10 后不建议使用
os_unfair_lock:用于取代不安全的OSSpinLock ,从iOS10开始才支持
pthread_mutex:互斥锁,等待锁的线程会处于休眠状态
@synchronized:关键字加锁,是对 mutex 递归锁的封装,加锁代码少,性能差—-因为里面会加入异常处理,所以耗时。适用线程不多,任务量不大的多线程加锁
NSLock:互斥锁,是对 mutex 普通锁的封装。所有锁(包括NSLock)的接口实际上都是通过NSLocking协议定义的,它定义了 lock 和 unlock 方法。
你使用这些方法来获取和释放该锁。
NSRecursiveLock:递归锁,有时候加锁代码中存在递归调用,递归开始前加锁,
递归开始调用后重复执行此方法以至于加锁代码照成死锁,使用NSRecursiveLock,便可解决问题。
NSCondition:条件锁,是对 mutex 和 cond 的封装
NSConditionLock:是对 NSCondition 的进一步封装,可以设置具体的条件值
dispatch_semaphore:信号量实现加锁

性能如下:

锁的性能.png

block和函数指针的理解

相同点:
1、函数指针和 Block 都可以实现回调的操作,声明上也很相似,实现上都可以看成是一个代码片段。
2、函数指针类型和 Block 类型都可以作为变量和函数参数的类型。(typedef 定义别名之后,这个别名就是一个类型)
不同点:
1、函数指针只能指向预先定义好的函数代码块(可以是其他文件里面定义,通过函数参数动态传入的),
函数地址是在编译链接时就已经确定好的。
2、Block本质是Objective-C对象,是NSObject的子类,可以接收消息。
3、函数里面只能访问全局变量,而Block代码块不光能访问全局变量,
还拥有当前栈内存和堆内存变量的可读性(当然通过__block访问指示符修饰的局部变量还可以在block代码块里面进行修改)。
4、从内存的角度看,函数指针只不过是指向代码区的一段可执行代码,
而 block 实际上是程序运行过程中在栈内存动态创建的对象,可以向其发送 copy 消息将 block 对象拷贝到堆内存,以延长其生命周期。

更多可以参考总结 iOS 面试相关总结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容

  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,139评论 30 470
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类...
    司马DE晴空阅读 1,285评论 0 7
  • 1.weak和assign区别 修饰变量类型的区别: weak 只可以修饰对象。如果修饰基本数据类型,编译器会报错...
    coderjon阅读 1,017评论 0 1
  • 把网上的一些结合自己面试时遇到的面试题总结了一下,以后有新的还会再加进来。 1. OC 的理解与特性 OC 作为一...
    AlaricMurray阅读 2,557评论 0 20
  • “你的综合素质各方面都不错,如果面试通过,明天会电话通知你的。” “好的,谢谢。” 这样类似的对话,季静这一个月里...
    黎甜阅读 1,472评论 18 31