这是我参与「第四届青训营 -iOS场」笔记创作活动的的第2篇笔记
内存管理
堆和栈
栈
- 栈,stack,存储push,销毁pop;寄存器中保存stack pointer,指向栈顶
- 存储参数、局部变量、函数调用后返回地址,线程安全
- 由高地址向低地址扩展
堆
- swift,由双向链表组成
- 存储数组和对象,线程共享
- 手动分配,低地址向高地址扩展
引用计数
- 记录当前内存资源有多少指针在引用
- 新增加一个可以访问这个资源的引用,计数器就会+1,反之就会-1;到0的时候就可以被回收
MRC
- 需要手动进行引用计数的管理
- alloc/new/copy/mutableCopy,创建的方法,引用计数=1
- retain 引用计数+1
- release 引用计数-1,到0时,会进行内存回收
Autorelease-Pool
- 把对象放入到自动释放池,会进行延迟统一释放
- 减少内存的峰值
ARC
- 编译器自动加入内存管理代码,不需要手动维护引用计数
- 只要有一个强指针指向内存对象,对象就不能被释放,ARC销毁的时间为强引用的个数为0,若指针需要显示声明
循环引用
- 最常出现的是代理模式,A类引用B类,A类的代理指向C类,C中的属性再指向A
修饰符
- strong,强引用
- weak,弱引用
- copy,在setter中通过深拷贝进行设置对象,调用了copy的方法
代码中用copy修饰不可变对象,保证自己拥有的是一份新的地址空间的值,保证不被外界修改,内存安全
- assign,指定setter进行简单赋值,用于基本类型。NSInteger/CGRect,默认修饰符
深浅拷贝
- 浅拷贝,指针拷贝,复制一个新的指针,复制后指向同一块内存区域
- 深拷贝,内容拷贝,内存中的对象复制了一份,放在一块新的区域,复制后指向的是不同的区域
NSString和NSMutableString
- NSString调用copy后,发生的是浅拷贝,指向的是同一个地址;调用mutableCopy,发生的是深拷贝,指向的不同的地址,创建的是新的NSMutableString对象,再赋值给NSString
- NSMutableString调用copy和mutableCopy,发生的都是深拷贝,指向的是不同的地址
- 对于NString,修饰符无论是strong还是copy,发生的都是浅拷贝,因为是不可变的,因此浅拷贝也没事
- 对于NSMutableString,copy修饰后,发生深拷贝;strong发生的是浅拷贝,因为是直接赋值
内存引起的crash
- 野指针,当所指向的对象被提前释放或者收回,但是该指针没有任何修改,以至于该指针仍指向已经被修改或回收的内存区域
- OOM,某个应用因为内存占用过高,而被操作系统强制终止
block
- 可以分配在栈和对上,也可以是全局的,分配在栈上的块,可以拷贝到堆上,具有引用计数
- 本质上是一个OC对象
使用
- 声明,可以用
typedef定一个block类型,方便实用
// 返回值 (^blockname)(参数);
void (^voidBlockType)(void);
NSInteger (^compute)(NSInteger a, NSInteger b);
- 放入函数中
+ (NSInteger) blockTest1: (voidBlockType)myBlock :(compute)mycompute;
+ (NSInteger) blockTest2: (NSInteger (^)(NSInteger a, NSInteger b))compute;
- 函数调用
+ (NSInteger) blockTest1: (voidBlockType)myBlock :(compute)mycompute {
myBlock();
return mycompute(10, 20);
}
// 作为函数参数
+ (NSInteger) blockTest2: (NSInteger (^)(NSInteger a, NSInteger b))compute {
return compute(10, 20);
}
内存
- 根据存储方式位置分为三种
- 全局
__NSGlobalBlock- 栈
__NSStackBlock- 堆
__NSMallocBlock
- ARC模式下,会默认调用block的copy方法,拷贝到堆区,保证使用安全
- block作为函数返回值
- 将block复制给strong指针
- 作为系统方法参数
- 在MRC下,要用copy修饰block属性
变量捕获
- block默认是值引用,再生成block时,会复制一份block捕获的变量,其中会脱离上下文使用
无论是OC对象还是基本数据类型,都是值拷贝
- block中引用
__block修饰的值,不再捕获变量,而是对象进行名称引用,随着外部值的变化而变化 - block通过self进行变量引用使用时,捕获的是self的值,会给self发送消息,获取变量
分类
- 值捕获,复制对应的值,block中的变量脱离上下文
- 名称引用,跟踪值变化
- 添加__block
- 捕获对象属性
循环引用
- 在block中通过self引用变量,那么就会给self+,此时self也引用block,产生循环引用
- 在block内部进行引用,需要通过
weak self,但是在使用的时候要用__strong引用变量,防止在block执行过程中,self被释放
应用
- 数组变量,
enumerate方法簇 - 传递数据,进行处理回调
- 链式调用,block返回值为
原类对象
// .h
@interface Calculate : NSObject
typedef Calculate* _Nullable (^CalculateBlock)(NSInteger num);
@property (nonatomic) NSInteger result;
- (CalculateBlock)add;
- (CalculateBlock)mul;
@end
// .m
@implementation Calculate
- (CalculateBlock)add {
return ^(NSInteger num) {
self.result += num;
return self;
};
}
- (CalculateBlock)mul {
return ^(NSInteger num) {
self.result *= num;
return self;
};
}
@end
// 调用
Calculate *calculate = [Calculate new];
// 调用add返回的闭包
calculate.add(2).mul(3);
NSLog(@"%ld", calculate.result);
- 异步操作,GCD