关于iOS一些常见知识点解析

96
NateLam
2016.06.20 19:39* 字数 3976

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方法进行赋值,

  1. 不要给控件设置圆角, 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

#include和#import的区别@class

include是c/c++导入头文件的关键字,#import 是oc导入头文件的关键字, #import 可以防止头文件重复导入,#import需要导入foundation框架,@class 告诉编译器有某一个类的存在,当执行时才会去查看类的实现文件,可以解决头文件的相互包含

写一个标准宏计算A和B较大的值

define MAX(A,B)((A)>(B)?(A):(B))

ios随笔
Gupao