Skip to content

Latest commit

 

History

History
459 lines (287 loc) · 21.4 KB

jichu.md

File metadata and controls

459 lines (287 loc) · 21.4 KB

基础常见题目

1. assgin copy retain strong 区别

  1. assgin 对基础数据类型 (NSInteger)和C数据类型(int, float, double, char,等)
  2. copy用于Stringblock
  3. retain 用于NSObject和其他子类。
  4. readonly只生成getter方法
  5. readwrite生成setter和getter方法。

assign:默认类型,setter方法直接赋值,而不进行retain操作 retain:setter方法对参数进行release旧值,再retain新值。

copy:setter方法进行Copy操作,与retain一样

1.1 @public、@private、@protected、@package

  • @public 在任何地方都能直接访问对象的成员变量
  • @package:在同一个包下就可以直接访问,比如说在同一个框架
  • @protected 可以在当前类及其子类对象方法中直接访问(系统默认下是用它来修饰的)
  • @private 只能在当前类的对象方法中直接访问,如果子类要访问需要调用父类的get/set方法 -w577

1.2 NSHashTableNSMapTable区别?

  • NSHashTable是可变的set,可以对加入的对象设置引用类型,可设置弱引用,不影响源对象生命周期
  • NSMapTable是可变的Dic,可以对加入的对象设置引用类型,可设置弱引用,不影响源对象生命周期
  • 当弱引用的对象重置为nil,则自动从NSHashTableNSMapTable删除。

2. #include#import的区别、#import@class 的区别

#include#import都可以引入头文件,但是#import只会引入一次。#import虽然只会引入一次,但是还会导致相互引入的问题。@class可以在头文件引入类,类可以是不存在的,可以在头文件@class引入类,在.m#import引入类实现从而解决循环引用的问题。

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

我们知道copy关键词是复制一份内存地址,用新的指针指向。是属于深拷贝,而用strong关键字是通过一个指针指向对象的内存地址,通过内存地址访问对象,是属于浅拷贝。对于strong声明的字符串 数组字典其他地方可以通过地址直接修改对象

4. 这个写法会出什么问题: @property (copy) NSMutableArray *array

通过copy修饰的NSMutableArry对象变成了NSArray,这样在编译类型是NSMutableArray,在运行时是NSArray,这样我们在编译时候调用NSMutableArray方法是不报错的,但是在运行时候类型不一样从而导致崩溃发生。

5. 如何为 Class 定义一个对外只读对内可读写的属性

我们可以在.h文件声明属性用readonly关键词,在.m声明同样的属性用readwrite默认的关键词。

/// .h
@interface ClassA : NSObject
@property (nonatomic, assign, readonly) int number;
@end

/// .m
@interface ClassA ()
@property (nonatomic, assign) int number;
@end

@implementation ClassA
@end

6. block和代理的区别,哪个更好?

block使用起来更加简单,比如访问作用域的变量还有代码逻辑的连贯性。代理更能表现是面向接口,能更好的描述功能和作用。如果是对外面向的接口推荐使用代理,日常开发页面之间的传值可以使用block.并且block和代理之间,block的执行会比较快一些。

7. 在block内如何修改block外部变量?

通过block内部访问外部的变量,block会自动copy到自己的闭包空间。也就是复制了一份变量在自己的作用域内部,所以是无法修改外部的变量。通过__block关键词修饰之后,通过复制变量的引用地址来修改和访问外部变量的。

8. 类别和类扩展的区别

类别可以添加属性 添加的方法不实现会报警告。可以添加实例变量,但是只能自己类访问。扩展可以添加方法,不实现不会报警告。不能添加实例变量,属性可以通过关联对象实现

9. 分类的作用?分类和继承的区别?

分类可以将一个臃肿的类分散到多个文件中,可以将功能按照模块划分,多人多人开发一个类。分类可以给系统类添加方法,还可以拦截系统方法。继承拥有对类完全控制权,分类只对于引入文件有效。分类可以在不获取原来代码基础上增加方法,不可以删除方法,也不可以新增属性,分类具有更高的优先级。继承可以添加和删除方法,也可以新增属性。

10. 重写一个类的方法用继承好还是分类好? 为什么?

分类只对于本次有效,推荐用分类。

11. 若一个类有实例变量NSString *_foo,调用setValue:forKey:时,可以以foo还是_foo作为key

这个其实是考察我们KVC的基本原理,都是可以的.

12. isMemberOfClassisKindOfClass 联系与区别

isMemberOfClass只能判断是否是当前的类,isKindOfClass可以用来判断是否是当前类或者子类。

13. 一个objc对象的isa的指针指向什么?有什么作用?

isa其实是一个结构体,isa对象指向当前类 当前类指向父类,父类指向元类也就是NSObject,元类指向自己。作用是方便找到对应所在的方法。

14. 请描述一个你所遇到retain cycle例子

这个第一次解除Block时候应该经常用到,在Block内部使用self调用属性或者方法。

15.请谈谈内存的使用和优化的注意事项

我们知道创建对象 加载图片 加载文件 创建线程都需要消耗内存,特别是加载图片和加载文件会大量消耗内存。我们还知道在大量循环中创建临时变量就直线性的内存暴涨。通过上面我们可以才去进一步的优化内存,对于创建对象,因为单利会驻留在内存里面,不要创建消耗内存大的对象作为单利。加载图片imageName:会把图片驻留在内存里面做缓存提高下一次访问速度,我们可以把大图片用imageWithContentsOfFile的方法进行加载。尽可能减少多线程创建和数量,使用NSCache作为缓存机制替换我们常用的数组和字段作为缓存手段。在大量循环中用外部变量防止多次创建变量或者在循环内部使用runloop.如果是列表形式,做到数据重用。尽可能少的创建和消耗内存

16. runtime 如何实现 weak 属性

weak属性其实是系统维护的一个Hash表,系统会把weak声明的属性的内存地址作为key存放在hash表里面。当这个属性引用技术为0dealloc方法时候,通过对应内存地址作为key再从hash表移出出去。

17. runtime如何通过selector找到对应的IMP地址?

每个类里面都有一个method_list数组存储着这个类所有的方法和实现,通过selector对应的方法名称找到对应的方法和实现。

18. +(void)load; +(void)initialize;有什么用处?

load放在会在第一次加载类方法会调用,调用顺序是先父类,然后子类,然后分类。initialize会在第一次调用类方法或者实例方法时候被调用。

19. 如何访问并修改一个类的私有属性?

可以通过KVC或者通过ivar进行访问.

20. Objective-C 如何对已有的方法,添加自己的功能代码以实现类似记录日志这样的功能?

我们可以利用分类,通过交换方法实现。可以在方法之前也可以在方法后面。

@interface ClassA : NSObject
@end

@implementation ClassA

- (void)printMyName {
    NSLog(@"josercc");
}

@end

@interface ClassA (Print)
@end

@implementation ClassA (Print)

+ (void)load {
    Method method1 = class_getClassMethod(self, @selector(printMyName));
    Method method2 = class_getClassMethod(self, @selector(printMyName1));
    method_exchangeImplementations(method1, method2);
}

- (void)printMyName1 {
    NSLog(@"Hello");
    [self printMyName1];
}

@end

21. 请说明并比较以下关键词:strong, weak, assign, copy

  • strong 会持有对象,会使对象引用计数加1
  • weak 不会持有对象 只是存储了对象的内存地址
  • assgin 用于声明基本类型 被assgin声明的会存放在栈区
  • copy 会复制一份内存地址,一般用于NSString NSArray NSDictionary

22. 请说明并比较以下关键词:__weak,__block

  • __weak用来修饰实例变量
  • __block用来修饰可以在block内部修改外部变量

23. 请说明并比较以下关键词:atomatic, nonatomic

  • atomatic 是相对线程安全的因为会自动在setget方法进行加锁,但是会额外的消耗性能
  • nonatomic 是线程不安全的性能好

24. 下面代码中有什么bug?

- (void)viewDidLoad {
  UILabel *alertLabel = [[UILabel alloc] initWithFrame:CGRectMake(100,100,100,100)];
  alertLabel.text = @"Wait 4 seconds...";
  [self.view addSubview:alertLabel];

  NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
  [backgroundQueue addOperationWithBlock:^{
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4]];
    alertLabel.text = @"Ready to go!”
  }];
}

我们是在子线程操作的UI应该在主线程操作UI不会更新(在Xcode11测试依然会更新,只是会报不能在子线程更新UI的警告 -[UILabel setText:] must be used from main thread only)。

25. Object-C有多继承吗?没有的话用什么代替?cocoa 中所有的类都是NSObject 的子类

OC是没有多继承的,可以通过协议进行代替。

26. Object-C有私有方法吗?私有变量呢?

OC并不存在严格来说的私有方法和私有变量。对于用@public修饰的属性和方法在.h都可以被外部调用。在.m实现的方法和实例变量只能本类调用就称作私有的。OC是一个有运行时的特性,所以我们可以获取方法列表找到私有方法通过消息转发调用。对于私有变量我们可以通过KVC,或者Ivar进行访问。所以OC不存在严格意义上面的私有方法和私有变量

27. 关键字const什么含义

对于const来说 声明在谁前面就意味着谁不能修改,如果const在第一位则向右移一位。比如int const a等效果于const int a都是让整形的a不可变。声明const可以让编译时候代码更紧凑,让系统修改变量时候随时产生报错,防止BUG的产生。

28. tableView的重用机制?

UITableView有两个实例变量,visiableCells保存当前界面显示的Cell的数组,reusableTableCells保存在可以重用的Cell的字典。当第一次加载界面,reusableTableCells没有任何重用的Cell就会走UITableViewCell的初始化方法进行创建,之后添加到visiableCells数组里面。当界面滚动时候,原本展示的Cell消失在屏幕之后。对应的Cell对象就会添加到reusableTableCells里面,即而从visiableCells数组里面进行移出。当一个新的cell将展示时候从reusableTableCells查看是否存在重用的,如果存在就展示重用的,如果没有就重新走创建的流程。

29. ViewController的didReceiveMemoryWarning是在什么时候调用的?默认的操作是什么?

当手机内存运行不足时候会调用ViewControllerdidReceiveMemoryWarning方法,默认时尝试释放ViewController所拥有的View。我们也可以重写做其他释放内存的任务。

30. delegate和notification区别,分别在什么情况下使用?

delegate是一对一的关系,notification是一对多的关系。delegate通畅用于开放接口用于其他模块的调用,notification可以做到模块分离,通畅用于模块之间的通信。

31. id、nil代表什么?

id代表在OC里面的对象的指针,nil代表是指针指向一个空的对象。

32. timer的间隔周期准吗?为什么?怎样实现一个精准的timer?

NSTimer因为是添加到runloop中的走的是默认的mode,当滑动表格的时候就会切换对应mode停止timerrunloop因为优先在触发时候执行输入源,才会执行runloop中的任务,虽有会有50-100毫秒的误差。

我们想做一个精准的timer就需要创建一个新的runloop来不受其他输入源的影响。

添加Timer到当前的runloop设置为NSRunLoopCommonModes

_timer = [NSTimer scheduledTimerWithTimeInterval:1.0
				      target:self
				    selector:@selector(timerStart)
				    userInfo:nil
				     repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
  • 在新线程添加NSTimer
dispatch_async(dispatch_queue_create("", 0), ^{
@autoreleasepool {
    self->_timer = [NSTimer scheduledTimerWithTimeInterval:1.0
					      target:self
					    selector:@selector(timerStart)
					    userInfo:nil
					     repeats:YES];
    [[NSRunLoop currentRunLoop] run];
}
});
  • GCD
timer = 
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"timer");
});
dispatch_resume(timer);

对比还是第三种方案简单,第二种执行的任务会在子线程,容易写出BUG,第一种容易遗漏添加到runloop设置对应的mode。第三种有现成的代码块,而且还十分的精准。

33. 一个 NSObject 对象占用多少内存?

系统分配16个字节,但是真正占用只有8个字节。

34 . 方法和选择器有何不同?(Difference between method and selector?)

selector只是代表方法名称,而method包含了名称和实现。

35. 你是否接触过OC中的反射机制?简单聊一下概念和使用

反射机制就是通过字符串反射为对应的类,协议,方法。或者将协议,方法或者类反射为字符串。通常用于做模块化跳转,或者用于做deeplink等运行时创建类等功能。

36. Objective-C中的协议默认是@optional还是@require?在使用协议的时候应当注意哪些问题?

协议默认为@require,使用时候要注意用weak声明代理,防止循环引用。

37 .通知的实现机制?为什么有时候监听的名称和发送通知名称相同却收不到回调。

通知底层核心是存在一个notication_map的字典,通知的名称作为KeyValue为数组结构,数组的每个元素包含了监听的对象,方法名称,还有传递的参数。当发送通知的时候,会在notication_map的字典当中找到对应key的数组,通过遍历数组,对数组里面所有的监听者发送通知。当发送参数和监听的参数不一样的时候,会被忽略,这就是为什么名字相同却收不到通知。

当接受通知的参数为空可以接受发送通知参数为空或者不为空

可以接受到通知

/// 当object=nil;则都可以接受到.
/// 当不为空的时候,则key为object过滤Observer。
	NSString *name = @"addNotification";
	NSNotificationCenter * center =  [NSNotificationCenter defaultCenter];
	[center addObserver:self
			   selector:@selector(receData:)
				   name:name
				 object:nil];
	[center postNotificationName:name object:@(1)];
	[center postNotificationName:name object:nil];
	


	NSString *name = @"addNotification";
	NSNotificationCenter * center =  [NSNotificationCenter defaultCenter];
	NSObject *key  = [NSObject new];
	[center addObserver:self
			   selector:@selector(receData:)
				   name:name
				 object:key];
	/// 1. 不接受
	/// 2. 不接受
	/// 3. 接受 当object设置的时候,下次一定要传过来才能接受到通知
	[center postNotificationName:name object:@(1)];
	[center postNotificationName:name object:nil];
	[center postNotificationName:name object:key];
	

38. KVO (Key-value observing)的实现机制

KVO系统通过isa混淆技术通过创建类的不可见子类,通过重写setget方法来实现监听机制的。对于没有调用setget方法是调用不了监听机制的,需要手动调用willChangeValueForKeydidChangeValueForKey触发。

39. 能否向已经编译的类添加实例变量

不能,因为编译之后类的内存大小已经固定不能再添加实例变量,但是可以动态的添加实例变量。

网络篇

40. 什么是心跳?

心跳就是为了检测TCP是否还在链接,通常由客户端发起,等待服务器响应。如果10秒没有回应就代表服务器或者客户端链接出现了问题。

41. AF中常驻线程的实现

通过单利创建线程,在线程内部通过NSMachPort监听加入到当前的runloop中,访问新建的线程没有任务就退出了。

42. 什么是WebSocket,解决了什么问题?

WebSocket是应用的一层协议,是基于一次握手之后,就通过TCP通道传输,和HTTP没关任何关系。WebSocket是基于Frame传输的,可以将传输数据分为几片Frame。解决了大数据传输问题,可以将大数据分割称不同小的Frame传输。可以和HTTP一样边生成边传输,提高了传输效率。

43. 断点续传如何实现?

断点续传的原理是通过控制head中的Range值来做的,关键是需要后台服务器支持Range

44. 什么是Cookie和Session

Cookie是客户端会吧会话的SessionID保存,之后发起请求把回话的SessionID带给服务器。Session是保存会话身份,标识身份是SessionID,服务器可以同Session同过客户端传递的SessionID获取到身份信息.

45. 非对称加密

非对称加密是有一堆密钥 公钥 私钥组成,私钥只能一方掌管。常用的比如RSA

46. 什么是对称加密

对称加密就是加密和解密都使用同样的密钥,常用的AES,DES,3DES

47.HTTPS链接的过程

  1. 客户端发起请求到服务器
  2. 服务器返回证书给客户端
  3. 客户端根据根证书对服务器返回的证书进行验证
  4. 客户端根据对称密钥根据服务器证书加密返给服务器
  5. 服务器使用私钥对客户端的密钥解密
  6. 双方使用密钥进行加密传输

48 .知道TCP/UDP吗?说说关于UDP/TCP的区别?

  1. TCP

TCP是面向传输流 传输可靠 不丢包。没有传输大小限制,支持一对一,需要三次握手链接。

  1. UDP

面向字节流 传输不可靠 容易丢包。传输大小限制64K,支持一对一,一对一,多对多,不需要握手就可以链接

49 . HTTP请求方式?

  • GET 用于查询资源
  • POST 用于创建资源
  • PUT 用于修改资源
  • DELETE 用于删除资源
  • HEAD 测试服务器性能
  • CONNECT SSL通信
  • OPTIONS 测试服务器性能
  • TRACE 测试链接

50. HTTP 的POST与GET区别与联系,实践中如何选择它们?

  1. POST

提交参数加密 默认最大支持2M文件传输(服务器配置) 通常创建修改删除资源用POST请求

  1. GET

提交参数可见 最大支持参数1024K大小。支持缓存,所以平时可以来做CND加速

51. 单例是什么?

单利是不管怎么初始化,在进程运行期间只存在一个实例对象

52 . GCD 是怎么保证单例的?

GCD在内部同过控制一个静态变量来是否走Block方法创建对象,内部通过信号量控制调用。就算是在多线程,其他没有调用完毕就等待,等调用完毕再去看对象是否创建完毕.

53. Runloop的作用

  • 保持应用的持续运行
  • 处理App的各种事件
  • 节省CPU资源,提升性能。
  • 负责渲染界面的UI

54. RunLoop和AutoreleasePool的关系

RunLoop进行处理事件的时候会自动创建一个AutoreleasePool,在处理事件过程中会将发送autorelease消息的对象添加到AutoreleasePool中。等待RunLoop处理事件结束,就释放当前的AutoreleasePoolAutoreleasePool则会将所有的对象进行release-1操作。

55. 怎么创建一个常驻线程

@autoreleasepool {
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [runloop run];
}

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

因为滑动操作当前的RunLoop运行在Tracking Mode上面,为了不打断用户的操作,我们可以在Default Mode上面进行刷新数据,也就是等待滑动结束之后,当前的RunLoopTracking Mode切换到Default Mode再去更新数据.

[self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];

57. GCD 在 Runloop 中的使用

GCD只有在回到主线程的时候才会获取当前的RunLoop,会触发RunLoopSource1事件。

58 .OpenGL 主要渲染步骤

  1. 查看答案
  2. 设置图元数据
  3. 着色器-shader 计算图元数据(位置·颜色·其他)
  4. 光栅化-rasterization 渲染为像素
  5. fragment shader,决定最终成像
  6. 其他操作(显示·隐藏·融合)

59. 为什么UIKit和AppleKit都要基于Core Animation框架

因为对于UIViewNSView来说,他们只负责更改属性和负责交互。负责渲染呈现UI的是CALayer,但是渲染对于iOSmacOS没有什么不同。为了让代码复用,框架简单。但是又要区分iOSmacOS端的交互,就将UIKitAppleKit底层都依赖于Core Animation来绘制界面。