第三方框架
AFNetworking 底层原理分析
AFNetworking主要是对NSURLSession和NSURLConnection(iOS9.0废弃)的封装,其中主要有以下类:
- AFHTTPRequestOperationManager:内部封装的是 NSURLConnection, 负责发送网络请求, 使用最多的一个类。(3.0废弃)
- AFHTTPSessionManager:内部封装是 NSURLSession, 负责发送网络请求,使用最多的一个类。
- AFNetworkReachabilityManager:实时监测网络状态的工具类。当前的网络环境发生改变之后,这个工具类就可以检测到。
- AFSecurityPolicy:网络安全的工具类, 主要是针对 HTTPS 服务。
- AFURLRequestSerialization:序列化工具类,基类。上传的数据转换成JSON格式 (AFJSONRequestSerializer).使用不多。
- AFURLResponseSerialization:反序列化工具类;基类.使用比较多:
- AFJSONResponseSerializer; JSON解析器,默认的解析器.
- AFHTTPResponseSerializer; 万能解析器; JSON和XML之外的数据类型,直接返回二进制数据.对服务器返回的数据不做任何处理.
- AFXMLParserResponseSerializer; XML解析器;
描述下SDWebImage里面给UIImageView加载图片的逻辑
SDWebImage 中为 UIImageView 提供了一个分类UIImageView+WebCache.h, 这个分类中有一个最常用的接口sd_setImageWithURL:placeholderImage:,会在真实图片出现前会先显示占位图片,当真实图片被加载出来后再替换占位图片。
加载图片的过程大致如下:
- 首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 作为数据的索引先在内存中寻找是否有对应的缓存
- 如果缓存未找到就会利用通过MD5处理过的key来继续在磁盘中查询对应的数据, 如果找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来
- 如果在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片
- 下载后的图片会加入缓存中,并写入磁盘中
- 整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来
SDWebImage原理:
调用类别的方法:
-
从内存(字典)中找图片(当这个图片在本次使用程序的过程中已经被加载过),找到直接使用。
-
从沙盒中找(当这个图片在之前使用程序的过程中被加载过),找到使用,缓存到内存中。
-
从网络上获取,使用,缓存到内存,缓存到沙盒。
友盟统计接口统计的所有功能
APP启动速度,APP停留页面时间等
算法
求最大公约数
/** 1.直接遍历法 */
int maxCommonDivisor(int a, int b) {
int max = 0;
for (int i = 1; i <=b; i++) {
if (a % i == 0 && b % i == 0) {
max = i;
}
}
return max;
}
/** 2.辗转相除法 */
int maxCommonDivisor(int a, int b) {
int r;
while(a % b > 0) {
r = a % b;
a = b;
b = r;
return b;
}
}
// 扩展:最小公倍数 = (a * b)/最大公约数
模拟栈操作
/**
* 栈是一种数据结构,特点:先进后出
* 练习:使用全局变量模拟栈的操作
*/
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
//保护全局变量:在全局变量前加static后,这个全局变量就只能在本文件中使用
//栈最多能保存1024个数据
static int data[1024];
//目前已经放了多少个数(相当于栈顶位置)
static int count = 0;
//数据入栈 push
void push(int x){
//防止数组越界
assert(!full());
data[count++] = x;
}
//查看栈顶元素 top
int top(){
assert(!empty());
return data[count-1];
}
//查询栈满 full
bool full() {
if(count >= 1024) {
return 1;
}
return 0;
}
//查询栈空 empty
bool empty() {
if(count <= 0) {
return 1;
}
return 0;
}
int main(){
//入栈
for (int i = 1; i <= 10; i++) {
push(i);
}
}
//出栈
while(!empty()){
printf("%d ", top());
//栈顶元素
pop();
//出栈
}
printf("\n");
return 0;
}
排序算法
选择排序、冒泡排序、插入排序三种排序算法可以总结为如下:
/**
*【选择排序】:最值出现在起始端
* 第1趟:在n个数中找到最小(大)数与第一个数交换位置
* 第2趟:在剩下n-1个数中找到最小(大)数与第二个数交换位置
* 重复这样的操作...依次与第三个、第四个...数交换位置
* 第n-1趟,最终可实现数据的升序(降序)排列。
* */
void selectSort(int *arr, int length) {
for (int i = 0; i < length - 1; i++) {
//趟数
for (int j = i + 1; j < length; j++) {
//比较次数
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
/** * 【冒泡排序】:相邻元素两两比较,比较完一趟,最值出现在末尾
* 第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n个元素位置 * 第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n-1个元素位置
* …… ……
* 第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第2个元素位置
*/
void bublleSort(int *arr, int length) {
for(int i = 0; i < length - 1; i++) {
//趟数
for(int j = 0; j < length - i - 1; j++) {
//比较次数
if(arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
都将数组分为已排序部分和未排序部分。
- 选择排序将已排序部分定义在左端,然后选择未排序部分的最小元素和未排序部分的第一个元素交换。
- 冒泡排序将已排序部分定义在右端,在遍历未排序部分的过程执行交换,将最大元素交换到最右端。
- 插入排序将已排序部分定义在左端,将未排序部分元的第一个元素插入到已排序部分合适的位置。
选择排序
冒泡排序
折半查找(二分查找)
/** * 折半查找:优化查找时间(不用遍历全部数据)
* 折半查找的原理:
* 1> 数组必须是有序的
* 2> 必须已知min和max(知道范围)
* 3> 动态计算mid的值,取出mid对应的值进行比较
* 4> 如果mid对应的值大于要查找的值,那么max要变小为mid-1
* 5> 如果mid对应的值小于要查找的值,那么min要变大为mid+1
* */
// 已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置
int findKey(int *arr, int length, int key) {
int min = 0, max = length - 1, mid;
while (min <= max) {
mid = (min + max) / 2;
//计算中间值
if (key > arr[mid]) {
min = mid + 1;
} else if (key < arr[mid]) {
max = mid - 1;
} else {
return mid;
}
}
return -1;
}
编码格式(优化细节)
在 Objective-C 中,enum 建议使用 NS_ENUM 和 NS_OPTIONS 宏来定义枚举类型。
//定义一个枚举(比较严密)
typedef NS_ENUM(NSInteger, BRUserGender) {
BRUserGenderUnknown,
// 未知
BRUserGenderMale,
// 男性
BRUserGenderFemale,
// 女性
BRUserGenderNeuter
// 无性
};
@interface BRUser : NSObject<NSCopying>
@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) BRUserGender gender;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age gender:(BRUserGender)gender;
@end
//说明:
//既然该类中已经有一个“初始化方法” ,用于设置 name、age 和 gender 的初始值: 那么在设计对应 @property 时就应该尽量使用不可变的对象:其三个属性都应该设为“只读”。用初始化方法设置好属性值之后,就不能再改变了。
//属性的参数应该按照下面的顺序排列: (原子性,读写,内存管理)
避免使用C语言中的基本数据类型,建议使用 Foundation 数据类型,对应关系如下:
- int -> NSInteger
- unsigned -> NSUInteger
- float -> CGFloat
- 动画时间 -> NSTimeInterval
其它知识点
一.什么是 OpenGL、Quartz 2D?
Quatarz 2d 是Apple提供的基本图形工具库。只是适用于2D图形的绘制。
OpenGL,是一个跨平台的图形开发库。适用于2D和3D图形的绘制。
ffmpeg框架:ffmpeg 是音视频处理工具,既有音视频编码解码功能,又可以作为播放器使用。
谈谈 UITableView 的优化
- 正确的复用cell。
- 设计统一规格的Cell
- 提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;
- 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;
- 滑动时按需加载,这个在大量图片展示,网络加载的时候很管用!
- 减少子视图的层级关系
- 尽量使所有的视图不透明化以及做切圆操作。
- 不要动态的add 或者 remove 子控件。最好在初始化时就添加完,然后通过hidden来控制是否显示。
- 使用调试工具分析问题。
二.如何实行cell的动态的行高
如果希望每条数据显示自身的行高,必须设置两个属性,1.预估行高,2.自定义行高。
设置预估行高 tableView.estimatedRowHeight = 200。
设置定义行高 tableView.estimatedRowHeight=UITableViewAutomaticDimension。
如果要让自定义行高有效,必须让容器视图有一个自下而上的约束。
三.说说你对 block 的理解
栈上的自动复制到堆上,block 的属性修饰符是 copy,循环引用的原理和解决方案。
四.说说你对 runtime 的理解
主要是方法调用时如何查找缓存,如何找到方法,找不到方法时怎么转发,对象的内存布局。
五.什么是野指针、空指针?
野指针:不知道指向了哪里的指针叫野指针。即指针指向不确定,指针存的地址是一个垃圾值,未初始化。
空指针:不指向任何位置的指针叫空指针。即指针没有指向,指针存的地址是一个空地址,NULL。
六.什么是 OOA / OOD / OOP ?
OOA(Object Oriented Analysis) --面向对象分析
OOD(Object Oriented Design) --面向对象设计
OOP(Object Oriented Programming)--面向对象编程
七.多线程是什么
多线程是个复杂的概念,按字面意思是同步完成多项任务,提高了资源的使用效率,从硬件、操作系统、应用软件不同的角度去看,多线程被赋予不同的内涵,对于硬件,现在市面上多数的CPU都是多核的,多核的CPU运算多线程更为出色;从操作系统角度,是多任务,现在用的主流操作系统都是多任务的,可以一边听歌、一边写博客;对于应用来说,多线程可以让应用有更快的回应,可以在网络下载时,同时响应用户的触摸操作。在iOS应用中,对多线程最初的理解,就是并发,它的含义是原来先做烧水,再摘菜,再炒菜的工作,会变成烧水的同时去摘菜,最后去炒菜。
八. iOS 中的多线程
iOS中的多线程,是Cocoa框架下的多线程,通过Cocoa的封装,可以让我们更为方便的使用线程,做过C++的同学可能会对线程有更多的理解,比如线程的创立,信号量、共享变量有认识,Cocoa框架下会方便很多,它对线程做了封装,有些封装,可以让我们创建的对象,本身便拥有线程,也就是线程的对象化抽象,从而减少我们的工程,提供程序的健壮性。
GCD是(Grand Central Dispatch)的缩写 ,从系统级别提供的一个易用地多线程类库,具有运行时的特点,能充分利用多核心硬件。GCD的API接口为C语言的函数,函数参数中多数有Block,关于Block的使用参看这里,为我们提供强大的“接口”,对于GCD的使用参见本文
NSOperation与Queue
NSOperation是一个抽象类,它封装了线程的细节实现,我们可以通过子类化该对象,加上NSQueue来同面向对象的思维,管理多线程程序。具体可参看这里:一个基于NSOperation的多线程网络访问的项目
NSThread
NSThread是一个控制线程执行的对象,它不如NSOperation抽象,通过它我们可以方便的得到一个线程,并控制它。但NSThread的线程之间的并发控制,是需要我们自己来控制的,可以通过NSCondition实现。
参看 iOS多线程编程之NSThread的使用
其他多线程
在Cocoa的框架下,通知、Timer和异步函数等都有使用多线程,(待补充).
九. 在项目什么时候选择使用GCD,什么时候选择NSOperation?
项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。
项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
十 KVO,NSNotification,delegate及block区别
- NSNOtification通知是一对多的关系,一个对象向所有观察者提供变更通知;
- Block是一对一的回调机制,更加简洁;但是当通信事件比较多时,建议使用代理;
- Delegate也是一对一的回调机制,需要协议方法。代理对象实现协议方法,并且需要建立代理关系才能进行通信;
- KVO观察者模式,是当被观察者对象的属性发生改变时,会向观察者发送一条改变的通知的设计模式。
十一 将一个函数在主线程执行的4种方法
dispatch_async(dispatch_get_main_queue(), ^{
//需要执行的方法
});
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
//主队列
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
//需要执行的方法
}];
[mainQueue addOperation:operation];
[self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil];
[self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES];
[[NSThread mainThread] performSelector:@selector(method) withObject:nil];
[[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil];
小编这呢,给大家推荐一个优秀的iOS交流平台,平台里的伙伴们都是非常优秀的iOS开发人员,我们专注于技术的分享与技巧的交流,大家可以在平台上讨论技术,交流学习。欢迎大家的加入(想要加入的可加小编微信15673450590)。
原文链接:www.jianshu.com/p/99e7ff283…
有未完善之处已补充