iOS复习笔记

2018-05-13  本文已影响0人  猎手Andy

runtime机制

objective-c代码总是先预编译成C代码,runtime机制也是基于C的实现。
消息机制是runtime的基础。研究runtime机制基本上就是阅读和理解对应的C代码。

NSArray用strong修饰有什么问题?

如果不小心将一个可变数组赋值给它,将导致修改可变数组的元素的时候导致该“不可变数组”的元素发生变化。实际上指向了可变数组的内存。

NSMutableArray用copy修饰有什么问题?

初始化的时候会导致得到一个不可变数组,增加修改的时候会崩溃。doesnotreconginizeselector

循环引用

Block

category使用和实现机制

利用runtime动态修改 methodList 的值来添加成员方法,这也是 Category 实现的原理,因此它不能增加成员变量。添加数据可以通过关联对象来绑定一个对象。

extension在编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension。
extension可以添加实例变量.

category是在运行期决议的。
category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。但是通过关联对象可以绑定数据成员。

typedef struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
} category_t;

上面结构表面,category可以增加实例方法,类方法,实现协议,添加属性(关联对象)。不能增加实例变量。

通过编译成C代码可以看的更清晰

clang -rewrite-objc MyClass.m

+load

代码形式 编译器/运行期 实例变量 和类的关系 类的源代码
@interface MainViewController ()<UIGestureRecognizerDelegate 编译器决定 可以增加实例变量 是类的一部分,跟类的产生而产生,随类的消亡一起消亡.用来隐藏类的私有信息。 需要类的源代码
@interface MainViewController (Gesture) 运行期决定 不可以增加实例变量,但是可以绑定一个数据对象 不是类的组成部分,由运行期修改类的方法数组增加的方法 不需要类的源代码

category通过关联对象增加属性的原理

运行时通过AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的,这是一个全局的map,key是关联对象的指针地址,value是另外一个AssociationsHashMap,里面保存了关联对象的kv对。

KVC实现原理

KVC运用了一个isa-swizzling技术。isa-swizzling就是类型混合指针机制。KVC主要通过isa-swizzling,来实现其内部查找定位的。isa指针,如其名称所指,(就是is a kind of的意思),指向维护分发表的对象的类。该分发表实际上包含了指向实现类中的方法的指针,和其它数据。

一个对象在调用setValue的时候,

-(1)首先根据方法名找到运行方法的时候所需要的环境参数。

-(2)他会从自己isa指针结合环境参数,找到具体的方法实现的接口。

-(3)再直接查找得来的具体的方法实现。

KVO使用和实现原理

当观察者为一个对象的属性进行了注册,被观察对象的isa指针被修改的时候,isa指针就会指向一个中间类,而不是真实的类。所以isa指针其实不需要指向实例对象真实的类。所以我们的程序最好不要依赖于isa指针。在调用类的方法的时候,最好要明确对象实例的类名。

只有当我们调用KVC去访问key值的时候KVO才会起作用,KVO是基于KVC实现的。

因为KVC的实现机制,可以很容易看到某个KVC操作的Key,也很容易的跟观察者注册表中的Key进行匹对。假如访问的Key是被观察的Key,那么我们在内部就可以很容易的到观察者注册表中去找到观察者对象,而后给他发送消息。

总结一下,想使用KVO有三种方法:

1)使用了KVC

使用了KVC,如果有访问器方法,则运行时会在访问器方法中调用will/didChangeValueForKey:方法;

没用访问器方法,运行时会在setValue:forKey方法中调用will/didChangeValueForKey:方法。

2)有访问器方法

运行时会重写访问器方法调用will/didChangeValueForKey:方法。

因此,直接调用访问器方法改变属性值时,KVO也能监听到。

3)显示调用will/didChangeValueForKey:方法。

MVVM、MVP、MVC的区别

第三方框架

属性的本质

多线程

1.进程和线程

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。

tasklet运行在伪并发中,使用channel机制进行同步数据交换。python中的greenlet提供了微线程的操作。不同于多线程,它给我们提供了一种更加轻量的异步编程模式。

协程(Coroutine)提供了不同于线程的另一种方式,它首先是串行化的。其次,在串行化的过程中,协程允许用户显式释放控制权,将控制权转移另一个过程。释放控制权之后,原过程的状态得以保留,直到控制权恢复的时候,可以继续执行下去。所以协程的控制权转移也称为“挂起”和“唤醒”。

2.同步、异步、并发、串行、并行

同步和异步决定了要不要开启新的线程

在当前线程中执行任务,不具备开启新线程的能力

dispatch_sync

将同步任务加入串行队列,会顺序执行,一般不这样做并且在一个任务未结束时调起其它同步任务会死锁。将同步任务加入并行队列,会顺序执行,但是也没什么意义。

在新的线程中执行任务,具备开启新线程的能力

dispatch_async

将异步任务加入串行队列,会顺序执行,并且不会出现死锁问题。将异步任务加入并行队列,会并行执行多个任务,这也是我们最常用的一种方式。

并发和串行决定了任务的执行方式

一个任务执行完毕后,再执行下一个任务
同步线程的实现,在同一个线程内依次执行任务。

串行队列

其实是真正的异步,多核CPU可以同时开启多条线程供多个任务同时执行,互不干扰
只有多核CPU才存在并行,多个CPU独立开启线程调度

CPU时间片调度,同时只有一个线程在执行。
但是可以多个线程异步执行,从外部看起来像是同时在执行,实际上是根据优先级不断抢占CPU的时间片执行代码。高优先级的抢占的时间多并且优先执行,所以上层看起来是同时执行但有优先级的概念。

叠加并行即多处理器后更复杂,即并行同时并发,并发未必是并行的,因为可能在单CPU上并发

并发队列

3.GCD(Grand Central Dispatch)

同步函数不具备开启线程的能力,无论是什么队列都不会开启线程;

异步函数具备开启线程的能力,开启几条线程由队列决定(串行队列只会开启一条新的线程,并发队列会开启多条线程)。
  
同步函数
  (1)并发队列:不会开线程
  (2)串行队列:不会开线程
 
异步函数
  (1)并发队列:能开启N条线程
  (2)串行队列:开启1条线程

4. dispatch_group

把一组任务提交到队列中,这些队列可以不相关,然后监听这组任务完成的事件。

使用dispatch_group_wait,可以阻塞线程,等待group的任务执行完毕,才能继续执行后续任务

使用dispatch_group_notify,不会阻塞线程(group外的线程执行顺序不受影响),而且可以在执行完成group的任务后进行操作

现在有4个任务,任务1、任务2、任务3、任务4. 任务3必须在任务2之后,任务4必须在前3个任务都执行完成后,才能执行,并且需要在主线程更新UI。

思路分析:
任务3必须在任务2之后,所以这两个必须串行执行,同时,任务2和3整体可以和任务1并行执行,最后,任务4只能等待前3个任务全部执行完成,才能执行。这里就可以用group快速实现场景需求。

-(void)dispatchGroup
{
   dispatch_queue_t globalQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   dispatch_queue_t selfQuene = dispatch_queue_create("myQuene", 0);
   dispatch_group_t group = dispatch_group_create();
   dispatch_group_async(group, globalQuene, ^{
       NSLog(@"run task 1");
   });
   dispatch_group_async(group, selfQuene, ^{
       NSLog(@"run task 2");
   });
   dispatch_group_async(group, selfQuene, ^{
       NSLog(@"run task 3");
   });
   dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"run task 4");
   });
}
A)
dispatch_group_async(group, queue, ^{ 
  // 。。。 
}); 
<==>
B) 
dispatch_group_enter(group);
dispatch_async(queue, ^{
  //。。。
  dispatch_group_leave(group);
});
-(void)disGroupEnterAndLeave{
    dispatch_queue_t globalQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    //任务1
    dispatch_group_enter(group);
    dispatch_async(globalQuene, ^{
         NSLog(@"run task 1");
        sleep(1);
        dispatch_group_leave(group);
    });
    
    //任务2
    dispatch_group_enter(group);
    dispatch_async(globalQuene, ^{
        NSLog(@"run task 2");
        sleep(2);
        dispatch_group_leave(group);
    });
    
    //任务3
    dispatch_group_enter(group);
    dispatch_async(globalQuene, ^{
        NSLog(@"run task 3");
        sleep(3);
        dispatch_group_leave(group);
    });
    
    //一直等待完成
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
  
    //任务4
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"run task 4");
    });
    
}

5. NSOperationQueue

6. NSThread

TableView性能优化

性能和内存优化

内存泄漏调试

内存泄漏检测工具

Facebook iOS 内存检测三剑客(FBAllocationTracker/FBMemoryProfiler/FBRetainCycleDetector),MSLeakHunter,MLeaksFinder,PLeakSniffer等等

利用ARC中weak指针指向的对象在对象释放时会自动置为nil的特性来检测VC是否在内存驻留。

通过监控UINavigationController的navigation stack,可以判断一个VC的生命周期的开始和结束。就是当VC从navigation stack移除且VC的viewDidDisappear方法执行时,可以认为一个VC的生命周期即将结束。这时候就可以创建一个指向该VC的weak指针,并初始化一个定时器对VC进行延时扫描,最后通过1中的方法判断VC是否还驻留在内存从而得出VC是否发生内存泄露的结论。

CollectionView

ARC内部原理

iOS内存管理

Autorelease机制

在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop

关联对象

设计模式

常量

1.NSString * const MY_CONSTANT;
2.const NSString * myVariable;
技巧:按*分割两部分,左边是数据的类型,右边是变量或常量。
1中,左边是数据类型为NSString,右边是constant,即常量,完整的说法是指向NSString(不可变类型)数据类型的常量MY_CONSTANT。
2中,左边是数据类型为const NSString,即数据类型为常量字符串,右边是变量,完整的说法是指向常量字符串的变量myVariable.
该变量可以再次指向其它任意的常量字符串。

开发过程中遇到最深刻的BUG是什么

1.百度推送证书更新问题
Mac OS 刚好升级到最新版本,按官方教程制作上传的证书无法验证通过。
最后发现是Open SSL版本兼容性问题,需要下载之前的版本重新安装。百度兼容性问题。
2.ViewController无法释放问题
知道是循环引用导致无法释放,但是因为涉及业务代码非常多,相关Block代码都需要逐一检查。
NSTimer、通知、Block。
在每个ViewController的dealloc做跟踪。

JSPatch 热更新原理

HTTP 2.0相比HTTP 1.0有什么优势?

HTTPDNS

提高域名解析成功率

移动APP网络优化

速度

弱网

安全

HTTP的几种方法

GET

请求指定的页面信息,并返回实体主体。

GET请求请提交的数据放置在HTTP请求协议头中,GET方法通过URL请求来传递用户的输入,GET方式的提交你需要用Request.QueryString来取得变量的值。
GET方法提交数据,可能会带来安全性的问题,数据被浏览器缓存。
GET请求有长度限制。

POST

向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。
POST请求可能会导致新的资源的建立和/或已有资源的修改。
POST方式提交时,你必须通过Request.Form来访问提交的内容

PUT

从客户端向服务器传送的数据取代指定的文档的内容。

DELETE

请求服务器删除指定的页面。
DELETE请求一般返回3种码

  1. 200(OK)——删除成功,同时返回已经删除的资源。
  2. 202(Accepted)——删除请求已经接受,但没有被立即执行(资源也许已经被转移到了待删除区域)。
  3. 204(No Content)——删除请求已经被执行,但是没有返回资源(也许是请求删除不存在的资源造成的)。

HEAD

类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头。

CONNECT

HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。

OPTIONS

允许客户端查看服务器的性能。

TRACE

回显服务器收到的请求,主要用于测试或诊断。

iOS内存泄漏检测和原因

https://www.jianshu.com/p/e9d989c12ff8

1.静态检测

2.动态检测

在Allocation中我们主要关注的是Persistent和Persistent Bytes,分别表示当前时间段,申请了但是还没释放的内存数量和大小。
记住当前这两个值,然后进入某个新页面,退出该页面,观察这两个值是否增加。需要注意的是,由于有些图片调用本身是有缓存的,如果是用SDWebImage管理,则网络图片都会缓存在内存中。因此退出页面后内存有增加是正常的,而且还有些单例的内存也是不会释放的,我们可以再次进入同一个页面,在图片都加载过的情况下,反复进入退出查看内存状况,如果持续增加,则说明有泄漏。

内存泄漏的常见场景

1. CF类型内存

2. MRC 使用release/autorelease

3. ARC下的循环引用,block循环引用

runloop是什么?

上一篇 下一篇

猜你喜欢

热点阅读