深化了解 Objective-C 特点内存办理语义

一文彻底搞懂 assign、weak、copy、strong 这些内存办理语义的用法与原理

举个例子:

//
// Dog.h
// oc-demo
//
// Created by 姚明振 on 2022/8/17.
//#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interfaceDog:NSObject@property(nonatomic,assign,nullable)NSObject*assignProperty;
@property(nonatomic,weak,nullable)NSObject*weakProperty;
@property(nonatomic,copy,nullable)NSString*propertyCopy;
@property(nonatomic,strong,nullable)NSObject*strongProperty;
@endNS_ASSUME_NONNULL_END

assign:

深入理解 Objective-C 属性内存管理语义(assign、weak、copy、strong)
想要它不 crash 其实是有办法的,咱们在它所指向目标被毁掉时手动帮它为特点赋 nil:
深入理解 Objective-C 属性内存管理语义(assign、weak、copy、strong)
当然这没有什么实际意义,不能完成咱们预期的目的。

原因是 alloc、init 办法返回的是一个 autorelease 的 NSObject 实例,当走完 init 后如果没有人对他进行 retain 操作,则立马会被毁掉。

深入理解 Objective-C 属性内存管理语义(assign、weak、copy、strong)
让临时变量持有该 NSObject 实例,即在效果域内被强引证着,所以 assign 润饰的特点在这种情况下也能正常拜访指向的目标。例子只为让咱们易于了解,千万不要在项目里这么用。

总结:

  • 只进行简略的赋值操作(把一个指针赋值给该特点,如果是目标则是目标地址,如果是根本数据类型则是该值的地址)
  • 只适用于根本数据类型(非 Objective-C 目标「语法上是支撑润饰 Objective-C 目标的」,如:int,float,NSInterge,CGFloat,及结构体类型)
  • 不影响该特点所指向目标的生命周期(不改动retaincount)
  • 在所指向目标毁掉时不会主动为该特点赋 nil 值

weak:

参考 assign,做个对比

深入理解 Objective-C 属性内存管理语义(assign、weak、copy、strong)
咱们发现跟 assign 不同的是,这儿没有产生 crash,原因是 weak 润饰的特点在所指向目标毁掉时主动为该特点赋 nil 处理。
深入理解 Objective-C 属性内存管理语义(assign、weak、copy、strong)
运用临时变量强引证 NSObject,此刻能正常拜访该特点指向的目标。

总结:

  • 只适用于 Objective-C 目标(语法上就不支撑润饰根本数据类型)
  • 不影响该特点所指向目标的生命周期(不改动retaincount)
  • 在所指向目标毁掉时会主动为该特点赋 nil 值(避免野指针拜访导致 crash)

strong:

深入理解 Objective-C 属性内存管理语义(assign、weak、copy、strong)
能够看到特点值正常输出字符串 “123”,阐明对字符串进行了强引证。
深入理解 Objective-C 属性内存管理语义(assign、weak、copy、strong)
不过当咱们赋值 NSMutableString 实例时,发现这违背了该特点的规划意图。明明是不可变的字符串,刚赋值为123,讲道理在不走 setter 办法时它的值是不允许改动的,可这种事真的产生了,它被修改为 123456。于是引入了 copy 来解决这个问题(下面介绍)。

总结:

  • 只适用于 Objective-C 目标(语法上就不支撑润饰根本数据类型)

  • 会强引证指向的目标(retaincount + 1)

  • 会开释之前指向的目标(retaincount – 1)

  • 当润饰 block 类型特点时(ARC 下表现与 copy 共同)

    • 当指向的目标为 StackBlock 时会把 block 目标复制到堆区成为 MallocBlock
    • 当指向的目标为 MallocBlock 时会增加引证计数(retaincount + 1)
    • 当指向的目标为 GlobalBlock 时什么都不会产生

copy:

当用 copy 润饰一个 NSObject 类型的特点,运行时产生了 crash,并抛出 NSObject 未完成 copyWithZone: 办法的反常。这是由于 copy 润饰的特点会在 setter 办法内对老值进行 release,对新值进行 copy 操作,也就是它真正指向的是传入目标 copy 后的返回值(可能是深 copy 也可能是浅 copy )。这一操作避免了给不可变类型(如:NSString)特点赋值对应可变子类(如:NSMutableString)导致的逻辑反常。

深入理解 Objective-C 属性内存管理语义(assign、weak、copy、strong)
把特点类型改为 NSString(它完成了copyWithZone: 办法)。

深入理解 Objective-C 属性内存管理语义(assign、weak、copy、strong)
这儿打印了字符串 “123”,阐明晰 copy 润饰的特点会对它指向的值进行强引证。

另一方面当为该特点赋值后,持续修改原值时该特点的值不受影响,由于此刻现已把 NSMutableString copy 为了 NSString。

总结:

  • 只适用于 Objective-C 目标(语法上就不支撑润饰根本数据类型)

  • 会强引证指向的目标(retaincount + 1)

  • 会开释之前指向的目标(retaincount – 1)

  • 当润饰 NSString,NSArray 等存在可变类型的子类时(这是 copy 与 strong 的最大差异)

    • 运用 copy 润饰,把可变类型深 copy 为不可变类型,避免不安全修改
    • 不安全的原因是:可变类型的目标能够在不改动目标地址的情况下修改值,由于你的特点是 NSString 类型,此刻值被意外修改是不符合预期的。
  • 当润饰 block 类型特点时

    • 当指向的目标为 StackBlock 时会把block目标复制到堆区成为 MallocBlock
    • 当指向的目标为 MallocBlock 时会增加引证计数(retaincount + 1)
    • 当指向的目标为 GlobalBlock 时什么都不会产生

总结

今日开始复习,决议把自己的了解以文章的形式输出,一方面能加深了解做个笔记,另一方面也期望能给咱们带来点帮助。笔者水平有限,不免有了解偏差与遗失,期望咱们批评指正。