面试题知识点梳理

重点

拓展知识点

面试要求

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

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

1.分类(category)的作用

  • 作用:可以在不修改原来类的基础上,为一个类扩展方法。
  • 最主要的用法:给系统自带的类扩展方法。

2、响应链

iOS-事件传递,响应者链条及常见面试题

iOS响应者链彻底掌握

3.栈、堆、静态区域的区别

OC中,非对象的变量都存在栈中,对象都存在堆中,静态区域的内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,主要存放静态数据、全局数据和常量。

栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存效率高,内存容量有限。

堆区:OC对象存储于堆中,当对象的应用计数为0时自动释放该对象。

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

管理方式:栈是由编译器自动管理,无需我们手动控制;堆释放工作由程序员控制,容易产生memory leak。
分配方式:堆只有动态分配。栈分为静态分配和动态分配。
分配效率:栈分配效率高。堆的分配效率相对低。

栈:

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

堆:

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

4、isMember和isKind的区别?

1、isMember用来判断该对象是否为指定类的对象

2、isKind 用来判断该对象是否为指定类或者指定类的子类的对象

3、is用法与isKind类型,上面两种只能用于类对象的判断,对于枚举和结构体同样适用

5、字典大致实现原理

一:字典原理

NSDictionary(字典)是使用hash表来实现key和value之间的映射和存储的
方法:- (void)setObject:(id)anObject forKey:(id)aKey;
Objective-C中的字典NSDictionary底层其实是一个哈希表

二:哈希原理

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

哈希概念:哈希表的本质是一个数组,数组中每一个元素称为一个箱子(bin),箱子中存放的是键值对。

三:哈希存储过程

1.根据 key 计算出它的哈希值 h。
2.假设箱子的个数为 n,那么这个键值对应该放在第 (h % n) 个箱子中。
3.如果该箱子中已经有了键值对,就使用开放寻址法或者拉链法解决冲突。

在使用拉链法解决哈希冲突时,每个箱子其实是一个链表,属于同一个箱子的所有键值对都会排列在链表中。

哈希表还有一个重要的属性: 负载因子(load factor),它用来衡量哈希表的空/满程度,一定程度上也可以体现查询的效率,计算公式为:
负载因子 = 总键值对数 / 箱子个数

负载因子越大,意味着哈希表越满,越容易导致冲突,性能也就越低。因此,一般来说,当负载因子大于某个常数(可能是 1,或者 0.75 等)时,哈希表将自动扩容。
哈希表在自动扩容时,一般会创建两倍于原来个数的箱子,因此即使 key 的哈希值不变,对箱子个数取余的结果也会发生改变,因此所有键值对的存放位置都有可能发生改变,这个过程也称为重哈希(rehash)。
哈希表的扩容并不总是能够有效解决负载因子过大的问题。假设所有 key 的哈希值都一样,那么即使扩容以后他们的位置也不会变化。虽然负载因子会降低,但实际存储在每个箱子中的链表长度并不发生改变,因此也就不能提高哈希表的查询性能。

基于以上总结,细心的朋友可能会发现哈希表的两个问题:
1.如果哈希表中本来箱子就比较多,扩容时需要重新哈希并移动数据,性能影响较大。
2.如果哈希函数设计不合理,哈希表在极端情况下会变成线性表,性能极低。

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

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_lookupMethodAndLoadCache3-->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 转场动画

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,076评论 1 32
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述?设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型...
    龍飝阅读 2,126评论 0 12
  • 史上最全的iOS面试题及答案 iOS面试小贴士———————————————回答好下面的足够了----------...
    Style_伟阅读 2,341评论 0 35
  • 把网上的一些结合自己面试时遇到的面试题总结了一下,以后有新的还会再加进来。 1. OC 的理解与特性 OC 作为一...
    AlaricMurray阅读 2,531评论 0 20
  • 多少事, 纷纷扰扰, 让你喜让你恼, 让你怨恨哀叹, 让你总是忘不了! 你总是想到桃花源去, 企图把一切事办的美好...
    田尔盛阅读 531评论 1 20