iOS 中对象与内存的不完全记录

病大虫
• 阅读 937
  • 类型系统

    1. 引用类型( refrence type)

      • 类( class )
      • 指针( pointer )
      • 块( block )
    2. 值类型( value type )

      • 基础数据类型( int, float, double... )
      • 机构体( struct )
      • 枚举( enum )
    3. 类型装饰

      • 协议( protocol )
      • 类别( category )
      • 扩展( extension )
  • 变量

    • 成员变量: @interface {} 中定义的变量

      • @public: 公开访问, 即使定义在 .m 文件中
      • @private: 私有访问, 默认
      • 使用 self->valueName 来访问
    • 属性: @property 定义的变量 or 对象

      • 修饰关键字

        • atmoic: 对 setter/getter 方法加锁(spinlock 自旋锁), 防止多个线程被同时访问(其他线程访问是会等待当前操作完成), 但有额外的资源开销, 且不保证数据的正确性
        • nonatmoic: 不加锁
        • setter/getter = methodName: 指定 setter/getter 的实现方法名
        • readwrite/readonly: 读写选项
        • dynamic: 由开发者实现 setter 和 getter, 声明之后无法使用 _name 的方式访问属性.
        • assign: 指针赋值, 引用计数不会增加, 一般用于基础数据类型和 c 数据类型. 不会自动置 nil. 即声明 A(assign), B=A, A 销毁后 B 的指针就变成了野指针.
        • weak: 声明为 weak 弱引用的对象, 赋值时不会引起赋值对象的引用计数变动, 且赋值对象销毁时, 自身会自动置 nil.
        • strong: 声明为 strong 强引用的对象, 赋值时会引起赋值对象的引用计数+1, 同时释放前一次持有的对象.
        • copy: 声明为 copy 的对象, 每次赋值时都会对赋值对象进行 copy 操作, 使用的是赋值对象的副本, 所以不引起赋值对象的引用计数变动. 注意生成的是不可变版本(非 Mutable)
  • 内存使用

    • 每个函数被调用时都会分配一个独立的栈内存空间
    • 栈(Stack): 由系统管理, 存储函数体的入参, 对象和属性参数地址
    • 堆(Heap): 由开发者管理, 或进程结束时系统全部回收, 存储引用类型, 属性参数的实际内容(值)
  • 析构器和初始化器

    • alloc: 在堆中按对象类型来申请分配合适的空间, 同时将属性和实例变量的内存地址置为 nil(或 0)
    • init: 调用父类 [super init], 并初始化对象的实例变量
    • dealloc: ARC 下对对象属性的引用计数 -1
  • 持有关系与引用计数

    • id obj = [[NSObject alloc] init];

      1. alloc: 以 NSObject 的数据结构, 在堆内存中开辟出相应大小的空间
      2. init: 在堆内存中创建 NSObject 实例, 引用计数为 1, 即"自己持有自己"

        • 我的理解: 内存释放的动作在每个 runLoop 循环都有进行, 若在一段非常大的代码块中(执行时间超过 1 次 runloop 循环即可), 创建了对象实例而又没有被赋值, 正因为"自己持有自己"(引用计数为 1)而可以在保活, 在代码块的较后位置被使用. 直到代码块结束, 里面每个对象进行"自己释放自己", 引用计数 -1 后被释放池安全挥手内存
      3. 在 ARC 中, 编译期会对 NSObject 实例自动追加 autoRelease 方法, 注册到 autoReleasePool, 当 [autoReleasePool drain] 时, 对象实例会自动调用 release 方法
      4. 声明了 id 类型的变量 obj, 存放在函数开辟的栈空间中, obj 的内容是 NSObject 实例指针
      5. 在 ARC 中, 编译期会对 obj 自动追加 retain 方法, 正式持有 NSObject 实例

        • 我的理解:

          • retain 会对引用计数 +1, 所以可将 autoRelease "未来某个时间必定 -1" 理解为 "现在先 -1, 未来某个时间点判断为 0 则释放", 代码执行完后 NSObject 的引用计数依然为 1, 持有者从自身转为变量 obj
          • obj 内容为 x 的实例指针的话, 即可认为 obj 持有 x; 一旦 obj 内容被赋值为 y 的实例指针, 就变成 obj 持有 y, x 没有被持有, 引用计数 -1
    • id obj_2 = obj;

      1. 看源码 _class_createInstanceFromZone 部分, '='的源码实现

设置 property 源码

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) 
{
   if (offset == 0) {
       object_setClass(self, newValue);
       return;
   }

   id oldValue;
   id *slot = (id*) ((char*)self + offset);

   if (copy) {
       newValue = [newValue copyWithZone:nil];
   } else if (mutableCopy) {
       newValue = [newValue mutableCopyWithZone:nil];
   } else {
       if (*slot == newValue) return;
       newValue = objc_retain(newValue);
   }

   if (!atomic) {
       oldValue = *slot;
       *slot = newValue;
   } else {
       spinlock_t& slotlock = PropertyLocks[slot];
       slotlock.lock();
       oldValue = *slot;
       *slot = newValue;        
       slotlock.unlock();
   }

   objc_release(oldValue);
}

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) 
{
   bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
   bool mutableCopy = (shouldCopy == MUTABLE_COPY);
   reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
qchen qchen
3年前
Java中6种创建对象的方式
1、使用关键字new创建对象java//无参构造Testtest1newTest();//有参构造Testtest2newTest("小明",18);new对象过程中,底层发生了什么?1.类加载JVM检查先是否已经加载,没有则执行类加载过程2.声明类型引用声明一个Test类型的引用test3.堆内存分配类加载步骤中已确定对象所需
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Kevin501 Kevin501
4年前
Go语言中new()和make()的区别
1.Go语言中的值类型和引用类型值类型:int,float,bool,string,struct和数组(数组要特别注意,别搞混了)变量直接存储值,分配栈区的内存空间,这些变量所占据的空间在函数被调用完后会自动释放。引用类型:slice,map,chan和值类型对应的指针变量存储的是一个地址(或者理解为指针),指针指向内存中真
Stella981 Stella981
3年前
JavaScript的深拷贝和浅拷贝
一、数据类型数据分为基本数据类型(String,Number,Boolean,Null,Undefined,Symbol)和对象数据类型。、1.基本数据类型的特点:直接存储在栈(stack)中的数据2.引用数据类型的特点:存储的是该对象在栈中引用,真实的数据放在堆内存里。引用数据类型在栈中存储了指针,该指针指向堆中该实
Stella981 Stella981
3年前
JS 苹果手机日期显示NaN问题
问题描述newDate("2019122910:30:00")在IOS下显示为NaN原因分析带的日期IOS下存在兼容问题解决方法字符串替换letdateStr"2019122910:30:00";datedateStr.repl
Wesley13 Wesley13
3年前
GO值类型与引用类型
值类型值类型包括基本数据类型,int,float,bool,string,以及数组和结构体(struct)。值类型变量声明后,不管是否已经赋值,编译器为其分配内存,此时该值存储于栈上。值类型的默认值:varaint//int类型默认值为0varbstring//string类型默认值为n
Wesley13 Wesley13
3年前
Java int与Integer的区别
int与integer的区别从大的方面来说就是基本数据类型与其包装类的区别:int是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象1.Java中的数据类型分为基本数据类型和复杂数据类型int是前者而integer是后者(也就是一个类);因此在类进行初始化时int类的变量初始为0.而Integer的变量则初始化为n
Stella981 Stella981
3年前
JVM调优总结一
数据类型   Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。基本类型包括:byte,short,int,long,cha
Stella981 Stella981
3年前
JVM调优总结(一)基本概念
数据类型Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。    基本类型:保存原始值,即:他代表的值就是数值本身;    引用类型:保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
病大虫
病大虫
Lv1
我有时间和精力可再没年华去浪费
文章
2
粉丝
0
获赞
0