iOS 面试题(二)

2022-04-24  本文已影响0人  搬砖的crystal

1.UDID和UUID

UDID

UDID的全名为Unique Device Identifier :设备唯一标识符。从名称上也可以看出,UDID这个东西是和设备有关的,而且是只和设备有关的,有点类似于MAC地址。需要把UDID这个东西添加到Provisoning Profile授权文件中,也就是把设备唯一标识符添加进去,以此来识别某一台设备。
UDID是一个40位十六进制序列,我们可以使用iTunes和Xcode来获取这个值。
自从iOS5之后,苹果就禁止了通过代码访问UDID,在这之前,可以使用[[UIDevice cuurrent] uniqueIdenfier]这个方法来获取某设备UDID。
苹果提供了一个参数identifierForVendor来替代原来UDID的作用。

NSUUID *uuid = [UIDevice currentDevice].identifierForVendor;
NSLog(@"uuid 1 = %@",uuid.UUIDString);

此时打印出的字符串UUIDString这个东西不是真正的UDID,而是一个有一点像的替代品。UDID是只和iOS设备有关的,而这个identifierForVendor是应用和设备两者都有关的,A应用安装到张三这台设备上,就会产生一个identifierForVendor(比如是:1234);A应用安装到李四这台设备上,就会产生另一个identifierForVendor(比如是:5678)。但是无论A应用安装卸载多少次,产生的是都是1234. 所以我们知道,这个identifierForVendor是一种应用加设备绑定产生的标识符。也就是说App的开发者没有办法去区分某一台设备了,而是只能识别某个应用在某台设备上。

UUID

英文名称是:Universally Unique Identifier,翻译过来就是通用唯一标识符。是一个32位的十六进制序列,使用小横线来连接:8-4-4-4-12 。UUID在某一时空下是唯一的。比如在当前这一秒,全世界产生的UUID都是不一样的;当然同一台设备产生的UUID也是不一样的。

NSString *uuid = [NSUUID UUID].UUIDString;
NSLog(@"uuid 2 = %@",uuid);

2.CPU和GPU

(1)概念

CPU(Central Processing Unit-中央处理器),是一块超大规模的集成电路,是一台计算机的运算核心(Core)和控制核心( Control Unit)。它的功能主要是解释计算机指令以及处理计算机软件中的数据。
GPU(Graphics Processing Unit-图形处理器),是一种专门在个人电脑、工作站、游戏机和一些移动设备(如平板电脑、智能手机等)上图像运算工作的微处理器。

(2)缓存

CPU有大量的缓存结构,目前主流的CPU芯片上都有四级缓存,这些缓存结构消耗了大量的晶体管,在运行的时候需要大量的电力。
GPU的缓存就很简单,目前主流的GPU芯片最多有两层缓存,而且GPU可以利用晶体管上的空间和能耗做成ALU单元,因此GPU比CPU的效率要高一些。

(3)响应方式

CPU要求的是实时响应,对单任务的速度要求很高,所以就要用很多层缓存的办法来保证单任务的速度。
GPU是把所有的任务都排好,然后再批处理,对缓存的要求相对很低。

(4)浮点运算方式

CPU除了负责浮点整形运算外,还有很多其他的指令集的负载,比如像多媒体解码,硬件解码等,因此CPU是多才多艺的。CPU注重的是单线程的性能,要保证指令流不中断,需要消耗更多的晶体管和能耗用在控制部分,于是CPU分配在浮点计算的功耗就会变少。
GPU基本上只做浮点运算的,设计结构简单,也就可以做的更快。GPU注重的是吞吐量,单指令能驱动更多的计算,相比较GPU消耗在控制部分的能耗就比较少,因此可以把电省下来的资源给浮点计算使用。

(五)应用方向

CPU所擅长的像操作系统这一类应用,需要快速响应实时信息,需要针对延迟优化,所以晶体管数量和能耗都需要用在分支预测、乱序执行、低延迟缓存等控制部分。
GPU适合对于具有极高的可预测性和大量相似的运算以及高延迟、高吞吐的架构运算。

3.堆、栈和队列

(1)堆

堆是一种经过排序的树形数据结构,每个节点都有一个值,通常我们所说的堆的数据结构是指二叉树。所以堆在数据结构中通常可以被看做是一棵树的数组对象。

堆需要满足以下两个性质:
1)堆中某个节点的值总是不大于或不小于其父节点的值;
2)堆总是一棵完全二叉树。

堆分为两种情况,有最大堆和最小堆。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆,在一个摆放好元素的最小堆中,父结点中的元素一定比子结点的元素要小,但对于左右结点的大小则没有规定谁大谁小。

堆常用来实现优先队列,堆的存取是随意的,这就如同我们在图书馆的书架上取书,虽然书的摆放是有顺序的,但是我们想取任意一本时不必像栈一样,先取出前面所有的书,书架这种机制不同于箱子,我们可以直接取出我们想要的书。

(2)栈

栈是限定仅在表尾进行插入和删除操作的线性表。我们把允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈。栈的特殊之处在于它限制了这个线性表的插入和删除位置,它始终只在栈顶进行。

栈是一种具有后进先出的数据结构,又称为后进先出的线性表,简称 LIFO(Last In First Out)结构。也就是说后存放的先取,先存放的后取,这就类似于我们要在取放在箱子底部的东西(放进去比较早的物体),我们首先要移开压在它上面的物体(放进去比较晚的物体)。

堆栈中定义了一些操作。两个最重要的是PUSH和POP。PUSH操作在堆栈的顶部加入一个元素。POP操作相反,在堆栈顶部移去一个元素,并将堆栈的大小减一。

栈的应用——递归。

(3)队列

队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表。允许插入的一端称为队尾,允许删除的一端称为队头。它是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。

队列是一种先进先出的数据结构,又称为先进先出的线性表,简称 FIFO(First In First Out)结构。也就是说先放的先取,后放的后取,就如同行李过安检的时候,先放进去的行李在另一端总是先出来,后放入的行李会在最后面出来。

4.iOS系统架构

iOS的系统架构分为四个层次:核心操作系统层(Core OS layer)、核心服务层(Core Services layer)、媒体层(Media layer)和可触摸层(Cocoa Touch layer)



Core OS是最为核心的系统层,包括了内存管理、文件系统、硬件管理、电源管理、安全管理等内容,是iOS的核心操作系统。
Core Services包含了多种核心服务提供给 APP 使用,主要功能为 CoreFundation 和 Fundation 这两个 framework。
Media层主要包括了各种媒体文件的处理,通过它我们可以在应用程序中使用各种媒体文件,进行音频与视频的录制,图形的绘制,以及制作基础的动画效果。
Cocoa Touch是可触摸层,这一层为我们的应用程序开发提供了各种有用的框架,并且大部分与用户界面有关,本质上来说它负责用户在iOS设备上的触摸交互操作以及一些其他的关键功能比如多线程和通知等。

5.视图控制器的生命周期

//  ViewController.m
#import "ViewController.h"
@interface ViewController ()

@end

@implementation ViewController

#pragma mark - 1. 类加载
+ (void)load {
   
}

#pragma mark - 2. 执行类第一个方法前调用
+ (void)initialize {
  
}

#pragma mark - 3. nib文件解档初始化
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    
    return [super initWithCoder:aDecoder];

}

#pragma mark - 4. nib文件解档对象的个性化设置
- (void)awakeFromNib {
    [super awakeFromNib];
    
}

#pragma mark - 5. 加载视图
- (void)loadView {
    [super loadView];
 
}

#pragma mark - 6. 视图加载完毕,视图个性化设置
- (void)viewDidLoad {
    [super viewDidLoad];
    
}

#pragma mark - 7. 视图即将显示
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}

#pragma mark - 8. 视图即将为子视图布局
- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
   
}

#pragma mark - 9. 视图已经为子视图布局完毕
- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
   
}

#pragma mark - 10. 视图已经显示完毕
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
}

#pragma mark - 11.视图即将消失
-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    
}

#pragma mark - 12.视图已经消失
-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    
}

#pragma mark - 13.内存不足时候会调用
-(void)didReceiveMemoryWarning{
    
}

@end

6.APP生命周期

(1)生命周期的5种状态
(2)iOS13之前
点击应用程序图标

1)程序入口:进入main函数
2)通过UIApplicationMain函数
3)初始化UIApplication对象并且设置代理对象AppDelegate
4)程序完成加载:[AppDelegate application:didFinishLaunchingWithOptions:]
5)创建Window窗口:UIWindow
6)程序被激活:[AppDelegate applicationDidBecomeActive:]

点击Home键

1)程序取消激活状态:[AppDelegate applicationWillResignActive:]
2)程序进入后台:[AppDelegate applicationDidEnterBackground:]

点击应用图标

1)程序进入前台:[AppDelegate applicationWillEnterForeground:]
2)程序被激活:[AppDelegate applicationDidBecomeActive:]

内存警告

[AppDelegate applicationDidReceiveMemoryWarning]

将要终止

[AppDelegate applicationWillTerminate]

(3)iOS13之后
点击应用程序图标

1)程序入口:进入main函数
2)通过UIApplicationMain函数
3)初始化UIApplication对象并且设置代理对象AppDelegate
4)程序完成加载:[AppDelegate application:didFinishLaunchingWithOptions:]
5)进入场景对象调用:[SceneDelegate scene:willConnectToSession:options:]方法
6)程序将要进入场景:[SceneDelegate sceneWillEnterForeground:]
7)场景已经激活:[SceneDelegate sceneDidBecomeActive:]

点击Home键:

1)取消场景激活状态:[SceneDelegate sceneWillResignActive:]
2)程序进入后台:[SceneDelegate sceneDidEnterBackground:]

点击图标

1)程序将要进入前台:[SceneDelegate sceneWillEnterForeground:]
2)程序已经被激活:[SceneDelegate sceneDidBecomeActive:]

进入程序选择界面

[SceneDelegate sceneWillResignActive:]

程序被杀死

[SceneDelegate sceneDidDisconnect:]

7.UIView的生命周期

纯代码

UIView创建为:[[UIView alloc] init];
1)initWithFrame:
2)init
3)layoutSubviews

纯代码

UIView创建为:[[UiView alloc] initWithFrame:[UIScreen mainScreen].bounds];
1)initWithFrame:
2)layoutSubviews

Xib

UIView创建为:NSArray *arr = [[NSBundle mainBundle] loadNibNamed:(@"XHView") owner:nil option:nil]; [arr lastObject];
1)initWithCoder:
2)awakeFromNib
3)layoutSubviews

8. UIApplication

UIApplication对象是应用程序的象征。
每一个应用程序都有自己的UIApplication对象,而且是单例。
一个iOS程序启动后创建的第一个对象就是UIApplication对象。
通过UIApplication *app = [UIApplication sharedApplication];可以获得这个单例对象。
利用UIApplication对象能进行一些应用级别的操作。

(1)创建

应用程序的入口main.m函数

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

第一个参数:argc系统或者用户传入的参数。
第二个参数:argv系统或用户传入的实际参数。
第三个参数 :nil代表UIApplication类名或者子类名称,nil相当于 @"UIApplicaiton"
第四个参数 :代表UIApplicaiton的代理名称NSStringFromClass([AppDelegate class]相当于@"AppDelegate"

(2)应用级别的操作
设置应用程序图标右上角的红色提醒数字
UIApplication *app = [UIApplication sharedApplication];
app.applicationIconBadgeNumber = 10;
// 创建通知对象
UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge categories:nil];
// 注册用户通知
[app registerUserNotificationSettings:setting];
设置联网指示器的可见性
app.networkActivityIndicatorVisible= YES;
管理状态栏

系统提供2种管理状态栏的方式:
1)UIViewController管理(每一个UIViewController都可以拥有自己不同的状态栏)

#pragma mark-设置状态栏的样式
-(UIStatusBarStyle)preferredStatusBarStyle
{
    //设置为白色
    //return UIStatusBarStyleLightContent;
    //默认为黑色
     return UIStatusBarStyleDefault;
}
#pragma mark-设置状态栏是否隐藏(否)
-(BOOL)prefersStatusBarHidden
{
    return NO;
}

2)UIApplication管理(一个应用程序的状态栏都由它统一管理)如果想利用UIApplication来管理状态栏,首先得修改Info.plist的设置,添加选中行,并设置为NO即可。


//通过sharedApplication获取该程序的UIApplication对象
UIApplication *app=[UIApplication sharedApplication];
//设置状态栏的样式
//app.statusBarStyle=UIStatusBarStyleDefault;//默认(黑色)
//设置为白色+动画效果
[app setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
//设置状态栏是否隐藏
app.statusBarHidden=YES;
//设置状态栏是否隐藏+动画效果
[app setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
openURL:方法
UIApplication *app = [UIApplicationsharedApplication];
打电话  [app openURL:[NSURLURLWithString:@"tel://110"]];
发短信  [app openURL:[NSURLURLWithString:@"sms://10086"]];
发邮件  [app openURL:[NSURLURLWithString:@"mailto://xxcc@fox.com"]];
打开一个网页资源 [app openURL:[NSURL URLWithString:@"http://www.baidu.com"]];
打开其他app程序   openURL方法,可以打开其他APP。
判断程序运行状态
    //判断程序运行状态
    /*
     UIApplicationStateActive, 
     UIApplicationStateInactive, 
     UIApplicationStateBackground
     */
 UIApplication *app = [UIApplication sharedApplication];
 if(app.applicationState ==UIApplicationStateInactive){
        NSLog(@"程序在运行状态");
    }
阻止屏幕变暗进入休眠状态
    //阻止屏幕变暗,慎重使用本功能,因为非常耗电。
   UIApplication *app = [UIApplication sharedApplication];
   app.idleTimerDisabled =YES;

9.性能测试

Profile
Instruments
Time Profiler

10.iOS的设计模式

https://www.jianshu.com/p/f3b25ae273eb
https://www.jianshu.com/p/40063d3e0807
https://www.jianshu.com/p/c3bbe227d51c
https://www.jianshu.com/p/eecee6055cde

11.MVC和MVVC设计模式

https://www.jianshu.com/p/b6b6753c685f
https://www.jianshu.com/p/d0408401c424

12.浅复制和深复制

https://www.jianshu.com/p/46d76d01c05d

13. 继承和类别区别

https://www.jianshu.com/p/308e8b23ae52

14.类别和类扩展的区别

https://www.jianshu.com/p/308e8b23ae52

15. 什么是KVO和KVC

https://www.jianshu.com/p/4f7732babd0b
https://www.jianshu.com/p/236a013cc244

16.代理的作用

https://www.jianshu.com/p/f3b25ae273eb

17.oc是动态运行时语言是什么意思

主要是将数据类型的确定由编译时,推迟到了运行时。
这个问题其实浅涉及到两个概念,运行时和多态。
https://www.jianshu.com/p/308e8b23ae52多态

18.OC中协议和通知的区别

(1)代理 Delegate
优势

严格的语法。所有的事件必须是在 delegate协议中有清晰的定义,如果 delegate 中的一个方法没有实现那么就会出现编译警告/错误;
在一个应用中的控制流程是可跟踪的并且是可识别的;
在一个控制器中可以定义多个不同的协议,每个协议有不同的 delegate
没有第三方对象要求保持/监视通信过程;
能够接收调用的协议方法的返回值。这意味着 delegate 能够提供反馈信息给委托方;

缺点 :

需要定义很多代码:1.协议定义;2.委托对象的 delegate 属性;3.在 delegate 本身中实现 delegate 方法定义;
在释放代理对象时,需要小心的将 delegate 改为 nil。一旦设定失败,那么调用释放对象的方法将会出现内存 crash;

(2)通知 Notification
优势 :

不需要编写多少代码,实现比较简单;
对于一个发出的通知,多个对象能够做出反应,即1对多的方式实现简单;
被观察者能够传递 context 对象(dictionary),context 对象携带了关于发送通知的自定义的信息;

缺点 :

在编译期不会检查通知是否能够被观察者正确的处理;
在释放注册的对象时,需要在通知中心取消注册(IOS9 之后可以不再手动remove);
在调试的时候应用的工作以及控制过程难跟踪;
被观察者和观察者需要提前知道通知名称、UserInfo dictionary keys。如果这些没有在工作区间定义,那么会出现不同步的情况;
只把消息发送出去,告知某些状态的变化,但是并不关心谁想要知道这个。被观察者也不能从观察者获得任何的反馈信息。

(3)KVO (Key-Value Observing)
优势 :

能够提供一种简单的方法实现两个对象间的同步。例如:model 和 view 之间同步;
能够对非我们创建的对象,即内部对象的状态改变作出响应,而且不需要改变内部对象的实现;
能够提供观察的属性的最新值以及先前值;
用 key paths 来观察属性,因此也可以观察嵌套对象;

缺点 :

我们观察的属性必须使用 strings 来定义。因此在编译器不会出现警告以及检查;
对属性重构将导致我们的观察代码不再可用;
对象正在观察多个值,需要复杂的“IF”语句要求。因为所有的观察代码通过一个方法来指向;
当释放观察者时不需要移除观察者

19.iOS响应链

https://www.jianshu.com/p/5212c141d57e

20.framebounds 不同

    view.frame = CGRectMake(CGFloat x, CGFloat y, CGFloat 
width, CGFloat height)

    view.bounds = CGRectMake(CGFloat x, CGFloat y, 
CGFloat width, CGFloat height)

两者的共同点是后面两个参数都是设置控件的大小,区别是前两个参数,对于 frame 来说,CGFloat x、 CGFloat y 两个参数是 view 相对于父视图的位置,boundsview 上面的子视图在 view 上面的起始位置。

上一篇下一篇

猜你喜欢

热点阅读