以下笔记是边看视频课程边记录下来的,供以后自己查阅复习使用
Runtime学习笔记1
消息转发
在OC中,调用方法其实是给对象发送消息
[[Person new] sendMessage:@"gelo"];
// 等价于
objc_msgSend([Person new], @selector(sendMessage:), "gelo");
通过对象的isa指针找到class,如果有方法的话,直接调用。没有找到的话,通过继承树查找,进入消息转发机制
动态方法解析,动态添加方法实现
resolveInstanceMethod
快速转发,当前的类或者继承树没有该方法的实现,在更加广的范围寻找
forwardingTargetForSelector
慢速转发
methodSignatureForSelector
forwardInvocation
doesNotRecognizeSelector:
方法交换 Method Swizzling
用自己写的方法替换系统方法,通过class_getInstanceMethod方法获取,通过method_exchangeImplementations交换两个函数
字典转模型
遍历字典获取key和value
key作为属性名,value作为属性值
通过objc_msgSend发送set方法
模型转字典
字典的key通过模型的属性列表获取
字典的value通过调用get方法获取
实现KVO
KVO是基于runtime的
A监听B 系统会为B创建一个子类
B的isa指针指向B的子类
在子类中重新set方法
KVO底层实现
KVO的基础使用
观察某一个对象的某个属性
options参数可以观察一下几个值
new 返回变化后的新值
old 返回变化前的旧值
init 注册的时候就会发一次通知,改变后的值的时候也会发送
prior 新值和旧值都会返回
KVO默认是自动模式,每次修改值都会发送通知
手动发送通知的时候,对象调用willchangeValueForKey,改变之后调用didchangeValueForKey
监听属性下面的属性值,只需要在监听path中通过点监听:"dog.age"(属性依赖)
KVO监听的是set方法。比如监听不到数组的add方法
要是需要监听容器方法,需要结合KVC
需要监听对象下多个属性,只需要监听对象本身,并实现keyPat***ForKey。返回需要的真正监听的内部属性的NSSet
内部实现
属性是对成员变量和set、get方法的封装
KVO观察的是set方法(设置成员变量之后,外部通过person->name方法修改,KVO监听不到)通过runtime创建一个观察者的子类(NSKVONotifying_Person)重写set方法。修改指针到子类、在重写的方法里面调用willchangeValueForKey、superSetName、didchangeValueForKey。
在创建的子类中,没有父类的set方法!需要重写set方法
OC的方法中包含SEL(方法编号)、IMP(方法实现)一一对应。调用方法的时候发送的是SEL
OC的方法调用里面有两个默认参数:id self,SEL _cmd。由于sendMsg传递了该参数(调用者以及SEL)
监听容器类(NSArray、NSDic)
通过KVO观察容器属性的变化,利用KVC
通过KVC的mutableArray**ForKey返回一个容器对象,向该对象添加元素可以实现KVO。内部新建子类、重写add方法。
KVO返回的NSDic中,kind类型
观察set方法 返回1
观察插入方法 返回2
观察删除方法 返回3
观察替换方法 返回4
数组中count
使用KVO中监听不到数组中的count、使用KVC同样取不到[array valueForKey:@"count"]
count是集合运算符,KVC需要用@"@count"取值。
count是只读属性
数组(NSMutableArray)
关于数组的容量,容量不够用的时候,会成倍的增加
对象本身是指针,指向该对象的结构体
x/100xb arr 打印arr 100个内存地址
找到count的内存地址,修改内存地址的值,进而可以修改count的值
Runtime学习笔记2
消息机制
OC代码会转化为C语言执行,使用runtime的时候,需要关闭代码的严格检测
调用函数的方法:
[p eat]
[p performSelector: SEL]
objc_msgSend()
使用runtime创建对象:
类名.class即为对象 Person.class == objc_getClass("Person")
// 在目录下执行
clang --rewrite-objc main.m
// 手动编译OC代码生成cpp文件
归档/解档
归档和解档对象,需要遵循NSCoding的协议,并且实现协议方法
KVC可以使用id类型为属性赋值
Ivar:成员变量
Method:成员方法
C语言 基本数据类型的指针 函数内部是为了修改外部的值
class_copyIvarList获取所有属性数量
关键字copy、new、creat代表着会在堆区域(malloc)开辟空间
方法执行完毕—>方法调用栈平衡—>内部变量指针出栈—>但是指针指向的堆区的值还在—>内存泄漏
在OC中使用C的代码,要手动释放指针,防止内存溢出
OC方法定位以及替换
OC的方法表:返回值类型+参数类型一样 编号就一样
类
SEL 编号 ————— IMP实现(地址指针)
SEL 编号 ————— IMP实现(地址指针)
SEL 编号 ————— IMP实现(地址指针)
用HOOK!钩住系统方法,在调用之前修改方法的调用
在分类中的load方法(由于预加载,比main更早执行)中交换IMP
#import
+ (void)load {
// 获取替换后的类方法
Method otherMethod = class_getClassMethod([UIImage class], @selector(imageNameNextWith:));
// 获取替换前的类方法
Method method = class_getClassMethod([UIImage class], @selector(imageNamed:));
// 然后交换类方法
method_exchangeImplementations(otherMethod, method);
}
+ (UIImage *)imageNameNextWith:(NSString *)nameString {
UIImage *image = nil;
image = [UIImage imageNameNextWith:[nameString stringByAppendingString:@"tupian.jpg"]];
return image;
}
关于在imageNameNextWith中调用自身,并不会引起循环引用。
交换之前
SEL(系统方法:ImageNamed) —————> IMP(系统方法:ImageNamed的实现地址指针)
SEL(自己的方法:ImageNamedNextWith) —————> IMP(自己的方法:ImageNamedNextWith的实现地址指针)
交换之后
SEL(系统方法:ImageNamed) —————> IMP(自己的方法:ImageNamedNextWith的实现地址指针)
SEL(自己的方法:ImageNamedNextWith) —————> IMP(系统方法:ImageNamed的实现地址指针)
交换之后,每当再次调用imageNameNextWith方法的时候,实际上执行的是系统方法ImageNamed指向的方法实现,所以不会引起循环调用。
OC对象本质上是指针占用8个字节
OC方法调用顺序:消息发送——>SEL——>IMP——>代码——>函数——>汇编
函数响应式编程RAC
RAC的代理
RAC里面内部实现类似于通知
- 创建信号 提供外界订阅
创建了一个容量为1的可变数组_subscribers
支持多个订阅者订阅该信号
RACSubject *subject = [RACSubject subject];
- 订阅信号(注册通知)
创建订阅者对象
将Block放到订阅者对象中
将订阅者对象放入_subscribers里面
[subject subscribeNext:^(id){
// 函数式编程,免除了遵循协议,引用方法的步骤
}];
- 发送信号(发起通知)
遍历_subscribers取出中的订阅者对象
执行订阅者对象中的Block
[subject sentNext:@"hahha];
RAC中可以使用Selector通过方法名称创建信号,直接订阅
RAC中的KVO
RAC可以直接用Block回调,在观察多个属性的时候,可以避免在回调函数中判断。
RAC同样不能观察到数组的count
RAC监听事件
将按钮的点击事件包装成信号,订阅
RAC中的Timer
使用NSTimer的时候,创建完之后需要添加到NSRunLoop中
在NSTread子线程中需要手动启动NSRunLoop
[[NSRunLoop currentRunLoop] run]
RAC中通过信号创建子线程并发Timer,底层使用GCD创建
RAC中的宏定义
当输入框内容发生变化,相应更新到_label上
RAC(_label, text) = _textField.rac_textSignal;
只要对象的属性发生改变,就会产生信号
RACObserver(self, name) sub....
关于Block中的循环引用,但是在特殊情况下是允许循环应用的出现
NSURLSession中的delegate是强引用,目的是发送请求的时候只需要一个对象。是单例
使用强引用,并不会销毁,导致内存泄漏
Socket探索
IP地址可以在网络上定位到一台终端设备
端口号可以访问到设备上的服务:比如80端口为Apache端口服务
网络的七层协议从上至下:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
标准帧格式数据包
socket处于传输层、IP/TCP协议在网络层
TCP与UDP协议的区别
UDP(用户数据报协议)短信
只管发送,不确认对方是否收到
将数据及源和目的封装成数据包中,不需要建立连接
每个数据报大小限制在64k
因为无需连接,因此是不可靠协议
不需要连接,速度快
TCP(传输控制协议)电话
建立连接,形成传输数据通道
在连接中进行大数据传输(数据大小不受限制)
通过三次握手连接,是可靠的协议,安全送达
必须建立连接,效率稍低
直播推流、游戏是UDP协议,下载的过程是TCP协议
Socket
类比插座,socket需要两端的IP+端口号建立连接
- 创建socket
/*
domain: 协议域 AF_INET = IPV4 IPV6
type: Socket类型 SOCK_STREAM(TCP)/SOCK_DGRAM(UDP)
protocol: IPPROTO_TCP, 传入0, 会自动根据第二个值选择合适的协议
*/
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
- 连接服务器
/*
客户端socket
服务器IP地址结构体指针
结构体长度
*/
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;//IPV4
serverAddr.sin_port = htons(80);//端口号
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");//IP地址
int connectResult = connect();
// 返回0代表连接成功
netcat工具,用于监听本地端口
- 发送数据
/*
客户端socket
发送内容地址
发送内容长度
发送方式标志,一般为0
返回值
发送成功之后返回发送字节长度,失败返回error
*/
send();
- 读取数据
/*
客户端socket
接受内容缓冲区域地址
接受内容缓冲区长度
接受方式标志,0表示阻塞,必须等待服务器返回数据
返回值
接受成功之后返回发送字节长度,失败返回error
*/
uint8_t buffer[1024];
recv();
通过data数据读取二进制数组,解析返回字节流
- 关闭socket
close();
HTTP访问
通过向百度的IP地址,发送"GET HTTP/1.1\n Host: www.baidu.com\n\n"可以接收到百度服务器返回的百度首页数据。
手写了一个HTTP协议
Connection被废弃的原因
异步下载,无法回调数据,因为Runloop默认在子线程不开启
线程管理
线程中有任务才有可能不被释放
HTTPS协议
https本身不会对客户端进行验证
加密算法:RSA
RSA:公钥和私钥
明文+公钥 = 密文
密文+私钥 = 明文
第一次请求HTTPS服务器,客户端安装证书(下载公钥并保存),通讯时通过该公钥加密传输
登录校验时随机盐可以提高安全性
加密详解
Base64
base64是编码方式,不属于加密算法
可以将任意的二进制数据进行编码 编码成为65中字符的文本文件
0-9,a-z,A-Z,+ / =
对称加密:
DES
3DES
AES(高级密码标准)
数学算法:
哈希函数MD5
内存释放
free()以及CFRelease()的区别
单元测试
setup 初始化
tearDown 销毁
所有的测试用例必须以test开头
given
when
then
架构模式
MVC解耦
vc代码过于沉重
代码耦合性过高 UI与Model的通讯
MVP
面向协议编程---代理
@synchronized(self)多线程锁
通过代理使UI与Model通讯
MVVM
双向绑定:数据和UI的绑定,即修改一处另一处随之修改
异步的一般处理:代理、通知、匿名函数(Block)
从数据 -----> UI 通过Block进行通讯
从UI -------> 数据 通过KVO监听通讯
NSMutableArray是线程不安全的,在处理里面的数据的时候需要加锁
多线程
进程
线程
NSThread
- alloc init
+ detacNewThread
- self perform
线程的名称
线程的优先级
多线程的共享资源
线程不安全:获取的数据和预期可能不一样
互斥锁:线程同步,@synchronized 当一个线程在操作数据的时候,其它线程不得操作该数据
原子属性 atomic
原子属性是线程安全的,自旋锁
原子属性的成员变量,在set方法中会添加@synchronized保护线程安全