面试题知识点梳理

作者: Hengry | 来源:发表于2019-03-20 08:04 被阅读0次

    重点

    • KVC、KVO
    • GCD
    • Runtime iOS开发之Runtime——面试解析
    • runloop
    • Block iOS开发之Block
    • 内存管理、堆栈
    • 通知、代理delegate
    • Net、Https双向认证
    • 数据持久化、数据库
    • MVVM、MVP、MVC
    • 第三方库
    • 性能优化: TableView
    • 响应式编程 RxSwift
    • 与Js交互、WKWebView

    拓展知识点

    面试要求

    1、熟悉iOS开发常用设计模式多线程、数据持久化、网络通信动画效果、界面布局、json解析、自定义控件等

    2、对runtime、GCD、KVO、Block等有一定了解,熟悉iOS内存管理机制,对程序性能优化、内存优化有一定经验。

    1.分类(category)的作用

    • 作用:可以在不修改原来类的基础上,为一个类扩展方法。

    • 最主要的用法:给系统自带的类扩展方法。

    130、多线程

    1、多线程概念

    多条线程是同步完成多项任务,提高资源的使用效率。多核的CPU运行多线程更为出色;在iOS应用中,对多线程最初的理解为并发。

    2、多线程的作用

    实现负载均衡问题,提高cpu利用效率。

    3、使用场景

    数据请求框架、多张图片下载、定时器,视频图像的采集、处理、保存等耗时操作的方法。

    133、进程和线程的区别与联系是什么?

    一个程序至少有个一进程,一个进程至少有一个线程:

    进程:拥有独立的内存单元,而多个线程共享一块内存

    线程:线程是进程内的一个执行单元

    联系:线程是进程的基本组成单位。

    136、对比iOS中的多线程技术

    1. NSThread

      NSThread需要手动管理线程生命周期

    2. GCD

      • GCD仅仅支持FIFO队列,只可以设置队列的优先级。而NSOperationQueue中的每个任务都可以被重新设置优先级(setQueuePriority:),从而实现不同操作的执行顺序调整。
      • GCD的执行速度比NSOperationQueue快
      • GCD不支持异步操作之间的依赖关系设置。如果某个操作依赖另一个操作的数据,使用NSOperationQueue能够设置依赖按照正确的顺序执行操作(addDependency:)。
    3. NSOperationQueue

      • 方便停止队列中的任务(cancelAllOpeations, suspended);GCD不方便停止队列中的任务
      • 支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished)、是否取消(isCanceld)
      • NSOperationQueue可设置最大并发数量(节电), GCD具有dispath_one(只执行一次、单例)和dispatch_after(延时执行)功能
    4. NSObject分类

      NSObject分类(perform)和NSThread遇到对象分配需要手动管理内存和线程生命周期

      NSObject分类线程通信

    137、多线程优缺点

    优点:

    • 是应用程序的响应速度更快,用户界面在进行其他工作的同时仍始终保持活动状态;
    • 优化任务执行,适当提高资源利用率(CPU,内存)

    缺点:

    • 线程占用内存空间,管理线程需要额外的CPU开销,开启大量线程,降低性能;
    • 增加程序复杂度,如线程间通信,多线程资源共享等。

    134、 异步执行两个耗时操作,等两次耗时操作都执行完毕后,再回到主线程执行操作。

    
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 全局并发队列
        
        dispatch_queue_t group = dispatch_queue_create("并行队列", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_group_async(group, queue, ^{ // 异步执行操作1
            // longTime1
        });
        dispatch_group_async(group, queue, ^{ // 异步执行操作2
            // longTime2
        });
        
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 主线程刷新UI
            // reload Data
        });
    

    面试题陷阱

        NSLog(@"1");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"2");
        });
        
        NSLog(@"3");
        
        //奔溃原因:
        // 同步线程不会去创建新的线程。
        // 在同步线程里面执行dispatch_get_main_queue()时会发送线程卡死的现象
        
        /* 正确做法:异步切换主线程
         NSLog(@"1");
         dispatch_async(dispatch_get_main_queue(), ^{
         NSLog(@"2");
         });
         
         NSLog(@"3");
         */
    

    GCD

    1、创建队列

    //OBJECTIVE-C
      //串行队列
     dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
     dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
      //并行队列
     dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    //SWIFT
      //串行队列
      let queue = dispatch_queue_create("tk.bourne.testQueue", nil);
      let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL)
      //并行队列
      let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT)
    
    

    2、全局队列

    //OBJECTIVE-C
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //SWIFT
    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    

    3、创建任务

    // 创建同步任务
    // OBJECTIVE-C
    dispatch_sync(<#queue#>, ^{
          //code here
          NSLog(@"%@", [NSThread currentThread]);
      });
    
    // SWIFT
     dispatch_sync(<#queue#>, { () -> Void in
          //code here
          println(NSThread.currentThread())
      })
    
    // 创建异步任务
    // OBJECTIVE-C
     dispatch_async(<#queue#>, ^{
          //code here
          NSLog(@"%@", [NSThread currentThread]);
      });
    
    // SWIFT
     dispatch_async(<#queue#>, { () -> Void in
          //code here
          println(NSThread.currentThread())
      })
    

    4、延时

    // 创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 设置延时,单位秒
    double delay = 3; 
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{
      // 3秒后需要执行的任务
    });
    

    NSOprationQueue

    NSOperation 有一个非常实用的功能,那就是添加依赖。比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:

    //1.任务一:下载图片
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下载图片 - %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    
    //2.任务二:打水印
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"打水印   - %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    
    //3.任务三:上传图片
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"上传图片 - %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    
    //4.设置依赖
    [operation2 addDependency:operation1];      //任务二依赖任务一
    [operation3 addDependency:operation2];      //任务三依赖任务二
    
    //5.创建队列并加入任务
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
    

    面试题:NSoperationQueue处理A,B,C三个线程,要求执行完A,B后才能执行C。

    参考地址:https://blog.csdn.net/qq_30932479/article/details/79790646

    1. NSOperation添加依赖关系实现
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        NSBlockOperation *A = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"A----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *B = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"B---%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *C = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"C---%@",[NSThread currentThread]);
        }];
        
        [C addDependency:A];
        [C addDependency:B];
        
        [queue addOperation:A];
        [queue addOperation:B];
        [queue addOperation:C];
    
    1. 使用GCD的栅栏函数或者队列组
        /*
         1,开启两个线程处理A和B
         2,通过任务组执行A,B之后执行C
         */
        dispatch_queue_t queue = dispatch_queue_create("dealWith", DISPATCH_QUEUE_CONCURRENT);
        dispatch_group_t group = dispatch_group_create();
        
        dispatch_group_async(group, queue, ^{
            NSLog(@"A----%@",[NSThread currentThread]);
        });
        
        dispatch_group_async(group, queue, ^{
            NSLog(@"B---%@",[NSThread currentThread]);
        });
        
        dispatch_group_notify(group, queue, ^{
            NSLog(@"C---%@",[NSThread currentThread]);
        });
    
    1. GCD添加栅栏函数
        //栅栏函数(栅栏函数不能用全局并发队列)
        //等执行完栅栏函数中的代码才继续执行下面的代码
        
        dispatch_queue_t queue = dispatch_queue_create("dealWith", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_barrier_async(queue, ^{
            NSLog(@"A----%@",[NSThread currentThread]);
        });
        
        dispatch_barrier_async(queue, ^{
            NSLog(@"B---%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"C---%@",[NSThread currentThread]);
        });
    
    

    142、Core Data

    Core Data是iOS 5之后才出现的一个基于Sqlite进行封装的数据持久化框架。它提供了对象-关系映射(ORM)的功能,即能够将OC对象转换为数据,保存到sqlite数据库文件中,也能够将保存在数据库中的数据还原为OC对象。在数据操作期间,不需要编写任何SQL语句。

    • Core Data是对sqlite数据库的封装
    • Core Data中的NSManagedObjectContext在多线程中不安全
    • 多线程访问Core Data,最好的方法是一个线程一个NSManagedObjectContext
    • NSpersistentStoreCoordinator持久化存储调度器,NSpersistentStore对象会将MOC提交的改变同步到数据库中

    如何解决Core Data线程数据同步问题?

    监听通知NSManagedObjectContextDidSaveNotification,在耗时操作处理完之后告诉主上下文哪些改变了。我们可以通过主线程执行合并操作来实现。

    152、UITableView

    1. UITableView最核心的思想

      Cell的重用机制。简单理解:UITableView只会创建一屏幕(或者一屏幕多一点)Cell,其他都是从中取出来重用的。每当Cell滑出屏幕时,就会收到一个Cell集合(复用池)中。当要显示某一位置的Cell时,会先从复用集合中取,如果有则直接拿来显示;如何没有,才会创建新的Cell。这样极大减少了内存的开销。

      tableView:cellForRowAtIndexPath: 方法只负责赋值

      tableView:heightForRowAtIndexPath: 方法只负责计算高度

    2. 自定义高度

    3. UITableView性能优化

      • 缓存行高
      • 异步绘制
      • 异步加载图片以及缓存
      • 滑动时按需加载,特别是加载大量的图片的列表
      • 不要动态创建子视图:所有子视图都预先创建,如果不需要显示设置隐藏
      • 所有子视图都应该添加到contentView上
      • 尽量少用或者不用透明图层
      • cell栅格化
    4. 离屏渲染的问题

      下面的情况或操作会引发离屏渲染问题:

      • 为图层设置遮罩(layer.mask)
      • 将图层的layer.masksToBounds/vew.clipsToBounds属性设置为true
      • 将图层的layer.allowsGroupOpacity属性设置为true、layer.opacity小于1.0
      • 设置阴影layer.shadow
      • layar.shouldRasterize属性为true
      • layar.cornerRadius,
      • 使用CGContext在drawRect:方法中绘制大部分都会导致离屏渲染
    5. 离屏渲染优化方案

      • 圆角优化

        渲染机制是GPU在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作,也就是离屏渲染,这会给我们带来额外的性能损耗。如果圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的频繁切换,性能的代价会宏观地表现在用户体验上—掉帧。

    方案一:使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角图片

     UIImageView *imageView = [[UIImageView alloc] initWithFrame: CGRectMake(100, 100, 100, 100)];
     imageView.image = [UIImage imageNamed: @"myImg"];
         
     // 开始对imagewView进行画图
     UIGraphicsBeginImageContextWith±Options(imageView.bounds.size, NO, 1.0);
     
     // 使用贝塞尔曲线画出一个圆形路径
     [UIBezierPath bezier±PathWithRoundedRect:imageView.bounds cornerRadius: imageView.frame.size.width] addClip];
     
     [imageView drawRect: imageView.bounds];
     
     // 重新设置圆角图片
     imageView.image = UIGraphicsGetImageFromCurrentImageContext();
      
     // 结束画图
     UIGraphicsEndImageContext±();
     [self.view addSubview: imageView];
            
    

    方案二:使用CAShapeLayer和UIBezierPath设置圆角遮罩层

     UIImageView *imageView = [[UIImageView alloc] initWithFrame: CGRectMake(100, 100, 100, 100)];
     imageView.image = [UIImage imageNamed: @"myImg"];
     
     // 使用贝塞尔曲线画出一个圆形路径
     UIBezierPath *maskPath = UIBezierPath bezierPathWithRoundRect: imagewView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii: imageView.bounds.size];
     
     // 创建CAShapeLayer
     CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
     maskLayer.frame = imageView.bounds;
     
     // 设置绘制路径
     maskLayer.path = maskPath.CGPath;
     imageView.layer.mask = maskLayer;
     [self.view addSubView: imageView];
     
    
    • shadow优化

      • 通过设置shadowPath来优化性能,能大幅度提升性能。
      imageView.layer.shadowColor = [UIColor grayColor].CGColor;
      imageView.layer.shadowOpacity = 1.0
      imageView.layer.shadowRadius = 2.0
          
      UIBezierPath *path = [UIBezierPath bezieerPathWithRect: imageView.frame];
      imageView.layer.shadowPath = path.CGPath;
      
    • 异步进行layer渲染 AsyncDisplayKit

    • Core Animation工具检测离屏渲染: Xcode->Open Develeper Tools-> Instruments -> Core Animation

    162、Objective-C堆和栈的区别?

    管理方式:栈是由编译器自动管理,无需我们手动控制;堆释放工作由程序员控制,容易产生memory leak。

    分配方式:堆只有动态分配。栈分为静态分配和动态分配。

    分配效率:栈分配效率高。堆的分配效率相对低。

    栈:

    • 栈向低地址拓展的数据结构,是一块连续的内存区域。先进后出的数据结构。

    堆:

    • 堆向高地址拓展的数据结构,是不连续的内存区域。树形结构。
    • 系统是用链表来存储空闲内存地址,自然是不连续,而链表的遍历方向是由低地址向高地址。堆的大小受限于手机系统中有效的虚拟内存。由此可见,堆获取得空间比较灵活,也比较大。

    145、说说关于UDP/TCP的区别?

    UDP

    • 是不可靠传输协议,不需要建立连接,速度快
    • 将数据及源和目的封装成数据包中
    • 每个数据报的大小限制在64k之内

    TCP

    • 建立连接,形成传输数据通道
    • 通过三次握手完成连接,是可靠协议,安全送达
    • 必须建立连接,效率稍低
    • 连接中进行大数据传输

    TCP与UDP的区别:

    • 基于连接与无连接
    • 对系统资源的要求(TCP较多,UDP较少)
    • 流模式与数据报模式
    • UDP程序结构较简单
    • TCP保证数据正确性、确保顺序;UDP可能丢包,不确保顺序

    TCP传输原理

    1. TCP如何防止乱序和丢包

    2. 描述一下三次握手

      第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

      第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

      第三次握手:客户端收到服务器的SYN+ACK包,并向服务器发送确认标包ACK(ack=k+1),此时发送完毕,客户端和服务器端进入ESTABLISHED状态,完成三次握手。完成三次握手,客户端与服务器端开始传输数据。

    3. 三次握手过程

      第一次握手:建立连接时,客户端发送同步序列编号到服务器,并进入发送状态,等待服务器确认。

      第二次握手:服务器收到同步序列编号,并确认同时自己也发送一个同步序列编号+确认标志,此时服务器进入接收状态

      第三次握手:客户端收到服务器发送的包,并向服务器发送确认标志,随后连接成功。

    148、Block

    1. Block定义格式:

      typedef void(^completion)(BOOL finnished)
      
    2. 使用block时什么情况会发生循环引用,如何解决?

      一个对象中强引用了block,在block中又使用了该对象,就会发生循环引用。解决方法:将该对象使用__weak或者__block修饰符修饰之后再在block中使用。

      __weak typeof(self) weakSelf = self;
      
      __weak typeof(self) weakSelf = self;
      [self doSomeBlockJob:^{
          __strong typeof(weakSelf) strongSelf = weakSelf;
          if (strongSelf) {
              ...
          }
      }];
      

      iOS中block的详解weakSelf、strongSelf https://blog.csdn.net/xgb742951920/article/details/69258372

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

      在block中访问的外部变量是复制过去的,即:写操作不对原变量生效。

      // __block修饰局部变量,这个变量在block内外属于同一个地址上的变量,可以被block内部修改。
      __block int a = 0;
      void (^foo)(void) = ^ {
          a = 1;
      }
      
      foo()
      

    120、KVO、NSNotification、delegate以及block区别?

    122、runtime/消息转发机制

    1. runtime原理

      1.1、runtime基本概念

      runtime是一套OC底层纯C语言编写的库。我们平时编写的OC代码中,程序运行过程,其实最终都是转成了runtime的C语言代码,runtime是OC的幕后工作者。

      1.2、runtime工作原理

      在程序运行过程中,动态创建类:

      objc_allocateClassPair,class_addIvar,objc_registerClassPair.

      动态为某个类添加属性/方法,修改属性/方法(修改封装的框架)

      objc_setAssociatedObjectobjc_setIvar

      遍历一个类的所有成员变量(属性)/方法(字典转模型,归档解析)

      class_copyIvarList,class_copyPropertyList,class_copyMethodList

    2. 消息机制

      2.1 消息转发的原理

      当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类型的调度表查(dispath table)找selector方法。一旦找到selector,objc_msgSend根据调度表的内存地址调用改实现方法。

    消息转发调用方法顺序

    objc_msgSend-->CacheLookup-->objc_msgSend_uncache-->±MethodTableLookup-->class_lookupMethodAnd±LoadCache3-->lookUpImpOrForward

    1. 动态绑定

      动态绑定—在运行时确定要调用的方法

    链表

    1、删除单链表节点

    r = p->pNext; // p后面的结点
    p->pNext = r->pNext; // 修改p的Next结点
    free(r); // 释放内存
    

    2、插入单链表结点

    r = p->pNext;
    p->pNext = q; // p的Next结点指向新结点q
    q->pNext = r; // 新插入结点的Next结点指向r
    

    3、创建单链表

    #include <stdio.h>
    #include <malloc.h>
    
    typedef struct Node
    {
        int data;
        struct Node *pNext;
    }NODE, *PNODE; // NODE等价于struct Node、 PNODE等价于struct NODE *
    
    // 函数声明
    PNODE create_list(void);
    // 遍历链表
    void traverse_list(PNODE pHead);
    // 判断链表是否为空
    bool is_empty(PNODE pHead);
    // 链表的长度
    int length_list(PNODE);
    // 在某个位置上插入结点 
    bool insert_list(PNODE, int, int *);
    // 删除链表
    bool delete_list(PNODE, int, int *);
    // 链表排序
    void sort_list(PNODE);
    
    int main(id)
    {
        
        PNODE pHead = NUll;
        
        pHead = create_list();
        traverse_list(pHead);
        sort_list(pHead);
        
        return 0;
    }
    
    // 创建链表
    PNODE create_list(void)
    {
        int len; // 链表长度
        int i;
        int val; // 临时存放用户输入的结点的值
        
        PNODE pHead = (PNODE)malloc(sizeof(NODE));
        
        printf("请输入您需要生产的链表结点个数: len = ");
        scanf("%d",&len);
        
        for (i =0; i<len; ++i)
        {
            printf("请输入第%d个结点的值:", i+1);
            scanf("%d", &val);
            
            PNODE pNew = (PNODE)malloc(sizeof(NODE));
            if (NULL != pNew)
            {
                printf("分配失败,程序终止!")    
                exit(-1);
            }
            
            // 将pNew挂到链表尾部
            pNex->data = val;
            pHead->pNext = pNew;
            pNew->pNext = NULL:
        }
        
        return pHead;
        
    }
    
    // 判断链表是否为空
    bool is_empty(PNODE pHead)
    {
        if (NULL == pHead->pNext)
        {
            return true;  
        }else{
            return false;
        }
    }
    
    // 链表长度
    int length_list(PNODE pHead)
    {
        PNODE p = pHead->pNext;
        int len = 0;
        while(NULL ! = p)
        {
            ++len;
            p = p->pNext;
        }
        
        return len;
    }
    
    // 遍历
    void traverse_list(PNODE pHead)
    {
        PNODE p = pHead->pNext;
        while(NULL != p)
        {
            printf("%d ", p->data);
            p = p->pNext;
        }
    }
    
    // 链表排序
    void sort_list(PNODE pHead)
    {
        int i, j, t;
        PNODE p, q;
        int len = length_list(pHead);
        
        // 冒泡排序
        for (i=0,p=pHead->pNext; i< len - 1; ++i, p  p->pNext)
        {
            
            for(j=i+1, q=p->pNext; j < len; ++j,q=q->pNext)
            {
                // 后面的结点比前面的大,则交换位置
                if (p->data > q->data) 
                {
                    t = p->data;
                    p->data = q->data;
                    q->data = t;
                }
            }
        }
        
        return;
    }
    
    
    // 在某个位置上插入结点 
    bool insert_list(PNODE pHead, int pos, int val)
    {
        int i = 0;
        PNODE p = pHead;
        
        while(NULL ! = p && i < pos -1)
        {
            p = p->pNext;
            ++i;
        }
        
        if (i > pos - 1 || NULL == p)
            return false;
        
        PNODE pNew = (PNODE)malloc(sizeof(NODE));
        if (NULL == pNew)
        {
            printf("动态分配内存失败!")
        }
        
        pNew->data = val;
        PNODE q = p->pNext;
        p->pNext = pNew;
        pNew->pNext = q;
        return true;
        
    }
    
    // 删除链表中某个下标的结点并返回删除元素pVal 
    bool delete_list(PNODE pHead, int pos, int *pVal)
    {
        int i = 0;
        PNODE p = pHead;
        
        while(NULL ! = p->pNext && i < pos -1)
        {
            p = p->pNext;
            ++i;
        }
        
        if (i > pos - 1 || NULL == p->pNext)
            return false;
        
    
        PNODE q = p->pNext;
        // 删除的值
        *pVal = q->data;
        
        // 删除结点后面的结点
        p->pNext = p->pNext->pNext;
        free(q);
        q = NULL:
        return true;
        
    }
    

    数据结构概念

    狭义:

    • 数据结构是专门研究数据存储的问题
    • 数据的存储包含两方面:个体的存储+个体关系的存储

    广义:

    • 数据结构既包含数据的存储也包含数据的操作
    • 对存储数据的操作就是算法

    算法概念

    狭义:算法是和数据的存储方式密切相关

    广义:算法和数据的存储方式无关

    数据的存储结构:

    • 线性:
      • 连续存储【数组】
        • 优点:存取元素的效率非常高
        • 缺点:
          • 事先必须知道数组的长度
          • 需要大块连续的内存块
          • 插入、删除元素的效率极低
      • 离散存储【链表】
        • 优点:
          • 空间没有限制
          • 插入、删除元素很快
        • 缺点:
          • 存取速度很慢
      • 线性结构的应用—栈
      • 线性结构的应用—队列
    • 非线性

    队列

    170、UIKit、CoreAnimation和CoreGraphics的关系是什么?在开发过程中是否使用到CoreAnimation 和CoreGraphics?

    178、动画

    CAAnimation 动画基类

    • CAPropertyAnimation 属性动画抽象类
      • CABasicAnimation 基础动画
      • CAKeyframeAnimation 关键帧动画
    • CAAnimationGroup 组动画
    • CATransition 转场动画

    相关文章

      网友评论

        本文标题:面试题知识点梳理

        本文链接:https://www.haomeiwen.com/subject/jwlipqtx.html