——青灯素笺
-
多线程的底层实现
1.首先搞清楚是什么线程、什么是多线程
2.Mach是第一个以多线程方式处理任务的系统,因此多线程的底层实现机制是基于Mach的线程
3.开发中很少用Mach级的线程,因为Mach级的线程没有提供多线程的基本特征,线程之间是独立的
4.开发中实现多线程的方案
* C语言的POSIX接口:#include <pthread.h>
* OC的NSThread
* C语言的GCD接口(性能最好,代码更精简)
* OC的NSOperation和NSOperationQueue(基于GCD)
-
weak和assign的区别
weak:用于各种UI控件以及代理(PS:weak是在用Xib或者StoryBord拖拽的时候的默认属性,在自己用代码流定义控件的时候,在把控不好的情况下,用strong即可)
assign:用于基本数据类型(PS:比如:NSInteger、long、int、float、double、BOOL)
-
Objective-C支持多继承么?
Objective-C不支持多继承。可以通过遵循多个协议来实现多继承。
-
#import "XXX.h";和@class + 类名;的区别
1.#import会包含这个类的所有信息,包括实体变量和方法;而@class只是告诉编译器,其后面声明的名称是类的名称。相当于,@class对当前类说:这些类是如何定义的,你暂时不用考虑知道,后面会再告诉你的。
2.在头文件中,一般只需要知道被引用的类的名称就可以了。不需要知道其内部的实体变量和方法具体是啥。所以,在X的头文件(即X.h)中一般使用@class来声明这个名称是类的名称。而在X的实现类(即X.m)里面,因为会用到这个引用类的内部的实体变量和方法。所以需要使用#import来包含这个被引用类的头文件。
3.从编译效率方面考虑,如果你有100个头文件都#import了同一个头文件,或者这些头文件是属于依次引用的关系,例如:A--->B,B--->C,C--->D,D--->E,……,这样的引用关系。当最开始的那个头文件有变化的话,后面所有引用它的类都需要重新编译,而此时恰好你的类很多很多的话,那将会耗费大量的时间。而,使用@class则不会。
4.如果有循环依赖关系(即相互引用关系),例如:A--->B,B--->A,这样的相互依赖关系,此时再使用#import来相互包含,那么就会出现编译错误;如果使用@class在两个类的头文件(即X.h)中相互声明,则不会出现编译错误,然后再在两个类的实现文件(即X.m)中使用#import包含彼此的头文件,就可以用彼此类内部的实体变量和方法了。
So,一般来说,@class是放在interface中的,只是为了在interface中引用这个类,把这个类作为一个类型来用的。在实现这个接口的实现类中,如果需要引用这个类的实体变量或者方法之类的,还是需要#import那个在@class中声明的类,放在实现类中。
-
系统中有哪些对象是单例?请写个单例。
系统中的单例对象是:NSNotification、UIApplication、NSUserDefaults、NSFileManager、NSURLCache、AVAudioSession
*自己实现单例需要注意:
1.不使用GCD时(即不考虑多线程安全时):
// 单例模式实现要点:
// 1. 废掉构造方法(调用的时候抛出异常)
// 2. 提供一个类方法向外界返回该类的唯一实例
-(instancetype)init {
@throw [NSException exceptionWithName:@"CDSingleton" reason:@"不允许调用构造方法" userInfo:nil];
// return nil;
}
// 此方法由于没有在.h文件中暴露接口相当于是私有方法
-(instancetype) initPrivate {
if(self = [super init]) {
_value = arc4random();
}
return self;
}
+(instancetype) sharedInstance {
// static类型的变量拥有全局的生命周期
static CDSingleton *instance = nil;
// 使用同步块保证在多线程环境下仍然是单例
//同步加锁,在多线程中使用,可以使线程安全
@synchronized(self) {
if(!instance) {
instance = [[self alloc] initPrivate];
}
}
return instance;
}
2.使用GCD时(即考虑线程安全时):
+(instancetype)sharedInstance{
static HCDSingleton *singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[HCDSingleton alloc]init];
});
return singleton;
}
-
简单讲一下你对协议的理解。
举个简单的例子吧。
A需要某样物品,但是A由于某些原因没法儿得到该物品,A委托B帮忙弄来这样物品,A制定一项协议,B愿意遵循A做的协议中的规定,这样B就可以提供给A其需要的那样物品。协议中包含了遵循方B所需要实现的方法,以及B能做的事情。换句话理解就是,B必须提供出这样的物品,而且,必须是合法途径。
-
如何理解ARC自动引用计数机制?
ARC的自动引用计数机制,并不是类似于Java中的垃圾回收机制。ARC的自动引用计数机制,只是使引用计数消失掉,而是为我们的代码自动加上ratain、release、autorelease语句。
-
如何理解retain、copy、assign、release、autorelease、dealloc关键字?
retain:调用retain函数,会让对象引用计数+1;
release:调用release函数,会让对象引用计数-1;
delloc:当对象引用计数为0时,会自动调用delloc将对象进行释放掉。即,对象在销毁的时候自动调用该函数,禁止手动调用。
copy:当你不想让A与B共享一块内存的时候,让A和B有各自的内存。
assign:在使用基本数据类型的时候,需要使用assign,assign会直接赋值,不会引起对象的引用计数+1。
autorelease:自动释放、延迟释放。写了autorelease的对象指针,并不会立即释放,只是把这个对象指针放入到最近的autoreleasepool中,等这个池子释放的时候,会给池子中的所有对象指针发送一条真正的release消息,然后将它们release掉。
-
请简述self.name = XXX与_name = XXX的区别。
self.name实际上是调用了set方法给变量赋值。
_name是直接给变量赋值。
-
请简述类别和继承有什么联系和区别。
类别和继承都会使用父类中的原有方法和属性,类别是对父类进行扩展。继承是讲父类中的属性、方法等保留下来,根据自己的实际需求状况进行实现。
继承可以增加、修改、删除方法,还可以增加属性。
类别、类目、类的扩展,即category,只能增加方法。但是,通过runtime可以实现动态的增加属性。也即是成员变量。
-
请简述你对strong和weak关键字的理解。
strong:相当于retain,让对象引用计数+1,防止对象在异常情况下被提前释放,导致crash。
weak:弱引用,防止产生循环引用导致无法释放对象和产生野指针。
strong,强引用,在ARC中使用strong告诉编译器帮我们自动插入retain关键字。
weak,弱引用,是普通赋值,相当于MRC中的assign。
-
如何实现ARC和MRC的混合编程?
工程名---->Targets---->build settings---->Apple LLVM 7.0 – Language - Modules---->Objective-C Automatic Reference Counting---->YESARC模式/NOMRC模式/-fno-objc-arc~ARC和MRC混编模式
-
Objective-C中变量默认是私有的吗?方法默认是私有的吗?
Objective-C中变量默认是私有的,即private;
方法默认是公有的,即public。
-
请简述页面传值都有哪些实现方式?
Block、代理、通知、单例、NSUserDefaults、属性
-
请简述 深拷贝 和 浅拷贝的区别。
举个例子①:
往简单了来说:浅拷贝就好比物体A的影子,物体A拿走了,物体A的影子也就没了;深拷贝就好比又拿来一个跟物体A一模一样的物体B,就算物体A被拿走了,物体B仍然存在。
举个例子②:
大家都用过Windows,这里就举Windows下文件夹的操作来说:
浅拷贝就类似于存在于D盘内的X文件夹,创建到桌面快捷方式,如果D盘内的X文件夹存在,就可以通过桌面创建的X文件夹快捷方式打开;如果不存在,则通过桌面创建的X文件夹快捷方式也无法打开;
深拷贝就类似于存在于D盘内的X文件夹,拷贝到桌面,就算 D盘里的X文件夹删除了,桌面拷贝的X文件夹依然可以打开,而且内容一模一样。
浅析原理:
深拷贝会重新再堆上开辟一块内存空间,是一个全新的对象,指针地址和原来的不一样。浅拷贝不会重新开辟一块内存空间,指针和原来是一样的。
深拷贝和浅拷贝的本质区别是:如果地址相同,就是浅拷贝;如果地址不同,就是深拷贝。
浅拷贝操作后,并没有进行真正的复制,而是另一个指针也指向了同一个地址。深拷贝操作后,是真正的复制了一份,另一个指针指向了拷贝后的地址。
备注:
retain:始终是浅复制。引用计数每次+1.返回对象是否可变与被复制的对象保持一致。
copy:对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制,引用计数每次+1。无论被复制的对象是可变还是不可变,最终都返回一个不可变对象。
mutableCopy:始终是深复制,引用计数不改变。始终返回一个可变对象。
不可变对象:值发生改变,其内存首地址随之改变。
可变对象:无论值是否改变,其内存首地址都不随之改变。
引用计数:为了让使用者清楚的知道,该对象有多少个拥有者(即有多少个指针指向统一内存地址)。
-
请简述对MVC设计模式的理解。
MVC:
Model:负责存储、定义、操作数据;
View:负责呈现画面以及与用户进行操作交互
Controller:Model和View的协调者,Controller把Model中的数据拿过来给View用。
Controller可以直接与Model和View进行通信,而View不能和Controller直接通信。View与Controller通信需要利用代理协议的方式,当有数据更新时,Model也要与Controller进行通信,这个时候就要用Notification和KVO,这个方式就像广播一样,Model发信号,Controller设置监听接收信号,当有数据更新时就发信号给Controller,Model和View不能直接进行通信,这样会违背MVC设计模式。
-
iOS中哪些技术符合观察者模式?
首先,先说明一下,什么是观察者模式。
观察者模式,即Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接收到通知。每次指定的被观察的对象的属性被修改后,KVO自动通知相应的观察者。
iOS中,通知(即NSNotification)符合观察者模式的一种。
-
什么是代理模式?实现代理需要注意什么?
代理模式:作为被委托方,帮助别人(即,委托方)完成别人(即,委托方)委托你完成的事情。
你需要注意遵守别人(即,委托方)给你定的约定,和你是否拥有完成这件事的能力。
-
请简述StoryBord和Xib的联系和区别。简述手动编写代码。
StoryBord可以在上面实现整个项目界面的搭建,可以清楚得看到各个试图控制器之间的关系;Xib实现自定义要素和良好部件重用性复杂的UI。
StoryBoard,方便整个项目界面的搭建。优点:可以看到界面效果,能同时进行多个界面的交互。缺点:不能进行界面的定制,缺少灵活性。
Xib,方便对界面进行编辑。可以在Xib上直接以托拉拽的方式添加各种视图。优点:直接看到界面的效果,操作简单。缺点:不方便对视图进行动态控制,不灵活。
手动编写代码,继承(主要是UIView、UIViewController)。优点:可以对视图进行定制,灵活控制方便。缺点:不能马上看到效果、复杂。
-
请简述UITableview对Cell的重用机制。
比如一个屏幕可以放下5个UITableViewCell 总共会创建6个。
设置CELL的重用标志符 一旦可以重用就重用 不能重用再创建,减少内存的消耗。
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier = @"healthNewsTableViewCell";
healthNewsTableViewCell *cell = [myTableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = (healthNewsTableViewCell*)[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier: @"healthNewsTableViewCell"];
}
return cell;
}
//再将数据绑定写在WillDisPlayCell中
//让UITableView稍微顺滑点的方法 在显示cell前被调用
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
healthNewsTableViewCell *MyCell = (healthNewsTableViewCell *)cell;
MyCell.model = dataArray[indexPath.row];
MyCell.backgroundColor = [UIColor colorWithRed:0.936 green:0.941 blue:0.936 alpha:1.000];
}
-
如何使用UIStrollView实现无限加载多张图片?
1.添加并设置定时器
2.设置定时器的调用方法
①. 获取当前正在展示的位置
②. 计算出下一个需要展示的位置
③. 通过动画滚动到下一个位置
注意点:需要进行判断。
定时器的说明:
当用户在处理其他事情的时候,定时器会停止工作。应该吧定时器添加到runloop中,告诉系统在处理其他事情的时候分一部分空间给它。
3.添加页码控件
4.监听collectionView的滚动,当手动触摸CollectionView,尝试拖拽的时候,把定时器停掉,当手指移开的时候,重启定时器。
-
请简述视图控制器的生命周期。
viewController的生命周期是:①—⑨。
①. initWithNib viewController会进行alloc,并init。
②. loadView 在这里会看它的子类是否有重写这个函数,如果重写了则调用子类的,否则就调用它自己的。 注意:这个时候视图还是没有加载进来的。
③. viewDidLoad 这个时候视图已经存在了。可以在这里添加你想要添加的UI控件。
④. viewWillAppear 视图将出现在屏幕上。
⑤. viewDidAppear 视图已经成功在屏幕上渲染完成了。
⑥. viewWillDisappear 视图将要消失了。
⑦. viewDidDisappear 视图从屏幕上消失了。
⑧. viewDidUnLoad 当发生内存警告的时候,如果本视图不是当前正在显示的视图,则会执行这个函数。将子视图释放。
⑨. dealloc 释放viewController。
而,view的生命周期则是:③—⑧。
-
请简述iOS中的事件传递机制。(即iOS中的响应者链的工作原理)
每一个应用有一个响应者链,我们的视图结构是一个N叉树(一个视图可以有多个子视图,一个子视图同一时刻只有一个父视图),而每一个继承UIResponder的对象都可以在这个N叉树中扮演一个节点。
当叶节点成为最高响应者的时候,从这个叶节点开始往其父节点开始追溯出一条链,那么对于这一个叶节点来讲,这一条链就是当前响应者链。响应者链将系统捕获到的UIEvent与UITouch从叶节点开始层层向下分发,期间可以选择停止分发,也可以选择继续向下分发。
-
UITableView有哪些必须要实现的方法?
必须要实现的2个数据源方法,分别是:
1.-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
//这个方法返回每个分段的行数,不同的分段返回不同的行数可以用switch来做,如果是单个列表就直接返回单个你想要的函数即可。
2.-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
//这个方法返回我们调用的每一个单元格。通过我们索引的路径的section和row来确定。
-
请简述HTTP协议中get请求和post请求的区别。
get请求:参数在地址后拼接,没有请求数据,不安全(因为所有参数都拼接在地址后面),不适合传输大量数据(长度限制,为1024个字节)。get提交、请求的数据会附在URL之后,即把数据放置在HTTP协议头中。以?分割URL和传输数据,多个参数用&连接。如果数据时英文字母或数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。
post请求:参数在请求数据区放着,相对get请求更安全,并且数据大小没有限制。把提交的数据放置在HTTP包的包体中。
get提交的数据会在地址栏显示出来,而post提交,地址栏不会改变。
传输数据的大小:
get请求时,传输数据就会受到URL长度限制,POST由于不是通过URL传智,理论上不受限。
安全性:
post的安全性要比get的安全性高;
通过get提交数据,用户名和密码将明文出现在URL上,比如登录界面有可能被浏览器缓存。
-
请简述你对同步/异步请求数据的理解。
1.同步请求可以从网络请求数据,一旦发送同步请求,程序将停止与用户交互,直到服务器返回数据完成,才可以进行下一步操作;
2.异步请求不会阻塞主线程,而会建立一个新的线程来操作,用户发出异步请求后,依然可以对UI进行操作,程序可以继续运行;
-
iOS中都有那些技术可以实现开辟线程?它们的联系和区别是什么?
NSThread、GCD、NSOperation。
三者抽象封装度层次从低到高,抽象封装度越高使用越简单。
NSThread:每个NSThread对象对应一个线程,量级较轻(真正的多线程)
以下俩是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题
NSOperation/NSOperationQueue:面向对象的线程技术
GCD——Grand Central Dispatch(派发):是基于C语言的框架,可以充分利用多核,是苹果推荐使用的多线程技术。
NSThread:
优点:比其他两种轻量级。
缺点:需要自己管理线程的生命周期、线程同步。线程同步对数据的加锁会有一定的开销。
Operation、GCD:
优点:不需要关心线程管理,数据同步的事情。
两者区别:NSOperationQueue可以方便的管理并发、NSOperation之间的优先级。GCD主要与block结合使用。代码简洁高效。
1.性能:GCD更接近底层,而NSOperationQueue则更高级抽象,所以GCD在追求性能的底层操作来说,是速度最快的。这取决于使用Instruments进行代码性能分析,如果有必要的话。
2.从异步操作之间的事务性、顺序性、依赖关系。GCD需要自己写更多的代码来实现,而NSOperationQueue已经内建了这些支持。
3.如果异步操作的过程需要更多地被交互和UI呈现出来,NSOperationQueue回事一个更好的选择。底层代码中,任务之间不太互相依赖,而需要更高的并发能力,GCD则更有优势。
三种多线程技术的对比:
NSThread:
-优点:NSThread比其他两个轻量级,使用简单
-缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销。
NSOperation:
-不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上
-NSOperation是面向对象的
GCD:
-Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用,是替代NSThread,NSOperation的高效和强大的技术
-GCD是基于C语言的。
-
NSThread中线程之间是如何实现通信的?
线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信。
线程间通信的体现:1个线程传递数据给另1个线程。在1个线程中执行完特定任务后,转到另1个线程执行任务。
线程间通信常用方法:
1.-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
该方法一般用于线程间相互通信,即在一个线程中发送消息给另一个线程。
2.-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
注:每一个线程都有自己的Runloop,但非主线程的Runloop默认是关闭的,当需要进行非主线程的通信时,需要确保通信线程的Runloop是开启的,否则发送给通信线程的消息就不会被执行。
-
GCD中有哪些创建线程的方式?
1.主线程队列:主线程队列,在该队列中放置的所有任务都在主线程执行
dispatch_get_main_queue();
2.全局并行队列:在该队列上提交的每个任务,都会生成一个线程,并行的执行(不会等到另一个执行完才执行)
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
3.串行队列:在该队列上提交的任务,生成一个线程,在该线程上顺序执行,一个执行完后一个才能执行
dispatch_queue_create(“”,DISPATCH_QUEUE_SERIAL);
-
iOS中有哪些技术可以保证线程安全?
线程锁、NSLock
-
ASIHttpRequest的父类是什么?
NSOperation
-
请简述AFNetwork的实现原理。
基于NSURL.采用block的方法处理请求,直接返回的是json、xml数据。AFNetwork直接操作对象是AFHTTPClient,是一个实现了NSCoding和NSCoping协议的NSObject子类。AFHTTPClient是一个封装了一系列操作方法的工具类。AFNetWorking默认没有封装同步请求,如果开发者需要使用同步请求,需要重写相关的方法getPath:parameters:failure
,对AFHTTPRequestOperation进行同步处理。
-
请简述TCP和UDP的区别。
TCP为传输控制层协议,为面向连接、可靠的、点到点的通信;
UDP为用户数据报协议,非连接不可靠的点到多点的通信;
TCP侧重可靠传输,UDP侧重快速传输。
-
请简述SDWebImage的实现原理。
调用类别的方法:
从内存中(字典)找图片(当这个图片在本次程序加载过),找到直接用;
从沙盒中找,找到直接使用,缓存到内存。
从网络上获取,使用,缓存到内存,缓存到沙盒。
-
请简述线程和进程有什么联系和区别。
一个程序至少要有一个进程,一个进程至少要有一个线程。
进程:
资源分配的最小独立单元,进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程:
进程下的一个分支,是进程的实体,是CPU调度和分派的基本单元,它是比进程更小的能独立运行的基本单位,线程自己基本不拥有系统资源,只拥有一点在运行中必不可少的资源(程序计数器、一组寄存器、栈),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源。
进程和线程都是由操作系统所提供的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多线程的程序要比多线程的程序健壮,但在进程切换时,耗费资源比较大,效率要差一些。
但对于一些要求同时进行并且又要共享某些变量的并发操作,只有用线程,不能用进程。
-
请简述什么是主键?什么是外键?
主键是本张表的主键,是唯一且非空的,而外键是另一张表中与这张表的某个字段的类型、字段名相同的字段,一般是用作关联两张或者两张以上的数据表时用的。
以下面三张表为例:
有三张表,一张表是读者信息,有一个属性为readno;一张表是图书的信息,有一个属性是bookno;一张表是借阅关系,有两个属性分别以读者信息表中的readno,和图书信息表中的bookno为外键。那么,在借阅关系表中插入数据时需要写入readno和bookno么?这样,设外键还有什么作用?
外键取值规则:空值或参照的主键值。
1.插入非空值时,如果主键表中没有这个值,则不能插入。
2.更新时,不能改为主键表没有的值。
3.删除主键表记录时,你可以在建外键时选定外键记录一起级联删除还是拒绝删除。
4.更新主键记录时,同样有级联更新和拒绝执行的选择。
简言之:
起约束作用,就是在借阅关系表中只能插入读者/图书信息表中存在的值,不然会出错。
作用在于,如果你插入的readno或者bookno在两个表中没有,就会插不进去。
-
请简述iOS的沙盒机制。(或者说,你对沙盒的理解)
每个iOS应用都被限制在“沙盒”中,沙盒相当于一个加了仅主人可见权限的文件夹,既是在应用程序安装过程中,系统为每个单独的应用程序生成它的主目录和一些关键的子目录。
苹果对沙盒有几条限制:
1.应用程序在自己的沙盒中运作,但是不能访问任何其他应用程序沙盒;
2.应用之间不能共享数据,沙盒里的文件不能被复制到其他应用程序的文件夹中,也不能把其他应用文件夹复制到沙盒中;
3.苹果禁止任何读写沙盒以外的文件,禁止应用程序将内容写到沙盒以外的文件夹中;
4.沙盒目录里面有三个文件夹:Documents——存储;应用程序的数据文件,存储用户数据或其他定期备份的信息;Library下有两个文件夹,Caches存储应用程序再次启动所需的信息,Preferences包含应用程序的偏好设置;temp存放临时文件即应用程序再次启动不需要的文件。
获取沙河根目录的方法,有几种方法:用NSHomeDirectory
获取。
获取Document路径:
NSSearchPathForDirectotiesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)
-
如何实现真机调试?
(Xcode 7.0之后可以不用配置开发者证书直接进行真机测试。)
1.首先需要用钥匙串创建一个钥匙(key);
2.将钥匙串上传到官网,获取iOS Development证书;
3.创建App ID即我们应用程序中的Boundle ID;
4.添加Device ID即UDID;
5.通过勾选前面所创建的证书:Apple ID、Device ID;
6.生成mobileprovision文件;
7.先决条件:申请开发者账号99美刀。
-
如何查找项目中的内存泄漏?
使用内存分析工具:
一、静态分析(不用运行程序时就能分析)
使用Analyze工具:在Xcode菜单栏中,点击Product-->Alalyze之后,Xcode就会帮你编译一次,并且找出你工程中有缺陷的代码,这个工具在当年非ARC的时候,非常火,只是现在的作用鸡毛蒜皮了。分析内存泄漏shift+command+b。
二、动态分析(运行程序时分析)
静态有时候不能把所有的内存泄漏检查出来,有的内存泄漏是在运行时,用户操作时才产生的。那就需要用到Instruments了。
Instruments工具操作流程:在Xcode菜单栏中,点击Product-->Profile-->build成功后跳出Instruments工具-->选择Leaks选项-->choose-->选择要检测的app-->点击红色按钮开始运行,进行检查。
程序跑起来之后,可以开始各种点击调试试用。
红色的圆圈里的菱形表示内存泄漏了。
如何通过Instruments这个工具看到在哪出现内存泄漏了?
现在工具栏按下暂停按钮,把工具监视内存的活动停下来。
选择Leak Checks,然后选择Call Tree(Details后面的那个选项)
选中右边栏,中间的按钮,选中中间的两项,然后会在中间的地方显示内存泄漏的地方,双击就可以跳转到对应的代码。最后解决内存泄漏问题。
-
项目中的支付环节如何实现的?
1.先与支付宝签约,获得商户ID(partner)和账号(seller);
2.下载相应的公钥私钥文件(加密签名用);
3.下载支付宝SDK;
4.生成订单信息;
5.调用支付宝客户端,由支付宝客户端跟支付宝安全服务器打交道;
6.支付完毕后返回支付结果给商户客户端和服务,SDK里有集成支付宝功能的一个Demo,集成支付功能的具体操作方式,可以参考Demo。
//生成订单信息及签名请求参数没有return_URL这个参数,商户可以根据自身情况选择签名方法
//点击获取product实例,并初始化订单信息
//订单ID
//商品标题
//商品描述
//商品价格
//回调URL
-
如何实现项目上线到AppStore?
1.登录应用发布网站添加应用信息;
2.下载安装发布证书;
3.选择发布证书,使用Archive编译发布包,用Xcode将代码(发布包)上传到服务器;
4.等待审核通过;
5.生成IPA:菜单栏-->Product-->Archive。
-
请简述你在项目中遇到过哪些问题,如何解决的?
举个当初自己在项目中解决接口问题的例子:
当时,拿着提供的接口,在浏览器中用JSON解析的工具看数据,没有任何问题。但是,放到工程中解析,第一次运行,没问题,是最新的数据。若是,稍等半天,再去刷新,依旧是第一次运行时的数据。但是,浏览器打开接口,却是最新的数据。后来,无论是打印,还是打断点,一直找不到问题所在。
我就尝试开始从接口的拼接中找问题。后来发现拼接中的一个数字如果是不断变化的话,就会显示出最新的数据。然后,就在接口的拼接过程中,将那个固定数字修改为随着点击而随机产生的数字。刚开始以为,能解决问题,后来发现,刷新到一定次数后,就会出现依旧还是旧数据的问题。只好再作调整。就给数字以逐渐累加的形式,来变化不同的数字。如此,做尝试,却发现,每次变化范围太小,依旧会出现数据不更新的情况。后来,最终确定一个大范围值,才解决了数据不更新的问题。
-
如何实现流媒体格式的视频边播边放边缓存?
视频播放器处理流程:
1.当开始播放视频时,通过视频url判断本地cache中是否已经缓存当前视频;如果有,则直接播放本地cache中视频;
2.如果本地cache中没有视频,则视频播放器向代理请求数据;
3.加载视频时展示正在加载的提示(动画:如旋转的小菊花);
4.如果可以正常播放视频,则去掉加载提示,播放视频;如果加载失败,去掉加载提示并显示失败提示;
5.在播放过程中如果由于网络国漫或拖拽原因导致没有播放数据时,要展示加载提示,跳转到第4步;
代理对象处理流程:
1.当视频播放器向代理请求dataRequest时,判断代理是否已经向服务器发起了请求,如果没有,则发起下载整个视频文件的请求;
2.如果代理已经和服务器建立连接,则判断当前的dataRequest请求的offset是否大于当前已经缓存的文件的offset;如果大于,则取消当前与服务器的请求,并从offset开始到文件尾向服务器发起请求(此时应该是由于播放器向后拖拽,并且超过了已缓存的数据时才会出现);
3.如果当前的dataRequest请求的offset小于已经缓存的文件的offset,同事大于代理向服务器请求的range的offset,说明有一部分已经缓存的数据可以传给播放器,则将这部分数据返回给播放器(此时应该是由于播放器向前拖拽,请求的数据已经缓存过才会出现);
4.如果当前的dataRequest请求的offset小于代理向服务器请求的range的offset,则取消当前与服务器的请求,并从offset开始到文件尾向服务器发起请求(此时应该是由于播放器向前拖拽,并且超过了已缓存的数据时才会出现);
5.只要代理重新想服务器发起请求,就会导致缓存的数据不连续,则加载结束后不用将缓存的数据放入到本地cache;
6.如果代理和服务器的链接超时,重试一次。如果还是错误,则通知播放器网络错误;
7.如果服务器返回其它错误,则代理通知播放器网络错误;
-
请简述xml和json数据各有哪些优势?分析json、xml的区别。json、xml解析方式的底层是如何处理的?
json:键值对,数据小,不复杂,便于解析,有框架支持,适合轻量级传输,作为数据包个数传输的时候效率更高;
xml:标签套内容,xml数据比较大,比较复杂,适合大数据量的传输,xml有丰富的编码工具,比如:Dom4j,JDom.解析方式有两种,一是通过文芳模型解析,另外一种遍历节点。
区别:
(1)可读性方面:基本相同,xml的可读性比较好
(2)可扩展性方面:都具有很好的扩展性
(3)编码难度方面:相对而言:JSON的编码比较容易
(4)解码难度:json的解码难度基本为零,xml需要考虑子节点和父节点
(5)数据体积方面:json相对于xml来讲,数据体积小,传递的速度跟快些
(6)数据交互方面:json与JavaScript的交互更加方面,更容易解析处理,更好的数据交互
(7)数据描述方面:xml对数据描述性比较好
(8)传输速度方面:json的速度远远快于xml
JSON底层原理:
遍历字符串中的字符,最终根据格式规定的特殊字符,比如{}号,[]号, : 号 等进行区分,{}号是一个字典 的开始,[]号是一个数组的开始, : 号是字典的键和值的分水岭,最终乃是将json数据转化为字典,字典中值可能是字典,数 组,或字符串而已。
XML底层原理:
XML解析常用的解析方法有两种:DOM解析和SAX解析。DOM 采用建立树形结构的方式访问 XML 文档,而 SAX 采用的事件模型。 。DOM 解析把 XML 文档转化为一个包含其内容的树,并可以对树进行遍历。使用 DOM 解析器的时候需 要处理整个 XML 文档,所以对性能和内存的要求比较高。SAX在解析 XML 文档的时候可以触发一系列的事件,当发现给定的tag 的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。SAX 对内存的要求通常会比较低,因为它让开发人员自己来决 定所要处理的tag。特别是当开发人员只需要处理文档中所包含的部分数据时,SAX 这种扩展能力得到了更好的体现。
延伸:
SAX与DOM的区别:
1、SAX处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且由于应用程序只是 在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不 必解析整个文档;它可以在某个条件得到 满足时停止解析。一般来说,SAX 还比它的替代者 DOM 快许多。另一方面,由 于应用程序没有以任何方式存储数据,使用 SAX 来更改数据或在数据流中往后移是不可能的。
2、DOM 以及广义的基于树的处理具有几个优点。首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数 据和结构作出更改。它还可以在任何时候在树中上下 导航,而不是像 SAX 那样是一次性的处理。DOM 使用起来也要简单 得多。另一方面,在内存中构造这样的树涉及大量的开销。大型文件完全占用系统内存容量的情况并不鲜见。此外,创建一 棵 DOM 树可能是一个缓慢的过程。
3、选择 DOM 还是选择 SAX,这取决于下面几个因素:
应用程序的目的:如果打算对数据作出更改并将它输出为 XML,那么在大多数情况下,DOM 是适当的选择。并不是说使 用 SAX 就不能更改数据,但是该过程要复杂得多,因为您必须对数据的一份拷贝而不是对数据本身作出更改。
数据容量: 对于大型文件,SAX 是更好的选择。数据将如何使用:如果只有数据中的少量部分会被使用,那么使用 SAX 来将该部分数据提取到应用程序中可能更好。 另一方面,如果您知道自己以后会回头引用已处理过的大量信息,那么 SAX 也许不是恰当的选择。
对速度的需要:SAX 实现通常要比 DOM 实现更快。
SAX 和 DOM 不是相互排斥的,记住这点很重要。您可以使用 DOM 来创建 SAX 事件流,也可以使用 SAX 来创建 DOM 树。事实上,用于创建 DOM 树的大多数解析器实际上都使用 SAX 来完成这个任务。