iOS-底层原理-内存管理

作者: 贝勒老爷 | 来源:发表于2021-12-29 09:57 被阅读0次
    一使用CADisplayLink、NSTimer有什么注意点?
    • 循环引用

    范例代码

    • CADisplayLink
    @property (strong, nonatomic) CADisplayLink *link;
    
    // 1.发生内存泄露
    self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];
    [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    - (void)linkTest {
        NSLog(@"%s", __func__);
    }
    
    
    • NSTimer
    @property (strong, nonatomic) NSTimer *timer;
    
    // 1.会内存泄露
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    
    - (void)timerTest {
        NSLog(@"%s", __func__);
    }
    
    
    二 介绍下内存的几大区域
    • 代码段:编译之后的代码

    • 数据段

      • 字符串常量:比如NSString *str = @"123"
      • 已初始化数据:已初始化的全局变量、静态变量等
      • 未初始化数据:未初始化的全局变量、静态变量等
    • 栈:函数调用开销,比如局部变量。分配的内存空间地址越来越小

    • 堆:通过alloc、malloc、calloc等动态分配的空间,分配的内存空间地址越来越大

    三 讲一下你对 iOS 内存管理的理解
    四 ARC 都帮我们做了什么?
    • LLVM + Runtime

    首先利用LLVM,帮我们自动生成release,retain,autorelease代码
    需要runtime运行时做一些事情
    即ARC时LLVM编译器和Runtime系统相互协作的一个结果

    五 weak指针的实现原理

    将弱引用存储到一个哈希表里,当对象要销毁时,就会取出当前对象的弱引用表,将该表存储的弱引用都给清除掉

    六 autorelease对象在什么时机会被调用release

    iOS在主线程的Runloop中注册了2个Observer

    • 第1个Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()
    • 第2个Observer
      • 监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()objc_autoreleasePoolPush()
      • 监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()

    代码例子如下

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        Person *person = [[Person alloc] init];
        NSLog(@"%s", __func__);
    }
    
    

    这个Person什么时候调用release,是由RunLoop来控制的
    它可能是在某次RunLoop循环中,RunLoop休眠之前调用了release
    Person *person = [[[Person alloc] init] autorelease];

    6.1 包含在@autoreleasepool中,则在pop的时候,即@@autoreleasepool作用域结束的时候销毁。
    七 方法里有局部对象, 出了方法后会立即释放吗
    • MRC环境下
      不一定,是在当前runloop循环中,即将进入休眠时释放

    • ARC环境下

    autorelease对象:在它所在的线程对应的本次runloop即将进入休眠时释放

    非autorelease对象:出了作用域就释放

    八 思考以下2段代码能发生什么事?有什么区别?
    @property(nonatomic,strong)NSString *name;
    // @property(nonatomic,copy)NSString *name;
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 1000; i++) {
        dispatch_async(queue, ^{
            // 加锁
            self.name = [NSString stringWithFormat:@"abcdefghijk"];
            // 解锁
        });
    }
    
    

    运行结果

    image
    • 分析

    因为给self.name赋值,实际上是调用其set方法

    - (void)setName:(NSString *)name {
        if (_name != name) {
            [_name release];
            _name = [name retain];
        }
    }
    
    

    set方法内部,会先执行release操作,然后再执行retain操作,如果是多个线程同时执行set方法,则会造成释放2次的情况,所有导致坏内存访问。

    代码二

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 1000; i++) {
        dispatch_async(queue, ^{
            self.name = [NSString stringWithFormat:@"abc"];
        });
    }
    
    

    执行结果:正常访问没有奔溃报错。

    • 分析上面两个为什么会出现不同的执行结果
    NSString *str1 = [NSString stringWithFormat:@"abc"];
    NSString *str2 = [NSString stringWithFormat:@"123abc11111111"];
    
    NSLog(@"%@ %@", [str1 class], [str2 class]);
    NSLog(@"%p %p", str1,str2);
    
    

    运行结果

    image

    因为一个是NSTaggedPointerString,一个是__NSCFString

    相关文章

      网友评论

        本文标题:iOS-底层原理-内存管理

        本文链接:https://www.haomeiwen.com/subject/cflvqrtx.html