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();
}

进一步谈谈 __strong 和 __weak

之前虽然了解过 double danceblock 循环引用的作用, 以及原理; 但却真是对 __strong 为什么默认要添加上, 不明原因.

__weak __typeof(self) weakSelf = self;
__strong typeof(weakSelf) strongSelf = weakSelf;
  • 理解 Block
  • 理解 __strong , __weak

上面的文章,以汇编的方式,探究解释了原因, 而不是记忆结论, 开阔了思路与眼界.

线程同步知识

Synchronization

  • 原子操作
  • 内存屏障和volatile变量
  • 条件变量
  • Perform Selector Routines

学习 iOS 调用链

谈谈iOS获取调用链

  • 什么是调用堆栈
  • 解析堆栈
  • 递归调用的栈溢出问题