技术点

2019-11-06  本文已影响0人  年轻就要活出样

1、oc中 load 和initialize 方法的异同?

连接

load 方法:
initialize方法:
load和initialize之间的区别如下:

追问1、Category 中有 load 方法吗? load 方法是什么时候调用的? load 方法能继承吗?

总结

Category 是在运行时时期将方法, 协议, 属性插入到主类中, 对于load方法并没有做特殊操作, 所以 load方法跟其它方法一样, 会 "覆盖" 主类的方法;
同时在运行时时期, 又会递归调用 load方法, 而且是通过函数指针直接调用的, 没有走消息转发的机制, 这就导致只要类实现了load 方法就会被调用.

2、直接用UILabel和自己用DrawRect画UILabel,哪个性能好?为什么?哪个占用的内存少?为什么?

  直接使用UILabel性能更好。
  原因:重写了-drawRect:方法,-drawRect :方法就会自动调用,生成一张寄宿图,然后内容就会缓存起来,等待下次你调用-setNeedsDisplay时再进行更新。
 &emsp直接使用UILabel内存更占优。

3、项目采用64位,为什么要用64位?怎么修改成64位?i386是什么?他们有什么关系?

1、修改组织文件,增加arm64
2、i386是针对intel通用微处理器32位处理器
x86_64是针对x86架构的64位处理器
模拟器32位处理器测试需要i386架构,
模拟器64位处理器测试需要x86_64架构,
真机32位处理器需要armv7,或者armv7s架构,
真机64位处理器需要arm64架构。

4、iOS的应用程序有几种状态?追问,退到后台代码是否可以执行?双击home键,代码是否可以执行?

1、Not running 未运行:应用还没有启动,或者应用正在运行但是途中被系统停止。
2、Inactive 未激活:当前应用正在前台运行,但是并不接收事件。
3、Active 激活:当前应用正在前台运行,并且接收事件。
4、Suspended 挂起:应用处在后台,并且已停止执行代码。
5、Background 后台:应用处在后台,并且还在执行代码

5、TCP与UDP的区别

1、TCP面向连接(需要建立三次握手协议);UDP是无连接的,即发送数据之前不需要建立连接。
2、TCP提供可靠的服务,保证数据无差错,不丢失,不重复,且按序到达。UDP只管发,不管收不收的到
3、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信.

6、NSString占用多少内存?

8个字节(通过sizeof() 函数计算)
OC 的对象类型 NSString、NSArray、NSInteger、CGFloat、NSDictionary 占用内存字节数为 8
基本数据类型 Int、Float 占用字节数 为 4

7、使用SDWebImage和YYImage下载高分辨率图,导致内存暴增的解决办法

http://www.cocoachina.com/ios/20160920/17602.html

8、谈谈对mrc和arc的理解?

https://www.jianshu.com/p/48665652e4e4

9、对于atomic、nonatomic、assign、retain、copy、strong、weak的简单理解?

1)atomic设置成员变量的@property属性时,atomic是默认值,提供多线程安全
2)nonatomic 若是禁止多线程,实现变量保护,提高性能,可设置成员变量的@property属性为nonatomic
3)assign 此标记说明设置器直接进行赋值,assign是默认值,针对基础数据类型(NSInteger, CGFloat)和C数据类型(int, float, double, char)等。 注意:MRC 下 assign 为属性的默认修饰符,无论是简单的数据类型,还是指向对象的指针
4)retain 定retain会在赋值时唤醒传入值的retain消息,对其他的NSObject和其子类对参数先进行release旧值,再retain新值。释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的引用计数为 1
5)copy 对NSString 它指出在赋值时使用传入值的一份拷贝。
6)strong 为 ARC 下属性的默认内存管理语义语义等同于 retain。变量的所有权修饰符为 __strong。被赋值时会持有对象,阻止对象被释放。
7)atomic:对于对象的默认属性,就是setter/getter生成的方法是一个原子操作。如果有多个线程同时调用setter的话,不会出现某一个线程执行setter全部语句之前,另一个线程开始执行setter的情况,相关于方法头尾加了锁一样。
8)nonatomic:不保证setter/getter的原子性,多线程情况下数据可能会有问题。效率高

strong与weak是由ARC新引入的对象变量属性
ARC引入了新的对象的新生命周期限定,即零弱引用。如果零弱引用指向的对象被deallocated的话,零弱引用的对象会被自动设置为nil

追问 1、

 NSObject *obj = [NSObject new];
    NSLog(@"1====%lu",(unsigned long)[obj retainCount]);
    
    NSObject *obj1 = obj;
    NSLog(@"2====%lu",(unsigned long)[obj retainCount]);

    NSObject *obj2 = [obj retain];
    NSLog(@"3====%lu",(unsigned long)[obj retainCount]);

打印结果:
2018-12-12 14:24:07.854338+0800 排序算法[3287:196458] 1====1
2018-12-12 14:24:07.854483+0800 排序算法[3287:196458] 2====2
2018-12-12 14:24:11.305737+0800 排序算法[3287:196458] 3====3

10、week 原理?

https://blog.csdn.net/jyt199011302/article/details/78368651

11、简述下block的实现?

https://www.aliyun.com/jiaocheng/352836.html

12、描述下IM系统如何保证消息不丢

https://blog.csdn.net/u014105540/article/details/80539507

13、数据源同步解决方案?

14、UIView 和 CALayer 关系?

15、UI视图显示工作原理?

16、离屏渲染?

追问1、tableview性能优化方案(拓展)

追问2、相应者链

屏幕快照 2019-06-26 下午2.09.31.png

17、分类和扩展

特点:
1、运行时决议(分类是在运行时才将分类中添加的内容真实的添加到宿主类中)
2、可为系统类添加方法。

特点:
1、编译时决议(编译的时候已经把我们扩展的内容添加到宿主类中了)
2、只以声明的形式存在,多数情况寄生于宿主类的.m文件中
3、不能为系统类添加扩展

追问 1、能否为分类增加成员变量?

可以通过关联对象的方式来达到分类可以添加成员变量的效果。

18、深copy 和浅copy

什么情况下是深拷贝?什么情况是浅拷贝?

可变对象:copy 或者 mutableCopy 操作都是深拷贝。
不可变对象:mutableCopy 操作是深拷贝。
不可变对象: copy 操作是浅拷贝。

copy操作拷贝的结果对象都是不可变对象;mutableCopy操作拷贝的结果对象都是可变对象。

面试题: @property (copy) NSMutableArray *array ; 这样声明一个成员属性会导致什么问题?

追问 1、 如何让自己写的对象具有拷贝功能?

 - (id)copyWithZone:(NSZone *)zone;
 - (id)mutableCopyWithZone:(NSZone *)zone;

19、请简述分类的实现原理。

分类:分类是有运行时决议的;不同分类中含有同名分类方法,谁最终生效,取决于谁最后参与编译;如果分类中添加的方法是宿主类中的方法,则分类方法会覆盖同名的宿主类方法。

20、KVO 的实现原理是怎样的?

KVO 是系统关于观察者模式的实现;kvo运用的isa混写技术来动态运行时去为某个类添加一个子类,然后重写setting 方法,setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象属性值的更改情况。(同时将原有类的isa指针指向新创建的类上面)
set方法实现内部会顺序调用willChangeValueForKey方法、原来的setter方法实现、didChangeValueForKey方法,而didChangeValueForKey方法内部又会调用监听器的observeValueForKeyPath:ofObject:change:context:监听方法

assign:

- (void)setObj:(id)obj{
  if(_obj != obj){
    [_obj release];
    _obj = [obj retain];
   }
}

23、[obj foo] 和 objc_msgSend()函数之间有什么关系?

objc_msgSend()是[obj foo]的具体实现。
在runtime中,objc_msgSend()是一个c函数,[obj foo]会被翻译成这样的形式objc_msgSend(obj, foo)。

追问1、 _objc_msgForward函数是做什么的?直接调用它将会发生什么?

答案:_objc_msgForward是IMP类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发
直接调用_objc_msgForward是非常危险
的事,这是把双刃刀,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事。

24、runtime 如何通过Selector 找到对应的IMP地址的?

isa包括:指针型的isa(isa的值代表Class的地址)和非指针型的isa(isa的值的一部分代表Class的地址)

追问2、isa指针的指向?

无论subclass、supclass还是rootclass的meta元类对象isa指针都指向根元类对象。注意:根元类对象它的superclass指针指向根类对象

屏幕快照 2019-07-04 下午3.39.40.png

追问5、我们调用的一个类方法没有对应的实现,但是有同名的实例方法的实现,这时候会不会崩溃?会不会产生实际的调用?

由于根元类的superclass对象指向根类对象, 当我们在跟类对象的类方法列表没有查找到时,就会到根类对象的实例方法列表中查找,如果有同名方法,就会执行同名方法的实例方法调用。

25、能否向编译后的类中添加实例变量?

runtime 动态给类添加方法,是在编译之前。编译之后是没办法在给类动态添加方法的。

26、你都了解知道哪些设计原则,以及你对这些设计原则都有哪些理解?

27、多线程编程技术的优缺点比较

28、GCD中一些系统提供的常用dispatch方法?

追问1、如何在不使用GCD和NSOperation、NSThread的情况下,实现异步线程?

这个方法有一个thread参数是指定执行的线程,但是很奇怪当我使用自己创建的线程 [[NSThread alloc] init];时,并不会执行test方法,只有当使用[NSThread currentThread]时才会执行:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self performSelector:@selector(tests) onThread:[NSThread currentThread] withObject:nil waitUntilDone:NO];
});

追问2、performSelector如何进行多值传输?

{
    NSNumber *age = [NSNumber numberWithInt:20];
    NSString *name = @"李周";
    NSString *gender = @"女";
    NSArray *friends = @[@"谢华华",@"亚呼呼"];

    SEL selector = NSSelectorFromString(@"getAge:name:gender:friends:");
    NSArray *array = @[age,name,gender,friends];

    ((void(*)(id,SEL,NSNumber*,NSString*,NSString*,NSArray*)) objc_msgSend)(self,selector,age,name,gender,friends);

}

- (void)getAge:(NSNumber *)age name:(NSString *)name gender:(NSString *)gender friends:(NSArray *)friends
{
    NSLog(@"%d----%@---%@---%@",[age intValue],name,gender,friends[0]);
}

-(id)performSelector:(SEL)aSelector withObject:(NSArray *)object
{
    //获得方法签名
    NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
    
    if (signature == nil) {
        return nil;
    }
    
    //使用NSInvocation进行参数的封装
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self;
    invocation.selector = aSelector;
    
    //减去 self _cmd
    NSInteger paramtersCount = signature.numberOfArguments - 2;
    paramtersCount = MIN(object.count, paramtersCount); 
    
    for (int i = 0; i < paramtersCount; i++) {
        id obj = object[i];
        
        if ([obj isKindOfClass:[NSNull class]]) continue;
        [invocation setArgument:&obj atIndex:i+2];
    }
    
    [invocation invoke];
    
    id returnValue = nil;
    if (signature.methodReturnLength > 0) { //如果有返回值的话,才需要去获得返回值
        [invocation getReturnValue:&returnValue];
    }
    
    return returnValue;
    
}

    NSNumber *age = [NSNumber numberWithInt:20];
    NSString *name = @"李周";
    NSString *gender = @"女";
    NSArray *friends = @[@"谢华华",@"亚呼呼"];
 SEL selector = NSSelectorFromString(@"getAge:name:gender:friends:");
    NSArray *array = @[age,name,gender,friends];
    
    [self performSelector:selector withObject:array];

28、iOS 中都有哪些锁?

追问 1、@synthesize 和 @dynamic 分别有 么作 ?

1)@property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;

2)@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。

3)@dynamic告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成。

28、什么是RunLoop,它是怎么做到有事做事,没事休息的?

29、怎样实现一个常驻线程?

1.为当前线程开启一个runloop;
createRunloop方法会查找当前线程是否有runloop,如果没有则系统会为我们创建一个
2.向该runloop中添加一个port/source等维持runloop的事件循环
3.启动该runloop

追问1、RunLoop与线程是什么关系?

RunLoop与线程是一一对应的关系,一个线程默认是没有RunLoop的(除了主线程)。

追问2、NSRunLoopCommonModes的特殊性?

追问3、当处于休眠状态的RunLoop我们可以通过哪些方式来唤醒他?

追问4、Runloop工作流程是怎样的?

1、在runloop启动之后,会发送一个通知,告知观察者Observer,runloop即将启动。
2、runloop会将处理Time/Sourece0事件的通知发送
3、处理source0事件的处理
4、如果接下来还有source1事件需要处理,通过go to语句进行代码逻辑的跳转,处理唤醒时接受到的消息
5、如果没有source1事件需要处理,此时线程要进入休眠,发送通知给observer,发生用户态向核心态的切换,线程正式进入休眠,等待唤醒。
6、杀死程序的时候,runloop就会退出停止,并且发送通知给observer即将退出runloop。
注释:当我们点击屏幕的时候 ,实际会产生一个machPort,基于machPort会转成一个source1,就会把主线程唤醒、运行、处理。

30、怎样保证子线程数据回来更新UI的时候,不打断用户的滑动操作?

当用户滑动的时候,当前的runloop运行在trackingMode模式下,我们可以把子线程抛会主线程更新UI这段逻辑封装到主线程的defaultMode下,这样抛回来的任务当用户滑动的时候,就不会执行打断用户滑动;当滑动结束后,主线程切回到defaultMode就可以执行更新ui数据。

31、怎样理解Block截获变量的特性?

30、iOS开发之 __block 与 __weak的区别理解

1.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
2.__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
3.__block对象可以在block中被重新赋值,__weak不可以。
4.__block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用,__weak只在ARC下使用,可以避免循环引用。

31、iOS 底层解析weak的实现原理

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

追问 1、 iOS-实现weak后,为什么对象释放后会自动为nil?

runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为 0 的时候会 dealloc,假如 weak 指向的对象内存地址是 a ,那么就会以 a 为键, 在这个 weak 表中搜索,找到所有以 a 为键的 weak 对象,从而设置为 nil 。

追问 2、当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?

1、调用objc_release

2、因为对象的引用计数为0,所以执行dealloc

3、在dealloc中,调用了_objc_rootDealloc函数

4、在_objc_rootDealloc中,调用了object_dispose函数

5、调用objc_destructInstance

6、最后调用objc_clear_deallocating

a. 从weak表中获取废弃对象的地址为键值的记录

b. 将包含在记录中的所有附有 weak修饰符变量的地址,赋值为 nil

c. 将weak表中该记录删除

d. 从引用计数表中删除废弃对象的地址为键值的记录

32、如何通过一个view查找它所在的viewController?

- (UIViewController *)findViewController:(UIView *)sourceView{ 
   id target=sourceView;   
 while (target) {     
   target = ((UIResponder *)target).nextResponder;    
    if ([target isKindOfClass:[UIViewController class]]) 
     {          
      break;    
     } 
   }   
 return target;
}

---------------------


33、iOS 如何扩大view的响应范围

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
   return  YES;
}
// 在view中重写以下方法,其中self.button就是那个希望被触发点击事件的按钮
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *view = [super hitTest:point withEvent:event];
    if (view == nil) {
        // 转换坐标系
        CGPoint newPoint = [self.button convertPoint:point fromView:self];
        // 判断触摸点是否在button上
        if (CGRectContainsPoint(self.button.bounds, newPoint)) {
            view = self.deleteButton;
        }
    }
    return view;
}


33、进程间的通信方式,并举例?

1、URLScheme
2、Keychain
3、UIPasteboard
4、UIDocumentInteractionController
5、local socket
6、 AirDrop
7、 UIActivityViewController
8、 App Groups

34、三种Block

根据isa指针,block一共有3种类型的block

_NSConcreteGlobalBlock 全局静态

_NSConcreteStackBlock 保存在栈中,出函数作用域就销毁

_NSConcreteMallocBlock 保存在堆中,retainCount == 0销毁

35、内存五大分区

36、get和post请求方式的区别?

安全:不引起server端的任何变化
幂等:同一个请求执行一次和执行多次的效果是完全相同的。
可缓存:请求是否可缓存

36、你都了解哪些状态码?

1xx、 2xx、 3xx、 4xx 、 5xx

37、tcp三次捂手四次挥手?

https://www.cnblogs.com/Andya/p/7272462.html
三次握手:

39、怎样理解滑动窗口协议?

定义:传输的每个部分被分配唯一的连续序列号,接收方使用数字并以正确的顺序放置接收到的数据包,丢弃重复的数据包并识别丢失的数据。

40、请简单描述TCP的慢启动的特点?

41、TCP的五大特点?

面向连接、可靠传输、面向字节流、流量控制、拥塞控制

42、加密算法?

43、NSOperation关于人物状态的控制都有哪些呢?

43、NSOperation状态的控制?

44、怎样移除一个isFinished = Yes 的NSOperation的?

KVO

45、怎样移除一个isFinished = Yes 的NSOperation的?

46、Swift比Objective-C有什么优势?

1、Swift容易阅读,语法和文件结构简易化。
2、Swift更易于维护,文件分离后结构更清晰。
3、Swift更加安全,它是类型安全的语言。
4、Swift代码更少,简洁的语法,可以省去大量冗余代码
5、Swift速度更快,运算性能更高

47、为什么代理 要 weak?代理的delegate和dataSource有什么区别?block和代理的区别?

48、id和NSObject*的区别?

id是 个 objc_object 结构体指针,定义是 typedef struct objc_object *id
id可以理解为指向对象的指针。所有oc的对象 id都可以指向,编译器不会做类型检 查,id调用任何存在的方法都不会在编译阶段报错,当然如果这个id指向的对象没有 这个方法,该崩溃还是会崩溃的。
NSObject *指向的必须是NSObject的 类,调用的也只能是NSObjec里面的方法否则就要做强制类型转换。
不是是所有的OC对象都是NSObject的子类,还有一些继承自NSProxy。NSObject * 可指向的类型是id的子
集。

49、iOS内存管理的原理是什么?

iOS中内存管理的方式主要有三种:1、taggedPointer。2、NONPOINTER_ISA。3、散列表。

taggedPointer:

那么为什么要使用taggedPointer这种内存管理方法呢?其如何达到节省内存的目的呢。举个例子,比如在OC中一个NSNumber这类小对象,在32位中的系统中占用4个字节的空间,但是迁移至64位系统中后,其占用空间达到了8字节,以此类推,所有在64位系统中占用空间会翻倍的对象,在迁移后会导致系统内存剧增,即时他们根本用不到这么多的空间,所以苹果对于一些小型数据,采用了taggedPointer这种方式管理内存。

什么是taggedPointer?

在64位的系统中,一个指针所占用的内存空间为8个字节,已足以存下一些小型的数据量了,当对象指针的空间中存满后,再对指针所指向的内存区域进行存储,这就是taggedPointer。

NONPOINTER_ISA :

在一个64位的指针内存中,第0位存储的是indexed标识符,它代表一个指针是否为NONPOINTER型,0代表不是,1代表是。第1位has_assoc,顾名思义,1代表其指向的实例变量含有关联对象,0则为否。第2位为has_cxx_dtor,表明该对象是否包含C++相关的内容或者该对象是否使用ARC来管理内存,如果含有C++相关内容或者使用了ARC来管理对象,这一块都表示为YES,第3-35位shiftcls存储的就是这个指针的地址。第42位为weakly_referenced,表明该指针对象是否有弱引用的指针指向。第43位为deallocing,表明该对象是否正在被回收。第44位为has_sidetable_rc,顾名思义,该指针是否引用了sidetable散列表。第45-63位extra_rc装的就是这个实例变量的引用计数,当对象被引用时,其引用计数+1,但少量的引用计数是不会直接存放在sideTables表中的,对象的引用计数会先存在NONPOINTER_ISA的指针中的45-63位,当其被存满后,才会相应存入sideTables散列表中。

散列表:

散列表是一个复杂的数据结构,其中包含了:自旋锁、引用计数表和弱引用表

散列表在系统中的提现是一个sideTables的哈希映射表,其中所有对象的引用计数(除上述存在NONPOINTER_ISA中的外)都存在这个sideTables散列表中,而一个散列表中又包含众多sideTable。每个SideTable中又包含了三个元素,spinlock_t自旋锁RefcountMap引用计数表weak_table_t弱引用表。所以既然SideTables是一个哈希映射的表,为什么不用SideTables直接包含自旋锁,引用技术表和弱引用表呢?因为在众多线程同时访问这个SideTables表的时候,为了保证数据安全,需要给其加上自旋锁,如果只有一张SideTable的表,那么所有数据访问都会出一个进一个,单线程进行,非常影响效率,而且会带来不好的用户体验,针对这种情况,将一张SideTables分为多张表的SideTable,再各自加锁保证数据的安全,这样就增加了并发量,提高了数据访问的效率,所以这就是一张SideTables表下涵盖众多SideTable表的原因。
当一个对象访问SideTables时,首先会取到对象的地址,将地址进行哈希运算,与SideTables的个数取余,最后得到的结果就是该对象所要访问的SideTable所在SideTables中的位置,随后在取到的SideTable中的RefcountMap表中再次进行一次哈希查找,找到该对象在引用计数表中所对应的位置,如果该位置存在对应的引用计数,则对其进行操作,如果没有对应的引用计数,则创建一个对应的size_t对象,其实就是一个uint类型的无符号整型

追问1、Objective-C 如何对内存管理的,说说你的看法和解决方法?

答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
1). 自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。
2). 手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。
3). 内存释放池Release Pool:把需要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中所有的内存空间也被自动释放掉。内存池的释放操作分为自动和手动。自动释放受runloop机制影响。

50.KVC的底层实现?

当一个对象调用setValue方法时,方法内部会做以下操作:
1). 检查是否存在相应的key的set方法,如果存在,就调用set方法。
2). 如果set方法不存在,就会查找与key相同名称并且带下划线的成员变量,如果有,则直接给成员变量属性赋值。
3). 如果没有找到_key,就会查找相同名称的属性key,如果有就直接赋值。
4). 如果还没有找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。

51. 什么是ARC?

自动引用计数:

手动引用计数:

53.请简单的介绍下APNS发送系统消息的机制

APNS优势:杜绝了类似安卓那种为了接受通知不停在后台唤醒程序保持长连接的行为,由iOS系统和APNS进行长连接替代。
APNS的原理:
1). 应用在通知中心注册,由iOS系统向APNS请求返回设备令牌(device Token)
2). 应用程序接收到设备令牌并发送给自己的后台服务器
3). 服务器把要推送的内容和设备发送给APNS
4). APNS根据设备令牌找到设备,再由iOS根据APPID把推送内容展示

54.Runtime实现的机制是什么,怎么用,一般用于干嘛?

1). 使用时需要导入的头文件 <objc/message.h> <objc/runtime.h>
2). Runtime 运行时机制,它是一套C语言库。
3). 实际上我们编写的所有OC代码,最终都是转成了runtime库的东西。
比如:
类转成了 Runtime 库里面的结构体等数据类型,
方法转成了 Runtime 库里面的C语言函数,
平时调方法都是转成了 objc_msgSend 函数(所以说OC有个消息发送机制)
// OC是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
// [stu show]; 在objc动态编译时,会被转意为:objc_msgSend(stu, @selector(show));
4). 因此,可以说 Runtime 是OC的底层实现,是OC的幕后执行者。

55.AFNetworking 为我们做了什么?

追问1、AFNetworking 架构图?

追问2、AFURLSessionManager 作为核心类的主要组成?

追问3、AFURLSessionManager 作为核心类的主要负责哪些工作?

追问4、NSURLConnection与NSURLSession(AFN 2.0 与 3.0区别)区别?

55.SDWebImage 为我们做了什么?

追问1、SDWebImage 架构图?

追问 2、.描述下SDWebImage里面给UIImageView加载图片的逻辑

加载图片的过程大致如下:
1.首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 作为数据的索引先在内存中寻找是否有对应的缓存
2.如果缓存未找到就会利用通过MD5处理过的key来继续在磁盘中查询对应的数据, 如果找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来
3.如果在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片
4.下载后的图片会加入缓存中,并写入磁盘中
5.整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来

追问 3、sdwebimage 下载了图片后为什么要解码?

一般下载或者从磁盘获取的图片是PNG或者JPG,这是经过编码压缩后的图片数据,不是位图,要把它们渲染到屏幕前就需要进行解码转成位图数据,而这个解码操作比较耗时。你也可以这么理解,图片在远端存储一定都是编码后存储的,这样体积小,一个图像可以看做是一个图像文件,里面包含了文件头,文件体和文件尾,图像的数据就包含在文件体中,而我们的解码就是运用算法将文件体中的图像数据转化为位图数据,方便渲染和展示。

iOS默认是在主线程解码,所以SDWebImage将这个过程放到子线程了。

同时因为位图体积很大,所以磁盘缓存不会直接缓存位图数据,而是编码压缩后的PNG或JPG数据。

54、AsyncDiaplayKit 为我们做了什么?

主要处理问题:主要是通过减轻主线程的压力,把更多的事情能放到子线程去做,就放到子线程,主要分为三反面;

追问1、ASDK 的实现原理是怎样的?

主要封装了一个ASNode节点,我们对于视图的属性设置都转化成了ASNode的设置,这部分设置有恰好可以放到后台线程当中实现,然后我们在Runloop将要结束的时候,接受一个通知,提取全局容器当中的ADNode,一次性设置给UIView。

56、方法和选择器有何不同?

selector是一个方法的名字,method是一个组合体,包含了名字和实现。

57、iOS 中的几种锁?

屏幕快照 2019-10-15 上午10.52.20.png
@synchronized(这里添加一个OC对象,一般使用self) { 
        //这里写要加锁的代码
 }  

注意点   
1.加锁的代码尽量少    
2.添加的OC对象必须在多个线程中都是同一对象 
3.优点是不需要显式的创建锁对象,便可以实现锁的机制。 
4. @synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。
所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。

2、NSLock

58、iOS 多读单写?

多个线程同时读取,多个线程有序写入。

// 通过宏定义 DISPATCH_QUEUE_CONCURRENT 创建一个并发队列
        concurrent_queue = dispatch_queue_create("read_write_queue", DISPATCH_QUEUE_CONCURRENT);

- (id)objectForKey:(NSString *)key
{
    __block id obj;
    // 同步读取指定数据
    dispatch_sync(concurrent_queue, ^{
        obj = [userCenterDic objectForKey:key];
    });
    
    return obj;
}

- (void)setObject:(id)obj forKey:(NSString *)key
{
    // 异步栅栏调用设置数据
    dispatch_barrier_async(concurrent_queue, ^{
        [userCenterDic setObject:obj forKey:key];
    });
}
上一篇下一篇

猜你喜欢

热点阅读