1.什么是函数式编程?什么是链式编程思想?
1.编程思想的由来:我们在开发中会遇到各种各样的问题,经常思考如何快速完成这个需求,这样就会慢慢形成快速完成这些需求的思想,
2.面向过程:处理事情以过程为核心,一步一步实现
3.链式编程思想:将多个操作(多行代码)通过点语法(.)链接成一句代码,代码可读性好
3.1.链式变成思想特点:方法的返回值是block,block必须有返回值(对象本身),block(参数),代表有masonry框架
4.响应式编程思想:不需要考虑调用顺序,只需要知道结果,代表有KVO运用
4.1函数式编程思想:把操作尽量写成一系列嵌套的函数或者方法调用
4.2函数式编程思想的本质:就是在方法里面传入block,方法中嵌套block调用,把代码聚合起来管理
4.3函数式编程的特点:每个方法必须有返回值(对象本身),把函数或者block当做参数,block参数(需要操作的值)block(操作结果),代表有ReactiveCocoa
2.为什么说OC是一门动态语言?
1.OC类的类型和数据变量的类型都是在运行时确定的,而不是在编译时确定的,
2.例如:多态性:我们可以使用父类指针指向子类对象,并且可以使用子类的方法,运行时的特性可以使我们动态加方法或者替换方法
3.讲一下MVC,MVVM,MVP?
1.MVC: 简单来说就是,逻辑,视图,数据进行分层,实现解耦
2.MVVM:是Model-View-ViewMode模式的简称,由视图,是视图模型,模型三部分组成,比MVC更加释放控制器的臃肿,将一部分逻辑(耗时,公共方法,网络请求)和数据处理从控制器搬运到ViewMode中,
2.1.低耦合:View可以独立于Model变化和修改,一个ViewMode可以绑定到不同View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
2.2.可重用性,可以把一些视图的逻辑放在ViewModel上。
2.3.独立开发:开发人员可以专注于业务逻辑的开发,设计人员可以专注界面的设计。
4.为什么代理使用weak?代理的delegate和dataSource有什么区别?block和代理的区别?
1.代理使用weak修饰,原因如下:
1.1.使用weak修饰是为了避免循环引用,
1.2.当使用weak修饰时,对象释放的时候系统会对属性赋值nil,OC的特性就是对nil对象发送消息也就会调用方法不会cash,
1.3.delegate传递的是事件,代理可以让A对象通知B对象,A发生的变化,前提是B遵循了A的代理,并且实现了A的代理方法
2.dataSource传递的是数据,如果A声明了数据源,我们创建A对象的时候,我们就该实现数据源。
3.代理和block的区别:相同点:代理和block基本用来倒序传值,都要注意避免循环引用,我们去使用的时候都需要判断是否实现
4.不同点:代理使用weak修饰,代理必须先声明方法,block是用copy修饰,block保存的是一段代码,其实就是一个函数,并且可以自动捕捉自动变量,如果想修改这个变量必须使用__block
5.属性的实质是什么?包含哪些部分?默认关键字有哪些?@dynamic关键字和@synthesize用来做什么?
1.属性的实质是描述类的特征,主要包含三部分,带下划线的成员变量,get,set方法,
2.默认的关键字:readwrite,assign,atomic,是针对基本类型(NSInteger, BOOL, NSUInteger, int, 等),
strong, readwrite, atomic是针对引用类型,(NSString, NSArray, NSDictory等),
3.@dynamic修饰的属性,getter和setter方法编译器不会帮你自动生成,必须是自己实现,
4.@synthesize:修饰的属性,其getter和setter方法编译器是会自动帮你生成,不必自己实现。且指定与属性相对应的成员变量。
6.NSString为什么使用copy关键字?如果使用strong有什么问题?
1.可变类型NSMutableArray,NSMutableString等,是不可边类型(NSString,NSArray等)的子类,因为多态,我们可以使用不可变类型去接受可变类型。
2.当我们使用strong修饰不可变类型A时,并且使用B可变类型给A赋值,再去修改可变类型B的时候,A所指向的值也会改变,引用strong只会让创建的对象引用计数+1,并返回当前对象的地址,当我们修改B指向的内容的时候,A指向的内容也会改变,因为他们指向的内存地址相同,是同一份内容.
3.当使用copy修饰不可变类型A时,并且使用可变类型B给A赋值,再去修改可变类型B的值,A的值不会改变,因为使用copy修饰的时候,会拷贝出一份内容,并且返回指针给A,当我们修改B的时候A不会改变,因为A指向的内存地址和B指向的内存地址是不相同的,是两份内容,
4.copy修饰不可边类型(NSString,NSArray等)的时候,且使用不可边类型进行赋值,表示浅拷贝,只拷贝一份指针,和strong修饰一样,当修饰的是可变类型(NSMutableArray,NSMutableString等)的时候,且使用可边类型进行赋值,表示深拷贝,直接拷贝新一份内容,到内存中。表示两份内容。![image](http://upload-images.jianshu.io/upload_images/2668212-cd8d26b0ac765b10?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
7.如何令自己所写的对象具有拷贝功能?
如果想让自己的类具备copy方法,并返回不可边类型,必须遵循nscopying协议,并且实现
- (id)copyWithZone:(NSZone *)zone
如果让自己的类具备mutableCopy方法,并且放回可变类型,必须遵守NSMutableCopying,并实现- (id)mutableCopyWithZone:(nullable NSZone *)zone
注意:再此说的copy对应不可边类型和mutableCopy对应不可边类型方法,都是遵从系统规则而已。如果你想实现自己的规则,也是可以的。
8.为什么IBOutlet修饰的UIView也适用weak关键字?
在xib或者sb拖控件的时候,其实控件已经加载到父控件的subviews数组里面,进行了强引用,即使使用weak也不会造成对象的释放。
9.用StoryBoard开发界面有什么弊端?如何避免?
使用简单逻辑页面的跳转是可以使用sb的,开发比较块。
但是SB对于逻辑项目比较复杂的时候,开发起来比较慢。不适合多人合作开发;也不利于版本的梗系和后期的维护。使用sb在项目变异编译的时候,也都会直接加载到内存中,造成内存的浪费。
可以使用xib来代替,编辑复杂逻辑界面时候可以使用纯码编写。
10.进程和线程的区别?同步异步的区别?并行和并发的区别?
进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
同步:阻塞当前线程操作,不能开辟线程。
异步:不阻碍线程继续操作,可以开辟线程来执行任务。
并发:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。
并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
区别:并发和并行是即相似又有区别的两个概念,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。倘若在计算机系统中有多个处理机,则这些可以并发执行的程序便可被分配到多个处理机上,实现并行执行,即利用每个处理机来处理一个可并发执行的程序,这样,多个程序便可以同时执行。
11.如何使用队列来避免资源抢夺?
当我们使用线程访问同一个数据的时候,可能造成数据的不准确性,这个时候我们可以使用线程锁来绑定,也可以使用串行队列完成,比如:fmdb就是使用FMDatabaseQueue,解决多线程抢夺资源
12.数据化持久存储的方法
plist:存储字典,数组比较好用
preference:偏好设置,实质上也是plist
NSKeyedArchiver:归档,也可以使用存储对象
sqlite:数据库,经常使用的第三方数据库操作,比如fmdb
coreData:也是数据库存储,苹果官方的
14.NSCache优于NSDictionary的几点?
1.NSCache是可以自动释放内存的
2.NSCache是线程安全的,我们可以在不同的线程中添加,删除,查询缓存的对象
3.一个缓存对象不会拷贝key对象
15.Designated Initializer (指定初始化函数)使用需要注意什么?
1.子类如果有指定的初始化函数,那么指定的初始化函数实现时必须调用它的直接父类的指定初始化函数。
2.如果子类有指定初始化函数,那么便利初始化函数必须调用自己的其它初始化函数(包括指定初始化函数以及其他的便利初始化函数),不能调用super的初始化函数。
16.实现description方法能取到效果?
description是NSObject的实例方法,返回值是一个NSString,我们使用NSLog打印结果的时候,打印的对象一般都是内存地址,我们实现description方法时,就可以打印出对象,我们可以把属性值和内存地址一起打印出来。
-(NSString *)description{
NSString * string = [NSString stringWithFormat:@"<Person:内存地址:%p name = %@ age = %ld>",self,self.name,self.age];
return string;
}
17.objc使用什么机制管理对象内存?
OC使用内存管理计数器来管理内存,当内存管理计数器为0的时候,对象就会被释放。
18.block实质是什么?一共有几中block?都是在什么情况下生成?
1.block的本质就是object-c对象
2.block存储位置:分为三个地方,代码区,堆区,栈区(ARC的情况下会自动拷贝到堆区,因此ARC情况下只能去两个地方,代码区和堆区)
2.1.代码区:不能访问栈区变量(比如局部变量)也不能访问堆区变量(alloc创建的对象),此时block存放在代码区
2.2.堆区:访问处于栈区或者堆区的变量,block存放在堆区,需要注意的是,实际存放的是栈区,不过ARC状态下会自动拷贝到堆区,如果不是ARC就是存放在栈区,所以函数执行完毕就会被释放,想在外面调用就是用copy,就拷贝到了堆区,strong属性不会拷贝,会造成野指针错区。
19.为什么在默认情况下无法修改block捕获的变量,__block都做了什么??
1.默认情况下,block里面的变量,拷贝进去的是变量的值,而不是指向变量内存的指针
2.当使用__block修饰变量,拷贝到block里面就是指向变量的指针,就可以修改变量的值
20.object在向一个对象发送消息时,发生了什么?
根据对象的isa指针类对象的id,在查询类对象里面的methodLists方法函数列表,如果没有找到,再沿着superClass寻找父类,再在父类methodLists方法列表里面查询,最终找到SEL,根据id和SEL确认IMP(指针函数),然后发送消息。
21.什么时候会报unrecognized selector错误?iOS有哪些机制避免走到这一步?
当我们发送消息的时候,我们会根据类里面的methodLists方法列表查询我们要动用SEL,当查询不到的时候,我们会沿着父类查询,当最终查询不到的时候会报unrecognized selector错误,当系统查不到的时候会调用 +(BOOL)resolveInstanceMethod:(SEL)sel 方法,动态解释的方法再添加吊用不到的方法,或者我们可以再次使用 -(id)forwardingTargetForSelector:(SEL)aSelector 重定向方法告诉系统,该调用什么方法,保证不会崩溃。
22.能否向编译后的类中增加实例变量?能否向运行时创建的类中增加实例变量?为什么??
1.不能向编译后得到的类中增加实例变量。编译后的类中已经注册在runtime中,类结构体中的objc_ivar_list(属性列表)实例变量的链表和instance_size(实例变量的内存大小)实例变量的内存大小已经确定,runtime会调用class_setvarlayout或calss_setWeaklvarLayout来处理strong weak引用,所以不能向存在的类中增加实例变量
2.能向运行时创建的类中增加实例变量。运行时创建的类中是可以添加实例变量的,调用class_addlvar函数,但是在调用objc_allocClassPair之后,调用objc_registerClassPair之前
23.runtime如何实现weak变量的自动置nil?
runtime会对类的注册进行布局,对于weak对象会放入一个hash表中,使用weak指向的对象内存地址为key,当这个对象引用计数为0的时候会dealloc,假如weak指向的对象内存地址是a,那么就会以a为键,在这个weak表中搜索,找到所有以a为键值的weak对象,从而设置为nil。
24.给类增加属性之后,在类的结构体哪些元素会发生变化??
1.instance_size :实例的内存大小
2.objc_ivar_list *ivars:属性列表
25.runloop是做什么的?runloop和线程有什么关系?主线程默认开启了runloop吗?子线程呢?
1.runloop使用来处理线程里面的事件和消息
2.runloop和线程的关系:每个线程如果想继续运行,不被释放,就必须有一个runloop不停地调用,用来处理线程里面的各个事件和消息。
3.主线程默认开启runloop,也就是这个runloop才能保证我们的程序正常运行,子线程是默认没有开启runloop.
26.runloop的mode是做什么的?有几种model?
系统默认注册了5个model:
1. kCFRunLoopDefaultMode:这是App默认的Model,通常主线程就是在这个Model下运行的。
2. UITrackingRunLoopMode:这个是界面跟踪的Model,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Model影响。
3. UIInitializationRunLoopMode:这个是刚启动App时进入的第一个Model,启动完成就不再使用。
4. GSEventReceiveRunLoopMode:这个接受系统事件内部的Model,没什么实际作用。
5.kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。
注意:iOS对以上五中model进行了封装,NSDefaultRunLoopMode,NSRunLoopCommonModes
27.为什么把NSTime对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环后,滑动ScrollView的时候NSTime却不动了。
NSTime对象是在NSDefaultRunLoopMode下面调用消息的,但是当我们滑动scrollView的时候,NSDefaultRunLoopMode模式就自动切换到UITrackingRunLoopMode模式下面,不可以继续响应NSTime发送的消息,如果想在滑动scrollView的情况下还能调用NSTime的消息,我们可以把NSrunloop的模式更改为NSRunLoopCommonModes。
28.苹果是如何实现Autorelease Pool的?
Autorelease Pool作用:缓存池,避免我们经常写relase的一种方式,其实就是延迟relase,将创建的对象添加到autorelease Pool中,等到autorelease Pool作用于结束,会将所有的对象的引用计数器-1
28.类结构中isa指针(对象的isa,类对象的isa,元类的isa都要说)
在oc中,类也是对象,属于元类
1.对象的isa指针指向所属的类
2.类的isa指针指向了所属的元类
3.元类的isa指针指向了根元类,根元类指向了自己
29.类的方法和实例方法有什么区别
1.调用的方式不同,类的方法必须使用类调用,在方法里面不能调用属性,类方法里面也必须调用类方法,存储在元类结构体的methodLists里面
2.实例方法必须使用实例对象调用,可以在实例方法里面使用属性,实例方法也必须调用实例对象,存储在类结构体的methodLists里面
30.分类你那个做什么?内部怎么实现?会什么会覆盖原来的方法?
category:我们可以给类或者系统类添加实例方法。我们添加的方法会被动态添加到类结构里面的methodsList方法列表里面,
category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休^_^,殊不知后面可能还有一样名字的方法。
31.运行时能增加成员变量吗?能增加属性吗?如果能,怎么增加,为什么?
运行时可以添加属性,但是必须实现它的getter和setter方法,但是不能添加带下划线的成员变量,可以通过runtime添加。
32.objc向一个nil对象发送消息会发生什么?
1.如果一个方法返回值是一个对象,那么发送给nil的消息将返回0(nil),
例如:Person * motherInlaw = [ aPerson spouse] mother];
如果spouse对象为nil,那么发送给nil的消息mother也将返回nil。
2.如果方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double 或者long long的整型标量,发送给nil的消息将返回0。
3.如果方法返回值为结构体,正如在《Mac OS X ABI 函数调用指南》,发送给nil的消息将返回0。结构体中各个字段的值将都是0。其他的结构体数据类型将不是用0填充的。
4.如果方法的返回值不是上述提到的几种情况,那么发送给nil的消息的返回值将是未定义的。
33.UITableview的优化方法(缓存高度,异步绘制,减少层级,hide,避免离屏渲染)
缓存高度:当我们创建的时候frame模型的时候,计算出来cell的高度,我们可以将cell的高度缓存到字典里面,以cell的indexpath和Identifier作为key。
hide:个人理解应该是hidden吧,把可能会用到的控件都创建出来,根据不同的情况去隐藏或者显示出来。
避免离屏渲染:只要不是同时使用边框/边框颜色以及圆角的时候,都可以使用layer直接设置。不会造成离屏渲染。