基础知识 - 内存管理

什么情况使用 weak 关键字,相比 assign 有什么不同

  • weak的使用场景
    1. delegate防止循环代理
    2. IBOutlet自身已经进行了一次强引用,没变要再强引用一次
    3. block不会导致野指针也不会导致循环引用
  • weak和assign的区别
    1. weak表明了一种非拥有关系,对该属性发生赋值操作时,设置方法既不保留新值,也不是释放旧值,这个是相对于mrc状态下的strong发生set方法是,release旧值,保留新值
    2. weak只能用来修饰对象,当weak对象释放时,指针会被置位nil,不会出现野指针的情况,很安全
    3. assign可以修饰对象,也可以修饰基本数据类型,单当assign用来修饰对象时,指针不会被置空,再向对象发送消息时,就会崩溃

copy和strong的区别

  • copy:一般用来修饰不可变的对象:NSString,NSArray,NSDictionary等,通过copy修饰的对象,在赋值的时候,release旧值,copy新值

  • strong:一般用来修饰可变对象或者自定义对象,strong修饰的对象在赋值的时候,release旧值,保留新值
    不能使用copy修饰可变对象原因是 copy修饰的对象会release旧值,copy新值,可变对象发生copy时,生成的对象为不可变对象,使用copy后的对象调用copy前对象的方法:add delete等,就会崩溃,找不到方法

  • 深拷贝/浅拷贝

    • 深拷贝:对象拷贝,拷贝时产生了新的对象,修改拷贝的对象对原对象不会有什么影响
    • 浅拷贝:指针拷贝,仅仅是拷贝了对象指针,没有产生新对象,由于拷贝后的对象跟原对象指针地址相同,所有修改任何一个对象都会改变另外一个对象
  • 使用strong修饰NSString/NSArray/NSDictionary为什么不好
    strong在发生赋值时 release旧值,保留新值,这样,当我们strong修饰NSString时,如果发生赋值的对象是一个可变对象,那么赋值之后我们改变可变对象的值,就会导致原对象的值发生改变,从而改变了原有封装,而使用copy修饰,发生copy操作时不可变对象copy之后仍然是不可变对象,浅拷贝,这样不会改变原有封装

runtime 如何实现 weak 属性

 id obj0 = [NSObject new];
 __weak id obj1 = obj0;
 __weak id objA = obj0;
  • runtime 对注册的类,会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指针(obj1、objA)指向的对象(obj0)内存地址作为 key,当此对象的引用计数为0的时候会反向找到 weak 指针(obj1、objA) 并 dealloc。假如 weak 指针(obj1、objA)指向的对象(obj0)内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象(obj1、objA),从而设置为 nil。

@property 的本质是什么

  • 本质: iva+setter+getter (实例变量+setter/getter方法)
  • 在 Category 中添加 @property,只会生成 setter/getter 方法的声明,并不会有具体的代码实现。这是因为 Category 在运行期对象的内存布局已经确定,此时如果添加实例变量就会破坏对象的内存布局,这将会是灾难性的。因此 Category 无法添加实例变量。
  • 分类中不会产生实例变量,而普通对象会产生实例变量,主要原因是category是运行时决议的 分类中如果需要添加属性 可以通过一下方式解决
objc_setAssociatedObject
objc_getAssociatedObject

KVO的实现原理

  • KVO系统关于观察者模式的一种实现
  • KVO运用了isa混写技术,来动态运行时,去为某个类添加一个子类,并重新这个子类的set方法,原有类isa指针指向新创建的对象

BLOCK相关

  • block分为堆区 栈区 全局区的block

  • 是否访问auto局部变量

    1. 没有auto变量 则为全局block
    2. 访问了auto变量则为栈block
    3. 栈区block执行了copy操作就变成了堆区block
  • 通过__block修饰auto变量

    • 将block在栈区的内存拷贝一份到堆区,原auto对象变成了拥有isa指针和一个__forwarding指针,__forwarding指向对象在堆区中的内存位置,这样,当修改变量值,通过__forwarding找到在堆区中的变量并修改其值,栈区block在作用域结束后就自动释放掉,堆区则需要程序员去管理,通过autorelesepool在下一个运行循环结束后,执行release方法

autoreleasepool的实现原理

ARC编辑器和runtime协作进行自动引用计数器
@autoreleasepool{}后,会掉
objc_autoreleasepoolpush
objc_autoreleasepoolpop
autoReleasepool 相当于一个栈容器,将新创建的对象通过push压入到容器中,当这个对象的引用计数器变为0是,通过pop把对象压出栈容器,那么占用的内存也就释放掉了
是oC引入的一种变相的内存回收机制
autoReleasepool自动释放池 当对象调用autorelease后,该对象就会被加入到最近的autoReleasepool,autoReleasepool会在最近的一个运行循环结束后,给push到栈中的所有对象发送一个release方法,对象接收到release方法后,如果该对象引用计数器为0,则该对象会被立即释放

autoreleasepool会在大括号作用域结束时释放内部的对象,所有自建autoreleasepool能加速内存的释放,否则就会被加入到默认的autoreleasepool中
autoreleasepool由一个个autoreleasepoolpage安装双向链表的形式连接起来,上一个page空间满了之后,会创建一个新的page

https://blog.csdn.net/Z1591090/article/details/119108399

在for循环中alloc图片数据等内存消耗较大的场景手动加入到autoreleasepool中,降低内存峰值,减少内存溢出的风险

for (int i = 0; i < 10000; i++) {
        @autoreleasepool {
              Dog * dog = [[Dog alloc]init];
              [dog eat];
         }
 }

然而autoreleasepool也不会万能,比如有大量图片需要获取图片信息 autoreleasepool不能释放掉内存中的图片

for (int i = 0; i < 2000; i++) {
        CGSize size = [UIImage imageNamed:[NSString stringWithFormat:@"%d",i]].size;
    }
///通过imageWithContentsOfFile来解决,使用完图片后,内存释放的问题
for (int i = 0; i < 2000; i++) {
        @autoreleasepool {
            CGSize size = [UIImage imageWithContentsOfFile:filePath].size;
        }
    }

循环引用

  • 自循环引用
  • 相互循环引用 代理
  • 循环引用
    block的循环引用
    通过__weak来破除询货引用

nstimer的循序引用

  • 非循环事件 结束时需要关闭定时器
  • 循环事件 通过中间对象,
    中间对象弱持有nstimer和target,nstimer的回调方法在中间对象中实现,在回调方法中不断的去判断target是否有值,如果原对象释放target则为nil,直接关闭定时器invalidate,如果target有值,则正常的回调

wkwebview的循环引用
[configuration.userContentController addScriptMessageHandler:self name:@"onScan"];
userContentController强引用self self强引用webview,这样就照成了询货引用 解决方法,在viewwilldisapper中removeScriptMessageHandler

  override func viewWillAppear(_ animated: Bool) {
        webView.configuration.userContentController.add(self, name: "onError")
    }
    override func viewWillDisappear(_ animated: Bool) {
        webView.configuration.userContentController.removeScriptMessageHandler(forName: "onError")
    }

大环引用

posted @ 2022-07-25 15:03  qqcc1388  阅读(4)  评论(0)    收藏  举报