http://www.cnblogs.com/huayuan320/p/5777610.html 常见问题收集
业界大神 孙源前百度现在滴滴打车, 唐巧, 叶孤城, YYKit郭曜源
MVC
MVC的目的是为了把数据(Model)和视图(View)分离开来,然后用控制器(Controller)负责调节两者之间的关系。也就是让专业的对象做专业的事情,View就只负责视图相关的东西,Model就只负责描述数据模型,Controller负责调节两者之间的关系。
MVC在核心通讯上基于推送/订阅模型,当一个model变化时它对应用其它模块发出更新通知(“publishes” ),订阅者 (subscriber)——通常是一个Controller,然后更新对应的view。观察者——这种自然的观察关系促进了多个view关联到同一个 model。
MVVM
就是在其中一个view中包含了view和model, 其目的就是给vc减负
id和instancetype的区别
相同点: 都可以作为方法的返回类型
区别: 1.id只能返回未知类型, 而instancetype返回所在类对象类型;
2. instancetype不能作为参数类型, 而id可以
runtime
即运行时, ios是一门可以把确定数据类型的时机从编译推迟到运行时的语言, 属于纯C语言的API
1.在程序运行过程中, 动态创建一个类(比如KVO的底层实现)
2. 在程序运行过程中, 动态地为某个类添加属性\方法, 修改属性值\方法
RunLoop
runloop是事件接收和分发机制的一个实现,当启动一个子线程,如果只是处理单一的线程,在事件处理完后就会退出,但是当我们需要让该线程随即监听某项事物,就得让线程一直存在,runloop就是优化后的死循环,每一个线程都有一个runloop,主线程中的runloop是默认开启的,子线程需要手动调用开启,当事件源没有传入的时候当前线程就会进入休眠状态,当事件源传入的时候runloop才会循环一次。runloop循环一次会使引用对象的计数-1,当计数变为0的时候对象就会释放掉。
NSDefaultRunLoopMode触碰就停止, 主线程默认此模式
UITrackingRunLoopMode 触碰屏幕才开始循环, 界面跟踪模式,
NSRunLoopCommonModes 一直在循环, 占位模式, 不是一个真正的模式
对于NSString *obj = [NSData alloc] init]; 编译和运行时是什么状态
编译时是前者,运行时时是后者
______block和___weak修饰符的区别
____block对象在block中是可以被修改的,重新赋值的, ______block对象不会在block中被强引用一次,从而不会出现循环引用的问题
使用了____weak的对象作用等同于定义为weak的property对象,是不会导致循环引用的问题,苹果官方文档已经说明了当原对象没有任何强引用的时候,弱引用指针也会被置为nil.
1.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
2.__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
3.______block对象可以在block中被重新赋值,___weak不可以。
如何解决tableview的cell卡顿问题
1使用不透明视图,
2.不要重复创建不必要的cell,
3减少视图的数目,
4.不要做多余的绘制工作
5.不可以阻塞主线程,所有刷新UI操作都是要到主线程中去的。
tableview优化,
1.不用xib铺界面,
2.在model缓存高度,
3.预估高度,
4.不在协议方法赋值, 用setter方法进行赋值,
- 不要给控件设置圆角, draw rect或者铺两个图
6.使用cell factory
core data是对sqlite进行的封装, 但是执行效率sqlite比较高
常用设计模式
单例模式:整个项目只有只需要创建一次的类,而且自行实例化并向整个系统提供这个实例,单例也被称为懒汉模式或恶汉模式, 数据库, 当然有数据库也就有coredata, 音频, 视频,
应用场景:当做数据缓存时 创建的datamanage就是单例,反向传值--优势:使用简单,易于跨模块
+(instancetype)shareManage{
static databaseManage *manage = nil;
static dispatch_once_t onceToken;
dispatch_once(&oncetoken,^{
manage = [[databaseManage alloc]init];
});
return manage;
}
好处: 节省资源, 一个应用就一个对象
注意: 一旦不小心销毁了单例, 再调用生成方法是不会被创建的, 而且单例一旦被创建, 整个app都不会释放, 这会占用内存, 因此不可滥用单例
代理模式
观察者模式
是定义对象间的一种一对多的依赖关系,并且当一个对象的状态发生改变的时候,所有依赖于它的对象都会得到通知且自动更新。
缺点: 代码可读性很差, 而且搜索通知时系统是全局逐行搜索, 运行效率低
远程推送
a)先在苹果推送服务器APNs上注册devicetoken ,返回给后台服务器
b)后台服务器将devicetoken和要发送的消息打包给APNs(需要在开发者中心注册推送证书)
c)APNs将消息发送给devicetoken上保存的指定的设备的app上。
APP发布到APPStore上的流程
a)首先到苹果官网购买开发者账号,分为企业和个人版本
b)在开发者中心注册开发者证书和发布证书,如需推送,同时也要注册推送证书,将p12装到电脑上
c)注册APP IDs和真机调试 接着注册调试 ——创建发布的配置文件
d)在itunces connectent 中心创建好应用并填写相关信息
e)在xcode中将项目打包并上传(boundle id 要一致,先在building setting 里填写创建好的配置文件,发布证书)
f)回到content 中选择填好的项目上传点击提交审核(无法构建版本的情况下需要重新打包上传)
GET和POST的区别
Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE 对应着对这个资源查, 改, 增, 删
所以GET一般用于获取/查询 资源信息,而POST一般用于更新 资源信息
再进一步了解下他们两个的区别:
1. GET使用URL或Cookie传参。而POST将数据放在BODY中
2. GET的URL会有长度上的限制,则POST的数据则可以非常大。
3. POST比GET安全,因为数据在地址栏上不可见。
简述SDWebImageView的实现原理
1.首先显示placeholderImage,然后sdWebImageManager根据URL开始处理图片
2.根据你提供的URL,去本地查找是否用对应这个URL的文件,如果有,就直接读取本地数据,如果没有,就发送网络请求(用MD5生成32位的固定)
3.开启一个子线程,在子线程当中使用异步请求的方式向服务器索取数据
4.下载完图片后进行解码处理,在主线程中宣告解码完成,回调给sdWebImageManager告知图片下载完成,在需要的地方展示图片,图片加载到UI之上,并且根据URL的字符串名字按一定的方式生成一个文件命名,保存到本地
SDWebImageView 是否有清除缓存的功能,如果没有,你如何实现?
有sdImageCache在内存警告或者退到后台的时候清理内存图片缓存,应用结束清理过期图片。遍历缓存文件夹,删除所有缓存文件
[[SDImageCache sharedImageCache] clearDisk];
AFNetworking 原理
AFNetworking的第一个重大突破就是将NSURLConnection 、NSOperation结合。可以从头到尾监视请求的状态,并储存请求、响应、响应数据等中间状态。
AFN 的基础部分是 AFURLConnectionOperation,一个 NSOperation subclass,实现了 基于NSURLConnection 相关的delegate+blocks,网络部分是由 NSURLConnection 完成,然后利用 NSOperation 的 state (isReady→isExecuting→isFinished) 变化来进行网络控制。网络请求是在一个指定的线程(networkRequestThread)完成。
AFURLConnectionOperation是一个很纯粹的网络请求 operation,可以对他进行 start/cancel/pause/resume 操作,可以获取对应的 NSURLRequest 和 NSURLResponse 数据。支持 NSInputStream/NSOutputStream,提供了 uploadPress 和downloadProgress 以方便其他使用。
AFHTTPRequestOperation是 AFURLConnectionOperation 的子类,针对 HTTP+HTTPS 协议做了一层封装,比如statusCode、Content-Type 等,添加了请求成功和失败的回调 block,提供了addAcceptableContentTypes: 以方便上层使用。
contentOffset, contentInset, contentSize
contentSize是scrollview可以滚动的区域,比如frame = (0 ,0 ,320 ,480) contentSize = (320 ,960),代表你的scrollview可以上下滚动,滚动区域为frame大小的两倍。
contentOffset是scrollview当前显示区域顶点相对于frame顶点的偏移量,比如上个例子你拉到最下面,contentoffset就是(0 ,480),也就是y偏移了480
contentInset是scrollview的contentview的顶点相对于scrollview的位置,例如你的contentInset = (0 ,100),那么你的contentview就是从scrollview的(0 ,100)开始显示
进程和线程区别
进程就是一个应用程序在处理机上的一次执行过程,它是一个动态的概念,而线程是进程中的一部分,进程包含多个线程在运行。
多线程框架
NSTheard 单线程—针对线程直接控制的,手动开启, 是三种方法里面相对轻量级的,但需要管理线程的生命周期、同步、加锁问题,这会导致一定的性能开销
NSOperation(NSOperation是抽象父类,不能直接使用子类NSblockinvation和nsinvocationoperation)
需手动创建线程,还需创建队列,无回调(断点续传用到)
线程池 主要是操作队列 主队列(主线程) 子队列(子线程) NSOperation 是对GCD 一个封装 要解决GCD的一些遗留问题
GCD 自动创建线程,不需手动管理周期,有回调,方便,但不能取消。
大的调度中心 主要是操作队列 主队列 子队列 GCD一旦开启一个子线程,就无法停止
任务队列做线程管理,把创建好的任务直接放到线程中,以任务为中心,GCD是nsoperation的底层实现
什么是串行队列 — 队列里的任务是按先后顺序依次有序的执行
什么是并发队列 — 队列里的任务是同时执行的
多线程如何优化执行效率
如何保证线程安全
线程锁
[NSLock lock]
信号量self(标志位),如果信号量已经存在,则该线程锁正在使用当中,其他线程无法进入锁内代码
@synchronized(self){ }
static dispatch_once_t one;
dispatch_once(&one, ^{
});
GCD
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
除了async,还有sync,delay,
async表明异步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了
举个例子
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];
NSData * data = [[NSData alloc]initWithContentsOfURL:url];
UIImage *image = [[UIImage alloc]initWithData:data];
if (data != nil) {
//回归主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
}
});
------------我是分割线----------------------------------
系统默认就有一个串行队列main_queue和全局队列global_queue(也是并行队列)
举个例子, globalQ和mainQ这俩名字随便起
1.dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2.dispatch_queue_t mainQ = dispatch_get_main_queue();
通常,我们可以在global_queue中做一些long-running的任务,完成后在main_queue中更新UI,避免UI阻塞,无法响应用户操作:
Serial queue有FIFO特性, 即first in first out
验证Serial queue的FIFO特性
NSDate *da = [NSDate date];
NSString *daStr = [da description];
const char *queueName = [daStr UTF8String];
dispatch_queue_t myQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL);
dispatch_async(myQueue, ^{
[NSThread sleepForTimeInterval:6];
NSLog(@"[NSThread sleepForTimeInterval:6];");
});
dispatch_async(myQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"[NSThread sleepForTimeInterval:3];");
});
dispatch_async(myQueue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"[NSThread sleepForTimeInterval:1];");
});
//在MRC需要释放
dispatch_release(myQueue);
运行结果
2013-07-24 16:37:14.397
NSThreadAndBlockDemo[1924:12303] [NSThread sleepForTimeInterval:6];
2013-07-24 16:37:17.399
NSThreadAndBlockDemo[1924:12303] [NSThread sleepForTimeInterval:3];
2013-07-24 16:37:18.401
NSThreadAndBlockDemo[1924:12303] [NSThread sleepForTimeInterval:1];
验证Concurrent queue(global dispatch queue)
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(myQueue, ^{
[NSThread sleepForTimeInterval:6];
NSLog(@"[NSThread sleepForTimeInterval:6];");
});
dispatch_async(myQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"[NSThread sleepForTimeInterval:3];");
});
dispatch_async(myQueue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"[NSThread sleepForTimeInterval:1];");
});
dispatch_release(myQueue);
运行结果
2013-07-24 16:38:41.660
NSThreadAndBlockDemo[1944:12e03] [NSThread sleepForTimeInterval:1];
2013-07-24 16:38:43.660
NSThreadAndBlockDemo[1944:12b03] [NSThread sleepForTimeInterval:3];
2013-07-24 16:38:46.660
NSThreadAndBlockDemo[1944:12303] [NSThread sleepForTimeInterval:6];
dispatch_group_async的使用
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:6];
NSLog(@"group1 [NSThread sleepForTimeInterval:6];");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"group2 [NSThread sleepForTimeInterval:3];");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"group3 [NSThread sleepForTimeInterval:1];");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"main thread.");
});
dispatch_release(group);
运行结果
2013-07-24 16:48:23.063
NSThreadAndBlockDemo[2004:12e03] group3 [NSThread sleepForTimeInterval:1];
2013-07-24 16:48:25.063
NSThreadAndBlockDemo[2004:12b03] group2 [NSThread sleepForTimeInterval:3];
2013-07-24 16:48:28.063
NSThreadAndBlockDemo[2004:12303] group1 [NSThread sleepForTimeInterval:6];
2013-07-24 16:48:28.065
NSThreadAndBlockDemo[2004:11303] main thread.
dispatch_barrier_async的使用
dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
dispatch_queue_t queue = dispatch_queue_create("gcdtest.rongfzh.yc", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"dispatch_async1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:0.5];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async3");
});
运行结果
2013-07-24 17:01:54.580 NSThreadAndBlockDemo[2153:12b03]
dispatch_async2
2013-07-24 17:01:56.580 NSThreadAndBlockDemo[2153:12303]
dispatch_async1
2013-07-24 17:01:56.580 NSThreadAndBlockDemo[2153:12303]
dispatch_barrier_async
2013-07-24 17:01:58.083 NSThreadAndBlockDemo[2153:12303]
dispatch_async3
如果使用
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
会发现运行结果为:
2013-07-24 17:07:17.577 NSThreadAndBlockDemo[2247:12e03]
dispatch_barrier_async
2013-07-24 17:07:18.579 NSThreadAndBlockDemo[2247:15207]
dispatch_async3
2013-07-24 17:07:19.578 NSThreadAndBlockDemo[2247:12b03]
dispatch_async2
2013-07-24 17:07:20.577 NSThreadAndBlockDemo[2247:12303]
dispatch_async1
dispatch_apply
执行某个代码片段N次。
dispatch_apply(5, globalQ, ^(size_t index) {
// 执行5次
});
dispatch_once
dispatch_once这个函数,它可以保证整个应用程序生命周期中某段代码只被执行一次!单例!
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// code to be executed once
});
举个栗子
+ (instancetype)sharedViewController{
static ViewController *vc = nil;
//输入dispatch
//只运行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
vc = [[ViewController alloc] init];
});
return vc;
}
dispatch_after
有时候我们需要等个几秒钟然后做个动画或者给个提示,这时候可以用dispatch_after这个函数:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// code to be executed on the main queue after delay
});
dispatch_set_target_queue
通过dispatch_set_target_queue函数可以设置一个dispatch queue的优先级,或者指定一个dispatch source相应的事件处理提交到哪个queue上。
dispatch_set_target_queue(serialQ, globalQ);
---------------------至此GCD的部分结束------------------
线程锁
互斥锁使用格式
@synchronized(锁对象) { // 需要锁定的代码 }
注意:锁定1份代码只用1把锁,用多把锁是无效的
OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic), 但是需要消耗大量CPU资源
nonatomic:非原子属性,不会为setter方法加锁, 适合内存小的移动设备(常用)
atomic加锁原理:
@property (assign, atomic) int age;
- (void)setAge:(int)age
{
@synchronized(self) {
_age = age;
}
}
我写了个模拟售票的demo叫"试着总结线程锁"
可以这么理解, 异步是为了在复杂操作过程中还可以干别的事情, 别如加载图片过程中用户可以继续滑动看别的东西
而加同步锁是为了避免一些必要的逻辑顺序错误, 比如点击图片, 加载, 双击放大, 如果在加载过程中能够双击, 那么就会返回错误, 这时就需要加同步锁.
然而. 滥用@synchronized()会降低代码效率,因为共用同一个锁的同步块,都必须按顺序执行。若是在对象上频繁加锁,那么程序可能要等另一段与此无关的代码执行完毕,才能继续执行当前代码,这样做会造成没有必要的损耗。
另一种方法是NSLock
- (void)synchronizedMethod{
[_lock lock];
//safe
[_lock unlock];
}
也可以使用NSRecursiveLock这种“递归锁”,线程能够多次持有该锁,而不会出现死锁现象。
这两种方法都不错,但是依然有缺陷,在极端情况下,同步块会导致死锁,还有效率也不高,
http://ios.jobbole.com/82622/
(这个网站写的不错, 用五个案例分析GCD死锁)
dispatch_async(queue,block) async 异步队列,dispatch_async 函数会立即返回, block会在后台异步执行。
dispatch_sync(queue,block) sync 同步队列,dispatch_sync 函数不会立即返回,及阻塞当前线程,等待 block同步执行完成。所以dispatch_sync 在主线程调用就会造成死锁
线程间的通讯
ios性能优化:
1. 不用xib铺界面, 不要给控件设置圆角, 用draw rect或者两张图用中间透明 四周白色的图片覆盖
2.在model缓存高度,
3.预估高度,
4.不在协议方法赋值, 用setter方法进行赋值,
5.在线程中使用 autoreleasepool
imageNamed与imageWithContentsOfFile
imageNamed默认加载图片成功后会内存中缓存图片,这个方法用一个指定的名字在系统缓存中查找并返回一个图片对象.如果缓存中没有找到相应的图片对象,则从指定地方加载图片然后缓存对象,并返回这个图片对象.
而imageWithContentsOfFile则仅只加载图片,不缓存.
大量使用imageNamed方式会在不需要缓存的地方额外增加开销CPU的时间来做这件事.当应用程序需要加载一张比较大的图片并且使用一次性,那么其实是没有必要去缓存这个图片的,用imageWithContentsOfFile是最为经济的方式,这样不会因为UIImage元素较多情况下,CPU会被逐个分散在不必要缓存上浪费过多时间.
deep copy和shallow copy 深拷贝和浅拷贝
浅拷贝:简单的指针复制,新指针和旧指针指向同一内存地址
深拷贝:新指针和旧指针指向不同的内存区域,新指针是一个新的对象,新对象改变了对旧对象没有任何影响.
1.如果对一个不可变对象复制,copy是指针复制,即浅拷贝,返回不可变对象;而mutableCopy则是对象复制,即深拷贝,返回的为可变对象。
2.如果对一个可变对象复制,始终是深拷贝。
协议代理模式
1)出现的原因 :
因为OC是不支持多继承的,所以很多时候都是用Protocol(协议)来代替。Protocol(协议)只能定义公用的一套接口,但不能提供具体的实现方法
2)必须使用weak修饰, 否则在双向代理中,会造成双向强引用.
OC的三大特性
封装: 将文件中具有相同方法的代码抽离出来,提供一个api接口供程序使用
继承: 子类可以继承父类的成员变量和方法,主要体现在代码重用,节省开发时间
多态:同一个操作作用域不同的对象,可以有不同的解释,产生不同的效果,在运行时可以指向基类的指针,产生派生类的方法。
MRC下检测内存泄露的工具
Instruments工具
反向传值
协议, block, 通知, 和单例(不推荐经常使用)
协议: 一对一
通知: 一对多
耦合性高低排布
协议代理->block->通知中心
运行效率高低排布
block->协议代理->通知中心
通知可读性差, 且系统运行时需要从头到尾搜索每一行代码, 不要滥用通知
UIView和CALayer之间的关系
UIView是CALayer的一个载体,UIView显示的部分,都是由CALayer来负责的,其实UIView最重要的功能不是用于显示内容,它主要作用是用于管理它所渲染区域的内部的各种事件.
UIView是继承自UIResponder,所以它可以响应事件.
如果AFNetworking不能使用了,使用MKNetWork第三方库框架替代它
视图控制器的生命周期
alloc init(执行一次)——loadView(只执行一次)——viewDidLoad—viewWillAppear—viewDidAppear—viewWillDisAppear—viewDidDisAppear——viewUnload —dealloc
程序生命周期
启动程序
lifeCycle[40428:11303] willFinishLaunchingWithOptions
lifeCycle[40428:11303] didFinishLaunchingWithOptions
lifeCycle[40428:11303] applicationDidBecomeActive
按下home键
lifeCycle[40428:11303] applicationWillResignActive
lifeCycle[40428:11303] applicationDidEnterBackground
双击home键,再打开程序
lifeCycle[40428:11303] applicationWillEnterForeground
lifeCycle[40428:11303] applicationDidBecomeActive