第三方框架

1、AFNetworking 底层原理分析
AFNetworking主要是对NSURLSession和NSURLConnection(iOS9.0废弃)的封装,其中主要有以下类:
1). AFHTTPRequestOperationManager:内部封装的是 NSURLConnection, 负责发送网络请求, 使用最多的一个类。(3.0废弃)
2). AFHTTPSessionManager:内部封装是 NSURLSession, 负责发送网络请求,使用最多的一个类。
3). AFNetworkReachabilityManager:实时监测网络状态的工具类。当前的网络环境发生改变之后,这个工具类就可以检测到。
4). AFSecurityPolicy:网络安全的工具类, 主要是针对 HTTPS 服务。
5). AFURLRequestSerialization:序列化工具类,基类。上传的数据转换成JSON格式(AFJSONRequestSerializer).使用不多。
6). AFURLResponseSerialization:反序列化工具类;基类.使用比较多:
7). AFJSONResponseSerializer; JSON解析器,默认的解析器.
8). AFHTTPResponseSerializer; 万能解析器; JSON和XML之外的数据类型,直接返回二进
制数据.对服务器返回的数据不做任何处理.
9). AFXMLParserResponseSerializer; XML解析器;

2、描述下SDWebImage里面给UIImageView加载图片的逻辑
SDWebImage 中为 UIImageView 提供了一个分类UIImageView+WebCache.h, 这个分类中有一个最常用的接口sd_setImageWithURL:placeholderImage:,会在真实图片出现前会先显示占位图片,当真实图片被加载出来后再替换占位图片。

加载图片的过程大致如下:
1.首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 作为数据的索引先在内存中寻找是否有对应的缓存
2.如果缓存未找到就会利用通过MD5处理过的key来继续在磁盘中查询对应的数据, 如果找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来
3.如果在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片
4.下载后的图片会加入缓存中,并写入磁盘中
5.整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来

SDWebImage原理:
调用类别的方法:
1. 从内存(字典)中找图片(当这个图片在本次使用程序的过程中已经被加载过),找到直接使用。
2. 从沙盒中找(当这个图片在之前使用程序的过程中被加载过),找到使用,缓存到内存中。
3. 从网络上获取,使用,缓存到内存,缓存到沙盒。

3、友盟统计接口统计的所有功能
APP启动速度,APP停留页面时间等

算法
1、不用中间变量,用两种方法交换A和B的值
// 1.中间变量
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}

// 2.加法
void swap(int a, int b) {
a = a + b;
b = a - b;
a = a - b;
}

// 3.异或(相同为0,不同为1. 可以理解为不进位加法)
void swap(int a, int b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
​```

2、求最大公约数 /** 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)/最大公约数

3、模拟栈操作 /**

  • 栈是一种数据结构,特点:先进后出
  • 练习:使用全局变量模拟栈的操作 */ #include #include #include //保护全局变量:在全局变量前加static后,这个全局变量就只能在本文件中使用 static int data[1024];//栈最多能保存1024个数据 static int count = 0;//目前已经放了多少个数(相当于栈顶位置)

//数据入栈 push void push(int x){ assert(!full());//防止数组越界 data[count++] = x; } //数据出栈 pop int pop(){ assert(!empty()); return data[–count]; } //查看栈顶元素 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; }

4、排序算法
选择排序、冒泡排序、插入排序三种排序算法可以总结为如下:
都将数组分为已排序部分和未排序部分。

  1. 选择排序将已排序部分定义在左端,然后选择未排序部分的最小元素和未排序部分的第一个元素交换。
  2. 冒泡排序将已排序部分定义在右端,在遍历未排序部分的过程执行交换,将最大元素交换到最右端。
  3. 插入排序将已排序部分定义在左端,将未排序部分元的第一个元素插入到已排序部分合适的位置。

选择排序
/**

  • 【选择排序】:最值出现在起始端
  • 第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; } } } }

5、折半查找(二分查找)
/**

  • 折半查找:优化查找时间(不用遍历全部数据) *
  • 折半查找的原理:
  • 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; }

​```

编码格式(优化细节)
1、在 Objective-C 中,enum 建议使用 NS_ENUM 和 NS_OPTIONS 宏来定义枚举类型。 //定义一个枚举(比较严密) typedef NS_ENUM(NSInteger, BRUserGender) { BRUserGenderUnknown, // 未知 BRUserGenderMale, // 男性 BRUserGenderFemale, // 女性 BRUserGenderNeuter // 无性 };

@interface BRUser : NSObject

@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 时就应该尽量使用不可变的对象:其三个属性都应该设为“只读”。用初始化方法设置好属性值之后,就不能再改变了。 //属性的参数应该按照下面的顺序排列: (原子性,读写,内存管理)

2、避免使用C语言中的基本数据类型,建议使用 Foundation 数据类型,对应关系如下: int -> NSInteger unsigned -> NSUInteger float -> CGFloat 动画时间 -> NSTimeInterval

​```

#其它知识点 1、HomeKit,是苹果2014年发布的智能家居平台。

2、什么是 OpenGL、Quartz 2D?

Quatarz 2d 是Apple提供的基本图形工具库。只是适用于2D图形的绘制。 OpenGL,是一个跨平台的图形开发库。适用于2D和3D图形的绘制。

3、ffmpeg框架:​ffmpeg 是音视频处理工具,既有音视频编码解码功能,又可以作为播放器使用。

4、谈谈 UITableView 的优化
1). 正确的复用cell。
2). 设计统一规格的Cell
3). 提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;
4). 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;
4). 滑动时按需加载,这个在大量图片展示,网络加载的时候很管用!
5). 减少子视图的层级关系
6). 尽量使所有的视图不透明化以及做切圆操作。
7). 不要动态的add 或者 remove 子控件。最好在初始化时就添加完,然后通过hidden来控制是否显示。
8). 使用调试工具分析问题。

5、如何实行cell的动态的行高
如果希望每条数据显示自身的行高,必须设置两个属性,1.预估行高,2.自定义行高。
设置预估行高 tableView.estimatedRowHeight = 200。
设置定义行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。
如果要让自定义行高有效,必须让容器视图有一个自下而上的约束。

6 如何让计时器调用一个类方法
计时器只能调用实例方法,但是可以在这个实例方法里面调用静态方法。
使用计时器需要注意,计时器一定要加入RunLoop中,并且选好model才能运行。 scheduledTimerWithTimeInterval方法创建一个计时器并加入到RunLoop中所以可以直接使用。
如果计时器的repeats选择YES说明这个计时器会重复执行,一定要在合适的时机调用计时器的invalid。不能在dealloc中调用,因为一旦设置为repeats 为yes,计时器会强持有self,导致dealloc永远不会被调用,这个类就永远无法被释放。比如可以在viewDidDisappear中调用,这样当类需要被回收的时候就可以正常进入dealloc中了。

[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];

-(void)timerMethod { //调用类方法 [[self class] staticMethod]; }

-(void)invalid { [timer invalid]; timer = nil; }

7 如何重写类方法
1、在子类中实现一个同基类名字一样的静态方法
2、在调用的时候不要使用类名调用,而是使用[self class]的方式调用。原理,用类名调用是早绑定,在编译期绑定,用[self class]是晚绑定,在运行时决定调用哪个方法。

8 NSTimer创建后,会在哪个线程运行。
用scheduledTimerWithTimeInterval创建的,在哪个线程创建就会被加入哪个线程的RunLoop中就运行在哪个线程
自己创建的Timer,加入到哪个线程的RunLoop中就运行在哪个线程。

9 id和NSObject*的区别
id是一个 objc_object 结构体指针,定义是
typedef struct objc_object *id
id可以理解为指向对象的指针。所有oc的对象 id都可以指向,编译器不会做类型检查,id调用任何存在的方法都不会在编译阶段报错,当然如果这个id指向的对象没有这个方法,该崩溃还是会崩溃的。

NSObject *指向的必须是NSObject的子类,调用的也只能是NSObjec里面的方法否则就要做强制类型转换。

不是所有的OC对象都是NSObject的子类,还有一些继承自NSProxy。NSObject *可指向的类型是id的子集。

10.ios开发逆向传值的几种方法整理
第一种:代理传值
第二个控制器:
@protocol WJSecondViewControllerDelegate

  • (void)changeText:(NSString*)text; @end @property(nonatomic,assign)iddelegate;

  • (IBAction)buttonClick:(UIButton*)sender { _str = sender.titleLabel.text; [self.delegate changeText:sender.titleLabel.text]; [self.navigationController popViewControllerAnimated:YES]; }

第一个控制器:

  • (IBAction)pushToSecond:(id)sender { WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@”WJSecondViewController” bundle:nil]; svc.delegate = self; svc.str = self.navigationItem.title; [self.navigationController pushViewController:svc animated:YES]; [svc release]; }
  • (void)changeText:(NSString *)text{ self.navigationItem.title = text; }

第二种:通知传值 第一个控制器: //注册监听通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(limitDataForModel:) name:@”NOV” object:nil];

  • (void)limitDataForModel:(NSNotification *)noti{ self.gamesInfoArray = noti.object; }

第二个控制器: //发送通知 [[NSNotificationCenter defaultCenter] postNotificationName:@”NOV” object:gameArray];

第三种:单例传值 Single是一个单例类,并且有一个字符串类型的属性titleName 在第二个控制器:

  • (IBAction)buttonClick:(UIButton*)sender { Single *single = [Single sharedSingle]; single.titleName = sender.titleLabel.text; [self.navigationController popViewControllerAnimated:YES]; }

第一个控制器:

  • (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; Single *single = [Single sharedSingle]; self.navigationItem.title = single.titleName; }

第四种:block传值 第二个控制器: @property (nonatomic,copy) void (^changeText_block)(NSString*);

  • (IBAction)buttonClick:(UIButton*)sender { _str = sender.titleLabel.text; self.changeText_block(sender.titleLabel.text); [self.navigationController popViewControllerAnimated:YES]; }

第一个控制器:

  • (IBAction)pushToSecond:(id)sender { WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@”WJSecondViewController” bundle:nil]; svc.str = self.navigationItem.title; [svc setChangeText_block:^(NSString *str) {

    self.navigationItem.title = str; }]; [self.navigationController pushViewController:svc animated:YES]; }

第五种:extern传值 第二个控制器: extern NSString *btn;

  • (IBAction)buttonClick:(UIButton*)sender { btn = sender.titleLabel.text; [self.navigationController popViewControllerAnimated:YES]; }

第一个控制器: NSString *btn = nil;

  • (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; self.navigationItem.title = btn; }

第六种:KVO传值 第一个控制器:

  • (void)viewDidLoad { [super viewDidLoad]; _vc =[[SecondViewController alloc]init]; //self监听vc里的textValue属性 [_vc addObserver:self forKeyPath:@”textValue” options:0 context:nil];
    }

第二个控制器:

  • (IBAction)buttonClicked:(id)sender { self.textValue = self.textField.text; [self.navigationController popViewControllerAnimated:YES]; }

11.浅谈iOS开发中方法延迟执行的几种方式
Method1. performSelector方法

Method2. NSTimer定时器

Method3. NSThread线程的sleep

Method4. GCD

12.NSPersistentStoreCoordinator , NSManaged0bjectContext 和NSManaged0bject中的那些需要在线程中创建或者传递 答:NSPersistentStoreCoordinator是持久化存储协调者,主要用于协调 托管对象上下文和持久化存储区之间的关系。NSManagedObjectContext使用协调者的托管对象模型将数据保存到数 据库,或查询数据。

13.您是否做过一部的网络处理和通讯方面的工作?如果有,能具体介绍一下实现策略么 答:使用NSOperation发送异步网络请求,使用NSOperationQueue管理 线程数目及优先级,底层是用NSURLConnetion,

14.你使用过Objective-C的运行时编程(Runtime Programming)么?如果使用过,你用它做了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗? 答:Objecitve-C的重要特性是Runtime(运行时),在#import <objc/runtime.h> 下能看到相关的方法,用过objc_getClass()和class_copyMethodList()获取过私有API;使用
objective-c Method method1 = class_getInstanceMethod(cls, sel1); Method method2 = class_getInstanceMethod(cls, sel2); method_exchangeImplementations(method1, method2);

代码交换两个方法,在写unit test时使用到。 15.Core开头的系列的内容。是否使用过CoreAnimation和CoreGraphics。UI框架和CA,CG框架的联系是什么?分别用CA和CG做过些什么动画或者图像上的内容。(有需要的话还可以涉及Quartz的一些内容) 答:UI框架的底层有CoreAnimation,CoreAnimation的底层有CoreGraphics。
UIKit | ———— | Core Animation | Core Graphics | Graphics Hardware|
使用CA做过menu菜单的展开收起(太逊了)

16.是否使用过CoreText或者CoreImage等?如果使用过,请谈谈你使用CoreText或者CoreImage的体验。 答:CoreText可以解决复杂文字内容排版问题。CoreImage可以处理图 片,为其添加各种效果。体验是很强大,挺复杂的。

17.NSNotification和KVO的区别和用法是什么?什么时候应该使用通知,什么时候应该使用KVO,它们的实现上有什么区别吗?如果用protocol和delegate(或者delegate的Array)来实现类似的功能可能吗?如果可能,会有什么潜在的问题?如果不能,为什么?(虽然protocol和delegate这种东西面试已经面烂了…) 答:NSNotification是通知模式在iOS的实现,KVO的全称是键值观察 (Key-value observing),其是基于KVC(key-value coding)的,KVC是一 个通过属性名访问属性变量的机制。例如将Module层的变化,通知到多 个Controller对象时,可以使用NSNotification;如果是只需要观察某个 对象的某个属性,可以使用KVO。 对于委托模式,在设计模式中是对象适配器模式,其是delegate是指向 某个对象的,这是一对一的关系,而在通知模式中,往往是一对多的关 系。委托模式,从技术上可以现在改变delegate指向的对象,但不建议 这样做,会让人迷惑,如果一个delegate对象不断改变,指向不同的对 象。

18.你用过NSOperationQueue么?如果用过或者了解的话,你为什么要使用NSOperationQueue,实现了什么?请描述它和G.C.D的区别和类似的地方(提示:可以从两者的实现机制和适用范围来描述)。 答:使用NSOperationQueue用来管理子类化的NSOperation对象,控制 其线程并发数目。GCD和NSOperation都可以实现对线程的管理,区别 是 NSOperation和NSOperationQueue是多线程的面向对象抽象。项目中 使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中 使用它,会使项目的程序结构更好,子类化NSOperation的设计思路, 是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接 口简单,建议在复杂项目中使用。 项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线 程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议 在简单项目中使用。

19.既然提到G.C.D,那么问一下在使用G.C.D以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么? 答:使用block是要注意,若将block做函数参数时,需要把它放到最 后,GCD是Grand Central Dispatch,是一个对线程开源类库,而Block 是闭包,是能够读取其他函数内部变量的函数。

对于Objective-C,你认为它最大的优点和最大的不足是什么?对于不足之处,现在有没有可用的方法绕过这些不足来实现需求。如果可以的话,你有没有考虑或者实践过重新实现OC的一些功能,如果有,具体会如何做?

答:最大的优点是它的运行时特性,不足是没有命名空间,对于命名冲 突,可以使用长命名法或特殊前缀解决,如果是引入的第三方库之间的 命名冲突,可以使用link命令及flag解决冲突。

你实现过一个框架或者库以供别人使用么?如果有,请谈一谈构建框架或者库时候的经验;如果没有,请设想和设计框架的public的API,并指出大概需要如何做、需要注意一些什么方面,来使别人容易地使用你的框架。

答:抽象和封装,方便使用。首先是对问题有充分的了解,比如构建一 个文件解压压缩框架,从使用者的角度出发,只需关注发送给框架一个 解压请求,框架完成复杂文件的解压操作,并且在适当的时候通知给是 哦难过者,如解压完成、解压出错等。在框架内部去构建对象的关系, 通过抽象让其更为健壮、便于更改。其次是API的说明文档。