ios 内存小结

原创 2015年07月08日 13:46:18

这是 看 书籍  Objective-C 高级编程 ios与os  x多线程和内存管理  一书的随记。

1 arc

首先记录一下几个调试的东西

调试  

1.查看 autoreleasePool 池里面的对象

extern void _objc_autoreleasePoolPrint();

    _objc_autoreleasePoolPrint();

2.通过  clang 可以 查看对应的 汇编代码。其实xcode  的 Product-》Perform Action ->Assemble ""  也就ok了

clang -fobjc-arc -framework Foundation test.m -o test  

3 通过 CFGetRetainCount((__bridge    CFTypeRef)test2));  或者

    extern uintptr_t _objc_rootRetainCount();

    NSLog(@"xxxx %lu",_objc_rootRetainCount(self.array));

可以在 arc 总看到 retaincount 的值。切记 不要依赖这个retaincount 编译器 有很多优化。可能跟你理解的不同。比如 当你 通过一个非 alloc new  copy  mutableCopy 方法获取一个对象的时候 按照道理是 获取的 autorelease 对象。然后在 retain 。编译器 会优化省略 autorelease 过程。


引用计数的思考方式

自己生成的对象,自己所持有

非自己生成的对象,自己也能持有

不在需要自己持有对象时释放

非自己持有的对象 无法释放


方法名称 以 alloc new copy mutableCopy 开头的方法 必须返回给调用者应当持有的对象。

以init 开头的 更加严格 必须返回 id 类型 或者当前类的 类型 或者 子类父类。这个对象不会注册到 autorelease 池里面去。只是


修饰符

_strong

id 类型 和对象类型 默认的修饰符。即:

id  xx = [[NSObject alloc] init]; 实际上就等于

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

简单的说 就是当 strong 修饰符修饰的变量 被赋值的时候。 就相等于 retain 了该对象。 当该修饰符 修饰的变量 抛弃原本指向的对象的时候。 就相当于 release了 该对象。当strong 修饰的变量结束了有效区域的时候。也会release

[objc] view plaincopy
  1. MyTest  *test = [[MyTest alloc]init];  
  2. MyTest  *test2 = test;  
  3. MyTest  *test3 = test;  
  4. MyTest  *test4 = test;  
  5. MyTest  *test5 = test;  
  6.   
  7. NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge  
  8.                                                     CFTypeRef)test));  
  9. test5 = nil;  
  10. NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge  
  11.                                                 CFTypeRef)test));  
  12.   
  13. test4 = [[MyTest alloc]init];  
  14. NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge  
  15.                                                 CFTypeRef)test));  

如上代码所示。因为默认修饰符是strong 所以 当test 赋值给strong修饰的变量的时候。 就retain了 test。所以第一个输出是5. 当 执行 test5 = nil 的时候。 因为 被修饰符为 strong 的变量 抛弃的对象会release 所以 变成了4. 同样道理 最后 test retainCount 变成了3;(ps:这里要说的是 之前在有的资料看过 非arc 跟 arc 混编的 复杂情况下 有可能导致的内存泄露。 主要是 对象是非arc 创建的 在 arc中使用的情况。 当时 给出的解决方案。就是 用完之后 设置为 nil 就行了。 想想这里的了解。其实有一定道理。当然现在找不到那个资料了所以 也没法实验总结)

_weak

当2个对象互相引用导致引用环的时候就需要weak了。 简单的说就是赋值的时候 不改变 retainCount的概念。当然还有当对象释放之后 会设置为 nil。这也是 _weak 和 __unsafe_unretain 的主要区别。

这里有个需要注意的。当使用__weak 变量的时候。不是简单的访问。 是会调用一个方法 retainWeakReference (可以重写这个非公开方法 用来调试。但正式开发千万别加逻辑。) 书上说 会加入到 autoreleasePool 里面去。可是访问结束查看 autoreleasePool 。在使用访问 __weak的时候 也确实可以查看到 retainCount 增加了1. 这里可以 猜测 是只是访问的那里新建了一个 autoreleasePool池。访问结束 就 已经释放了。

这样的作用是因为 weak 不持有该对象。当访问过程中。可能被释放掉。 这样就能保证访问过程中都是有效状态。

_unsafe_unretained

我能想到的跟 __weak 的区别就是 释放了对象的时候 不设置为 nil。用这个修饰符的对象 不被arc内存管理 所管理。

_autoreleaseing

autorelease 的本质就是 创建一个NsAutoreleasePool 对象。当一个对象调用 autorelease 的时候。 会去获取当前使用的 最内侧(如果有嵌套的池的话)的NsAutoreleasePool对象。然后调用 NsAutoreleasePool对象的一个 类似数组 的addobject 方法。让 NsAutoreleasePool对象来持有这个 autorelease 对象。 当调用 NsAutoreleasePool 的 drain方法的时候。清除引用。 就释放了 autorelease对象。

实际上在arc 中 。编译器有很多优化。让原本是autorelease 的对象 根本就不加入到 NSAutoreleasePool中去。

pd:这里需要注意的是 指针变量 默认的修饰符 是 autoreleaseing  比如 id *xx。 是 autoreleaseing的 而 id xx 是 strong的。

属性

属性 修饰符
assign __unsafe_unretained
copy __strong(是retain的复制的新对象)
retain __strong
strong __strong
unsafe_unretained __unsafe_unretained
weak __weak
这里 感觉没有太多的东西需要了解。 只要了解了修饰符 就会知道属性的这些含义了。  需要注意的是 有人喜欢在类里面的方法里面对属性复制的时候 用 下划线开头的变量。这里不建议这样做。 除了 初始化方法 delloc方法,以及 set get 方法内部。 其他地方 对属性赋值的时候 尽量用属性访问。这是为了维护好属性的 copy  strong 等。

另外这里测试的时候 发现 如果在方法里面 有个 __weak属性  每次使用 属性都会导致retainCount +1. 查看汇编代码 有个

_objc_retainAutoreleasedReturnValue 方法被调用了。完全不知道为什么。。

看了下 _objc_autoreleasedReturnValue 函数 返回 注册到 autoreleasepool 里面的对象。 但是 跟 autorelease 函数又不同。 如果 _objc_autoreleasedReturnValue 方法之后 会立即调用 _objc_retainAutoreleasedReturnValue  就不将对象注册到 autoreleasepool 中。而是直接传递到方法或者函数的调用方。 _objc_retainAutoreleasedReturnValue 和 objc_retain 函数不同。 这个作用 就是 上面说到的。 对于不是 new alloc copy mutablecopy 开头的方法。按道理返回的对象是 autorelease 然后在retain 。这个机制就优化了这里的代码

相关文章推荐

Object 获取对象的引用计数(retain count)

MRC可以直接获取对象的引用计数,像这样 NSLog(@"%lu",obj.retainCount); ARC下引用计数由编译器自动管理,不能够直接获取,所以上面的方式不能用了。可以用下面的几种方法:...

IE内存泄露与无法回收研究小结(持续增加中)

一、内存泄露    之前确实看了很多资料,但这位大哥的话可谓画龙点睛,不是奉承他,一下子就打通了我的任督二脉,请看: trarck 写道    IE下的内存泄露原因就是循环引用,IE的垃圾回...
  • e_wsq
  • e_wsq
  • 2011年12月29日 01:00
  • 356

c++primer第十二章动态内存小结-12

第十二章---动态内存 1.动态内存 C++中,动态内存管理是通过一对运算符完成的:new和delete。C语言中通过malloc与free函数来实现先动态内存的分配与释放 C++中new与dele...

深入理解Linux内核个人小结2---内存寻址

一. 内存地址---80X86微处理器中使用的三种不同的地址:       物理地址:用于内存芯片级内存单元寻址。它们与从微处理器的地址引脚发送到内存总线上的电信号相对应。       线性地址:...

进程间通信学习小结(共享内存)

要使用共享内存,应该有如下步骤:1.开辟一块共享内存 shmget()2.允许本进程使用共某块共享内存 shmat()3.写入/读出4.禁止本进程使用这块共享内存 shmdt()5.删除这块共享内存 ...

[原创]小结:opencv中碰到的cvGetSubRect内存泄漏问题(初学者笔记)

近日在写一段opencv程序,使用cvGetSubRect()函数时碰到了内存泄漏问题(参见:Justin http://blog.csdn.net/Justin4wd/archive/2008/06...

进程间通信学习小结(共享内存)

要使用共享内存,应该有如下步骤: 1.开辟一块共享内存 shmget() 2.允许本进程使用共某块共享内存 shmat() 3.写入/读出 4.禁止本进程使用这块共享内存 shmdt() 5...
  • jia0511
  • jia0511
  • 2012年05月31日 08:06
  • 558

《Linux0.11内核完全注释》读后小结 --- 内存寻址

Linux 内存管理主要是32保护模式下,段页式内存管理。如何处理内存相关的寄存器,使得程序可以读写超过4G的内存。x86这个CPU框架,会根据寄存器的值和段页式规则来进行地址变换,进行内存访问。其中...

深入理解Linux内核个人小结8---内存区管理

一. 综述:      静态内存:用来存放内核代码级静态内核数据结构的物理内存部分。如0-1MB部分用来存储BIOS相关东西及保留页框部分(页框0用于存放BIOS加电自检例程)。1-3MB存放...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ios 内存小结
举报原因:
原因补充:

(最多只允许输入30个字)