iOS 天问 第5讲
持续的输出可以倒逼你的输入 , 忘记从哪里看到的这句话, 很有道理. 像自己这种菜鸟, 最应该做的就是持续不断的写东西,哪怕写的是狗屎一样, 也不要担心误人子弟, 只要明白,这些东西是经过自己思考过后的知识, 一点一点学习,整理, 那么所记录东西的严谨性,权威性才会不断增强. 总比菜鸟畏手畏脚,不去实行要强得多, 毕竟没有偶像包袱
如何从线上崩溃日记上找线索
文章 1 NSMutableAttributedString initWithData:options:documentAttributes:error 崩溃分析
文章 2 优雅解决 iOS 8 UIScrollView delegate EXC_BAD_ACCESS
- 堆栈汇编分析
- runtime 知识
- 消息转发
- 关联对象
- Method Swizzling
NSPointerArray 知识
上面解决崩溃时,使用了一种可以存储弱引用对象的类, NSPointerArray
The pointer array class is modeled after NSArray, but can also hold nil values. You can insert or remove nil values which contribute to the array's count.
A pointer array can be initialized to maintain strong or weak references to objects, or according to any of the memory or personality options defined by NSPointerFunctionsOptions.
The NSCopying and NSCoding protocols are applicable only when a pointer array is initialized to maintain strong or weak references to objects.
When enumerating a pointer array with NSFastEnumeration using for...in, the loop will yield any nil values present in the array. See Fast Enumeration Makes It Easy to Enumerate a Collection in Programming with Objective-C for more information.
惯例, 先来一段官方描述. 总之就是,
①可以存储 nil 值
② 一般被用来 持有 弱引用对象 , 而不用担心该对象是否会被释放. 释放时会自动置为 null
③ 注意 NSPointerArray is not suitable for subclassing
④ 使用__bridge
转换对象 桥接为 指针类型 void *
⑤ 使用 compact
踢除NULL
元素时, 一定要先 添加一个 NULL
值. 已知 BUG
NSPointerFunctionsOptions 定义数组的内存管理策略 详细参照 iOS 中集合如何弱引用对象
之所以不使用 NSValue
来存储弱引用, 是因为 NSValue
中存储的对象,如果释放,其值不能自动变为nil
,有野指针的问题
了解 __strong 做了什么
// .m - ViewController
- (IBAction)testClick:(id)sender {
StrongObject *obj = [StrongObject new];
[obj invoke];
}
// .m StrongObject
#import "StrongObject.h"
@implementation StrongObject
- (void)invoke {
NSLog(@"invoke %@",self);
_block = ^(){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"self: %@",self);
});
};
_block();
}
- (void)dealloc {
NSLog(@"dealloc %@",self);
}
@end
上面的打印如何, 有什么问题, 为什么?
如果invoke
替换下面会如何
- (void)invoke {
NSLog(@"invoke %@",self);
__weak __typeof(self) weakSelf = self;
_block = ^(){
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"self: %@",strongSelf);
});
};
_block();
}
invoke
再替换下面会如何
- (void)invoke {
NSLog(@"invoke %@",self);
__weak __typeof(self) weakSelf = self;
_block = ^(){
//remove __strong
typeof(weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"self: %@",strongSelf);
});
};
_block();
}
如果再替换下面呢
- (void)invoke {
NSLog(@"invoke %@",self);
__weak __typeof(self) weakSelf = self;
_block = ^(){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"self: %@",strongSelf);
});
};
_block();
}
之前虽然了解过 double dance
在 block
循环引用的作用, 以及原理; 但却真是对 __strong
为什么默认要添加上, 不明原因.
__weak __typeof(self) weakSelf = self;
__strong typeof(weakSelf) strongSelf = weakSelf;
- 理解
Block
- 理解
__strong
,__weak
上面的文章,以汇编的方式,探究解释了原因, 而不是记忆结论, 开阔了思路与眼界.
线程同步知识
- 原子操作
- 内存屏障和volatile变量
- 锁
- 条件变量
- Perform Selector Routines
学习 iOS 调用链
- 什么是调用堆栈
- 解析堆栈
- 递归调用的栈溢出问题
Member discussion