OC基础知识总结三

1、OC的理解与特性

OC有动态特性,包含动态类型、动态绑定、动态加载。所谓动态都是在运行时才会去确定。
OC面向对象,具有面向对象的特性:封装继承多态

2、简述内存管理基本原则

遵循谁创建谁释放谁是用谁管理的原则!ARC中内存由系统自动释放。
但是也要注意循环引用导致的内存无法释放的问题。Block与self相互持有导致无法释放。

3、如何理解MVC设计模式、MVVM模式

Model view controller 各司其职,
M存储、定义和操作数据 view展示数据给用户并和用户进行交互
C是M、V的协调者,C把M中的数据拿过来给View用,所以C可以与M&V直接通信,但是view不能和C直接通信。此时就可以用代理。
有数据更新时M就需要和C通信,此时可以使用通知和KVO。
M数据更新通知C,C将数据传给V加载。

4、Objective-C 中是否支持垃圾回收机制?

OC的ARC自动回收内存。

5、简述类目category优点和缺点。

1、扩展类的方法、分散类的实现
2、方法的优先级高于原类,如果命名相同,方法会覆盖原类的方法。
3、Extension扩展也属于类别。既可以添加属性也可以添加方法,属于匿名类目,但是可不一样奥!

6、weak与unowned的使用区别

1、weak修饰的对象会随着原对象的销毁而销毁,指针置nil,OC想一个nil发送消息没有问题。
2、unowned修饰的对象随着原对象的销毁而销毁,但是指针还是指向原来的内存地址,当再次访问的时候会产生野指针错误。

7、循环引用的产生原因,以及解决方法。

A,B两个对象相互引用对方作为自己的成员变量,对象A依赖B的销毁,对象B依赖A的销毁,结果谁都没法释放。
使用weakself弱引用,不持有新对象,不释放旧对象。这样就断开了他们的强引用,就不会导致循环引用了。

8、键值编码(KVC)和键值观察(KVO)

a、键值编码是一种间接访问对象的属性,使用字符串来标识属性,而非通过调用存储方法。直接或通过实例变量访问的机制。非对象类型的变量将被自动封装或者解封装成对象,很多情况下会简化程序代码。
缺点:一旦使用KVC,编译器将无法检查错误,也就是不会对设置的键、键路径进行错误检查,且效率要低,因为KVC编码要先解析字符串,然后在设置或者访问对象的实例变量。

b、KVO键值观察:使对象能够获取到其他属性的通知,极大地简化了代码。
另外,实现KVO键值观察模式,被观察者对象必须使用KVC键值编码来修改它的实例变量,这样才能被观察者观察到。所以!!!!!KVC是KVO的基础。

9、KVC机制通过key找到value的原理。

a、当通过KVC调用对象时,比如::[self valueForKey:@”someKey”]
首先,程序会查找对象是否带有somekey的方法,如果没找到会查找是否带有somekey的实例变量,如果再没找到,程序会继续调用 -(id) valueForUndefinedKey:方法,如果这个方法还没有被实现的话,程序会抛出一个NSUndefinedKeyException异常错误。

b、设计valueForUndefinedKey:方法的主要目的是当你使用-(id)valueForKey方法从对象中请求值时,对象能够在错误发生前,有最后的机会响应这个请求。

10、在 Objective-C 中如何实现 KVO

1、设置观察者观察对象的属性
2、接收变更通知。
3、移除观察者。

11、代理的作用

实现对象之间的通信,解除了对象之间的耦合性。
代理的属性使用weak修饰的原因就是为了防止循环引用。

12、NSNotification、Block、Delegate和KVO的区别。

NSNotification、KVO都是一对多。
Delegate、Block都是一对一。
使用场景分析:
NSNotification使用的场景远远多于其他,可以传参数、也可以监控系统等事件。
KVO只能监控对象的属性。
Delegate效率比通知高;
Block更加简洁;

13、static、self、super关键字的作用

static 只开辟一份内存,调用的时候只是调动这个内存中的数据,所以更加更加高效。
self 当前消息的接受者
super 向父类发送消息

14、当我们调用一个静态方法时,需要对对象进行 release 吗?

不需要,系统会自动将静态方法中的类放到自动释放池中,在适当的时机释放。

15、当我们释放我们的对象时,为什么需要调用[super dealloc]方法,它的位置又是如何的呢?

先释放子类拥有的实例,后释放父类拥有的实例。
子类的某些实例继承了父类,所以要释放父类拥有的实例,其实也是释放掉本类的方法。

16、#include与#import的区别,#import 与@class 的区别

#include是C语言的类的引用 #import是OC的类的引用
#import 是导入的类,确保头文件只被导入一次。

@class表明只定义了类的名称,类的具体的行为是未知的。
@class比#import的编译效率更高。
@class是为了防止引用死锁。

17、@public、@protected、@private 它们的含义与作用

  • public:对象的实例变量的作用域在任意地方都可以被访问
  • @protected:对象的实例变量作用域在本类和子类都可以被访问
  • @private:实例变量的作用域只能在本类(自身)中访问 .


18、isMemberOfClass 和 isKindOfClass 联系与区别

联系:都是判断对象是否是某个类的成员
1、isKindOfClass用来判断某个对象是否属于某个类,或者是属于某个派生类。
2、isMemberOfClass用来判断某个对象是否为当前类的实例。
https://juejin.im/entry/599c13bc6fb9a0248926a77d
3、isMemberOfClass不能检测任何的类都是基于NSObject类这一事实,而isKindOfClass可以。

19、iOS 开发中数据持久性有哪几种?

1、NSUserDefault
2、数据库
3、CoreData
4、解归档
5、存储到磁盘
CoreData提供的是对象-关系映射功能,也就是说,CoreData可以将Objective-C对象转换成数据,保存到SQL中,然后将保存后的数据还原成OC对象。

20、自动释放池工作原理

对象的作用域就在自动释放池里面,自动释放池一旦执行完毕就会被释放掉。
它只是延缓对象的释放。一旦出了作用域就会被释放掉。

21、在某个方法中 self.name = _name,name = _name 它们有区别吗,为什么?

self.name会导致引用计数+1,_name引用计数不加1。
self方法实际上是用了get和set方法进行调用,下划线方法是直接对变量进行操作。

22、解释self = [super init]方法

初始化父类方法,防错机制。为什么防错呢?
这里是担心父类初始化失败,如果初始化一个对象失败,就会返回nil,当返回nil的时候self = [super init]测试的主体就不会再继续执行。如果不这样做,你可能会操作一个不可用的对象,它的行为是不可预测的,最终可能会导致你的程序崩溃。
知识点解析:https://www.jianshu.com/p/9b36e1b636d8

23、定义属性时,什么时候用 assign、retain、copy 以及它们的之间的区别。

1、基本类型assign
2、对象retain
3、Block、NSArray、NSString等copy

24、堆和栈的区别

进出方式:堆FIFO 栈FILO
堆:
1、堆区内存一般是由开发者自己分配并释放的。
2、当我们使用alloc来分配内存是分配的内存就是在堆上。由于我们现在使用ARC,所以现在堆区的分配和释放基本不需要我们的管理。
3、堆区是向高地址扩展一块非连续区域。
栈:
1、栈区是由编译器自动分配并释放的。
2、栈区存放的是函数的参数以及自动变量。栈是向低地址扩展的一块连续的内存区域。
3、分配在栈上的变量,当函数的作用域结束,系统就会销毁变量。

25、UITableViewCell上有个UILabel,显示NSTimer实现的秒表时间,手指滚动cell过程中,label是否刷新,为什么?

会暂停,RunLoop调用defaultModel和currentRunLoopModel,一旦出现UI交互运行时模式会跳转到UITrackingRunLoopModel,由于类型不相同导致定时器失效,所以要换成currentRunLoopModel才有用。

26、对于单元格重用的理解

系统把超出的单元格添加到重用队列中,等待被重用,当有新单元从屏幕外滑到屏幕内时,重用队列中若有重用的单元格就直接重用,否则重新创建一个。
分配的cell的个数为屏幕最大显示数,当有新的cell进入屏幕时,会随机调用已经滚出屏幕的Cell所占的内存,这就是Cell的重用。

27、有a、b、c、d 4个异步请求,如何判断a、b、c、d都完成执行?如果需要a、b、c、d顺序执行,该如何实现?

判断完成执行:使用GCD的串行异步队列,GCD会有执行完成通知。dispatch_group_notify
顺序执行:设置依赖、串行执行都可以

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

每个APP都至少有一个进程,一个进程可以包含多个线程。

29、SDWebImage原理

调用类别的方法:
* 从内存中(字典)找图片(当这个图片在本次程序加载过),找到直接使用;

* 从沙盒中找,找到直接使用,缓存到内存。

* 从网络上获取,使用,缓存到内存,缓存到沙盒。


30、RunLoop的理解。

分线程默认不运行RunLoop,执行完毕就归还资源。分线程只执行一次。
实际上,只有主线程的RunLoop是开启的,子线程的RunLoop默认不启动,需要手动开启。
1、RunLoop和线程的关系:
一个线程对应一个RunLoop,主线程的RunLoop默认启动,子线程的RunLoop需要手动启动(调用run方法)

2、RunLoop定义:
和字面意思一样:运行着的循环(事件处理的循环),作用:接受循环事件和安排线程的工作。目的:让线程在有任务的时候忙于工作,而没任务的时候处于休眠状态。

3、runloop的作用:
* 3.1、保持程序的持续运行(如:程序一启动就会开启一个主线程(中的 runloop 是自动创建并运行),runloop 保证主线程不会被销毁,也就保证了程序的持续运行)。
* 3.2、处理App中的各种事件(如:touches 触摸事件、NSTimer 定时器事件、Selector事件(选择器 performSelector))。
* 3.3、节省CPU资源,提高程序性能(有事情就做事情,没事情就休息 (其资源释放))。
* 3.4、负责渲染屏幕上的所有UI。
保持程序的持续运行,处理APP的各种事件,节省CPU资源(懒加载,有事做,没事挂起来)、渲染屏幕上的UI。

大神讲解,多多研习:https://juejin.im/entry/599c13bc6fb9a0248926a77d

31、苹果是如何实现Autorelease Pool的?

Autorelease Pool缓存池,其实就是延时release,将创建的对象添加到Autorelease Pool中,等到其作用域结束的时候会将里面所有对象自动释放掉。

32、objc在向一个对象发送消息的时候,发生了什么?

根据对象的isa指针找到对象所属的类,查询类的methodLists方法函数列表,如果没找到,会寻找父类的methodLists方法列表里查询,最终找到SEL,根据对象id和SEL确认IMP(指针函数),再发送消息。

  • objc中向一个nil对象发送消息将会发生什么?
    如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。

需要补充的知识点:
isa、IMP、SEL 以及runtime的方法列表,防止方法找不到的机制。(重新再过一遍,总是忘掉,只记个大概)

搞不清楚runtime的专有名词是无法深入理解runtime的。
objc_msgSend(receiver, selector)
a.reveiver:接收消息的对象,
b.selector是执行消息的函数体名称,是C字符串,函数也就是方法。

c.objc_selector透明的数据结构,可以理解为C string
d.SEL:指向C string的指针。
e.typedef struct objc_selector *SEL;

f.typedef struct objc_object *id; id指向一个类的实例对象

IMP指向实际执行函数体的函数指针。

34、runtime详细解释

meta-class (元类)存储着一个类的所有类方法。
1、向一个对象发送消息时,runtime会在这个对象所属的类的方法列表中查找方法。
2、向一个类发送消息时,runtime会在这个类的meta-class(元类)的方法列表中查找
meta-class是一个类,也可以向它发送消息,那么他的isa又指向什么呢?为了让这种结构无限延伸下去,OC设计者让所有的meta-class的isa指向基类(NSObject)的meta-class,而基类的meta-class指针指向自己。
isa:是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。

详细Demo地址:https://github.com/CoderLN/Runtime-RunLoop

猜你喜欢

转载自blog.csdn.net/xiaoxiaocode/article/details/80354907