iOS一些容易被忽略的基础面试题
什么是对象 ,OC中的对象有哪些?
对象是类的实例;是通过一个类创建出来的实例,一般称之为实例对象;OC中的常见对象有实例对象、类对象、元类对象;
什么是类?什么是元类?类对象和类,元类对象和元类有什么区别?
类: 是面向对象程序设计(OOP,Object-Oriented Programming)实现信息封装的基础。类是一种用户定义的引用数据类型,也称类类型。每个类包含数据说明和一组操作数据或传递消息的函数。类的实例称为对象
元类:以类作为其实例的类;
类对象:类本身也是一个对象,所以就有类对象;类对象可以通过实例对象的ISA指针获得
元类对象:元类本身也是一个对象,所以就有元类对象;元类对象可以通过类对象的ISA指针获得
区分二者:
- 类、元类是面向对象编程中的一种类型
- 类对象、元类对象是一种对象
什么是分类?
分类也是一个类,其底层结构和类稍有不同;给分类添加的方法会在运行时合并到原有类的方法列表(二维数组)中
分类多用来给类做扩展使用;在OC开发中应用广泛
什么是类扩展?
类扩展:用来给类扩充私有属性、方法
什么是数组?
数组可表示为占用一块连续的内存空间用来存储元素的数据结构;OC中的数组有可变和不可变两种;可变数组做了优化利用环形缓冲区技术
提高增删改查时的性能
什么是字典?
字典以键值的形式存储数据,底层实现是哈希表;OC对象作为字典的Key需要遵守NSCopying协议并且实现hash和isEqual两个方法。比如:NSNumber、NSArray 、NSDictionary、自定义OC对象 都可以作为key
什么是集合?
集合是一种用来存储数据的数据结构,内部存储的数据时无序的,其他和数组相同
OC语法有哪些?
OC中的语法有点语法.
,这里的点一般转化为setter、getter方法调用
什么是Method?
Method是method_t的结构体,是对一个方法的描述:struct method_t{
SEL name; //函数名/方法名
const char *types;//编码(返回值类型、参数类型)
IMP imp; //指向函数的指针(函数地址)
}
什么是内敛函数?
- 内联函数基本概念
在c++中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。内联函数具有普通函数的所有行为。唯一不同之处在于内联函数会在适当的地方像预定义宏一样展开,所以不需要函数调用的开销。因此应该不使用宏,使用内联函数。
在普通函数(非成员函数)函数前面加上inline关键字使之成为内联函数。但是必须注意必须函数体和声明结合在一起,否则编译器将它作为普通函数来对待。
inline void func(int a);
以上写法没有任何效果,仅仅是声明函数,应该如下方式来做:
inline int func(int a){return ++;}
注意: 编译器将会检查函数参数列表使用是否正确,并返回值(进行必要的转换)。
这些事预处理器无法完成的。
内联函数的确占用空间,但是内联函数相对于普通函数的优势只是省去了函数调用时候的压栈,跳转,返回的开销。我们可以理解为内联函数是以空间换时间。
类内部的内联函数
为了定义内联函数,通常必须在函数定义前面放一个inline关键字。但是在类内部定义内联函数时并不是必须的。任何在类内部定义的函数自动成为内联函数。
什么是构造函数?
在一个类中定义一个和类名相同的函数,这个函数就是构造函数
面向对象的设计原则是什么 ?
单一责任原则
、开闭原则
、接口隔离原则
、依赖倒置原则
、里式替换原则
、迪米特原则
- 单一责任原则
一个类只负责一件事情,CALayer只负责动画和视图的显示,UIView只负责事件的传递、事件的响应 - 开闭原则
对修改关闭,对扩展开放; 要考虑API的后续扩展,而不是在原有基础上来回修改 - 接口隔离原则
使用多协议的方式来定义接口,而不是一个臃肿的协议;比如delagate, datesource - 依赖倒置原则
抽象不应该依赖具体实现,具体实现依赖于抽象 - 里式替换原则
父类和子类无缝衔接,且原有功能不受影响;比如:KVO, 用完就走不留痕迹 - 迪米特原则
高内聚,低耦合
面向对象语言的三大特性是什么 ?
封装、继承、多态
OC的继承体系
ARC环境修饰OC对象用strong、copy、weak,修饰基本数据类型用assign;
静态变量用static, 修饰为常量用const
常用设计模式?
设计模式分为四类:结构型模式、创建型模式、行为型模式、软件设计原则
常用的结构型模式有:代理、装饰
常用的创建型模式有:单利、工厂
常用的行为型模式有:观察者、发布订阅模式
代理:是一种消息传递方式,一个完整的代理模式包括:委托对象、代理对象和协议。
- 请代理三部曲:
1 定义代理协议
2 声明delegate对象
3 调用代理方法 - 当别人代理的三部曲
1 遵循协议
2 设置代理
3 实现方法
装饰:动态地给一个类添加一些额外的职责;Category 就是实现了装饰的设计模式;Category是一个特殊的类,通过它可以给类添加方法的接口与实现;
观察者:包含通知和KVO
单利:单:唯一,例:实例;即唯一的一个实例,该实例自创建开始到程序退出由系统自动释放;单利常被当做共有类使用;
系统常见单利类
UIApplication(应用程序实例类)
NSNotificationCenter(消息中心类)
NSFileManager(文件管理类)
NSUserDefaults(应用程序设置)
NSURLCache(请求缓存类)
NSHTTPCookieStorage(应用程序cookies池)
工厂模式:分为简单工厂模式、工厂模式、抽象工厂模式
简单工厂模式:简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
工厂模式:抽象了工厂接口的具体产品,应用程序的调用不同工厂创建不同产品对象。(抽象产品)
抽象工厂模式:在工厂模式的基础上抽象了工厂,应用程序调用抽象的工厂发发创建不同产品对象。(抽象产品+抽象工厂)
懒加载:把初始化逻辑通过重写的方式封装起来,到需要时直接调用的方式
懒加载的优点
- 相对来说,如果代码量不是很多,可读性略强
- 相对来说,防止为nil,减少了后续使用时安全检查的后顾之忧
- 使用适当,可节省内存资源
- 一定程度上,节省了某一个期间内的时间
- 使用得当,优化性能,提高用户体验
懒加载的缺点 - 使用太泛滥,导致可读性变差
- 使用不得当,可能会造成死循环,导致crash
- 代码量增多(每增加一个懒加载,代码会平均多出3-4行)
什么时候使用懒加载?
一般情况下,不需要使用懒加载,懒加载未必能增强可读性、独立性,滥用反而让可读性适得其反。简言之,就是在逻辑上,觉得现在不需要加载,而在后面某一时间段内可能会加载,就可以考虑懒加载
生产者消费者:
在编码中,有时会遇到一个模块产生数据,另外一个模块处理数据的情况,不论是为了模块间的结偶或是并发处理还是忙闲不均,我们都会在产生和处理数据的模块之间放置缓存区,作为生产和处理数据的仓库。以上的模型就是生产者消费者模型
生产者-消费者
中介者:
- 中介者模式又叫做调停者模式,其实就是中间人或者调停者的意思
- 概念:中介者模式(Mediator),用一个中介者对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可 以独立地改变他们之间的交互
- UINavigationViewController就是属于一个中介者
- 中介者模式的优缺点
中介者模式很容易在系统中应用,也很容易在系统中误用。当系统出现了多对多交互复杂的对象群时,不要急于使用中介者模式,而要先反思你在系统上设计是否合理。
优点就是集中控制,减少了对象之间的耦合度。缺点就是太过于集中 - 应用场景
对象间的交互虽定义明确然而非常复杂,导致一组对象彼此相互依赖而且难以理解。
因为对象引用了许多其他对象并与其通信,导致对象难以复用。
想要定制一个分布在多个类中的逻辑或者行为,又不想生成太多子类
发布订阅模式
其实基本的设计模式中并没有发布订阅模式,上面也说了,他只是观察者模式的一个别称。但是经过时间的沉淀,似乎他已经强大了起来,已经独立于观察者模式,成为另外一种不同的设计模式。
在现在的发布订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。
举一个例子,你在微博上关注了A,同时其他很多人也关注了A,那么当A发布动态的时候,微博就会为你们推送这条动态。A就是发布者,你是订阅者,微博就是调度中心,你和A是没有直接的消息往来的,全是通过微博来协调的(你的关注,A的发布动态
观察者模式和发布订阅模式有什么区别?
观察者模式: 观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。
发布订阅模式: 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Topic),当发布者(Publisher)发布该事件(Publish topic)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。常见的是用协议的方式来做
- 观察者模式是不是发布订阅模式
网上关于这个问题的回答,出现了两极分化,有认为发布订阅模式就是观察者模式的,也有认为观察者模式和发布订阅模式是真不一样的。
其实我不知道发布订阅模式是不是观察者模式,就像我不知道辨别模式的关键是设计意图还是设计结构(理念),虽然《JavaScript设计模式与开发实践》一书中说了分辨模式的关键是意图而不是结构。
如果以结构来分辨模式,发布订阅模式相比观察者模式多了一个中间件订阅器,所以发布订阅模式是不同于观察者模式的;如果以意图来分辨模式,他们都是实现了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新,那么他们就是同一种模式,发布订阅模式是在观察者模式的基础上做的优化升级。
不过,不管他们是不是同一个设计模式,他们的实现方式确实有差别,我们在使用的时候应该根据场景来判断选择哪个
block
- block是封装了函数调用和函数调用环境的OC对象,block分为3种类型:NSGlobalBlock、NSStackBlock、NSMallocBlock; 其都继承自NSBlock,NSBlock 继承自NSObject;
- block使用需注意循环引用问题;一般需要使用强弱引用、__block来解决问题
- block声明为属性时需要使用copy或strong来修饰;因为block最初是被分配在栈空间,内存由系统管理;但一般使用block是需要在运行时的某一个时机使用,所以需要开发者自己管理block的内存,使用copy和strong修饰会把block的内存复制到堆空间,这样就达到了自己管理内存的目的
为什么block一开始的内存会被分配在栈空间?
block使用会有两种情况:局部变量
、typedef
声明
局部变量申请的内存肯定在栈空间
对象的本质?
对象的本质是结构体;
内存分配原理:以16个字节为单位且遵循了内存对齐原则向堆内存申请内存空间
isa指针?
isa指针是OC对象的第一个成员变量;isa是一个联合体结构,通过位域来存储数据;
isa最重要的作用是用于消息发送;
位域宏定义(真机环境arm64)
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; 拿二进制的1位来存储 \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
OC的内存管理原则 ?
OC中内存管理是通过引用计数管理实现的,当一个对象的引用计数为0时就会进入释放流程;ARC利用LLVM编译器动态的在合适的位置添加内存管理代码的方式帮助开发者管理内存,同时又通过runtime管理weak修饰的弱引用表;基本实现了不用开发者关心内存问题就可以进行开发;
- block、定时器时需要注意循环引用问题
- 声明属性时需要注意强弱引用的使用
多线程?
即 multithreading
, 是指从软件或者硬件上实现多个线程并发执行的技术。
具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。
多线程的调度原理可以认为是:时间片轮转调度算法,每个线程都会分配一个时间片然后大家轮着做任务,多线程执行时会快速切换时间片来完成多线程任务的执行;其实操作系统对进程、线程都是按照这种调度逻辑实现的
程序、进程、线程、例程、协程是什么?
程序:全称 计算机程序(Computer Program),是一组计算机能识别和执行的指令,又称计算机软件
是指为了得到某种结果而可以由计算机等具有信息处理能力的装置执行的代码化指令序列,用某些程序设计语言编写,如:C、C++、OC等;它运行于电子计算机上进程:是计算机中的程序关于某数据集合上的一次运行活动;是独立运行、独立分配资源和独立接受调度的基本单位;是操作系统结构的基础
在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体
线程:线程是计算机调度的最小单位,用来处理不同的任务;
例程:即函数,一个函数就可以看做是一个例程
协程:利用单线程执行多任务的技术解决方案,性能上避免线程间切换要优于线程调度;是线程的更小拆分,又称之为“微线程”,是一种用户太的轻量级线程;
和线程的区别:
线程是系统级别的,它们由操作系统调度;同时是可被调度的最小单位;
协程则是程序级别的,由程序员根据需要自己调度
子程序:函数
在一个线程中会有很多子程序,在子程序执行过程中可以中断去执行别的子程序,而别的子程序也可以中断回来继续执行之前的子程序,这个过程及称为协程。也就是说在同一线程内一段代码在执行过程中会中断然后跳转执行别的代码,接着在之前中断的地方继续开始执行,类似于yield操作
实例的生命周期?
- alloc、new、copy、mutableCopy
- 引用计数变化
- 引用计数为0
- dealloc
作者:9523_it
链接:https://www.jianshu.com/p/7646a2e8165f