美文网首页
内存管理

内存管理

作者: 健健锅 | 来源:发表于2017-01-23 18:00 被阅读51次

ARC MRC 属性 循环引用 深浅copy
一 ,先说深浅copy
概念很简单 浅copy 就是copy指针,不copy对象
深copy就是copy 对象 完全是新的对象
(1) 非容器类对象 nsstring
按照网上找到了的 对 字符串 copy copy的是同一个地址 mutablecopy 是copy的对象. 但是试了了一下 感觉不太对:

图片.png

无论是copy 还是 mutablecopy 其指针地址全部是不一样的.
问题就是出在 &object 和 object 的去别 还有

 NSLog(@" %p ",&object);
//他的意思是打印了该对象指针地址的指针地址.比较绕需要好好理解一下

我觉得很多人会犯这个毛病

图片.png

这才是他的正确打开方式
(2) 接下来呢是系统容器类的 数组字典

图片.png

可以看出来,array对象的情况和刚才的字符串一样, 但是 发现数组中的对象还是浅copy 可以看出来 元素的的内存地址是没变的
我试了一下 把数组长得元素改成可变字符串也是这个效果

既然mutablearray数组中的元素是一样的,那么对数组操作一下看看效果

图片.png

可以看出对mutablecopy 出来的数组进行对象操作不会影响原数组
但是 如果去修改数组中的元素 那么数组就全部变化了如图


图片.png

由此可见,对于容器而言,其元素对象始终是指针复制

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Collections/Articles/Copying.html

图片.png 图片.png 图片.png

通过官方这篇文档可以看出来 真正的实现 深复制 是通过 归档接档
(3) 平时用到过深浅复制么?
如果说用到深浅复制,,,,,那也就是代码中的 对象赋值了
(4)实现深浅copy:

使用copy功能的前提:
    1、copy:需要遵守NSCopying协议,实现copyWithZone:方法。

    @protocol NSCopying

   - (id)copyWithZone:(NSZone *)zone;

      @end

      2、mutableCopy : 需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法

      @protocol NSMutableCopying

      - (id)mutableCopyWithZone:(NSZone *)zone;

      @end   

总结: 之前很多人问我或者告诉我 说 copy 是浅copy mutablecopy 是深copy .看了前面写的你还会这么认为么. 我觉深浅拷贝应该是一种现象,就只 发生了复制指针,叫浅copy 发生了 对象复制 叫深copy.mutablecopy 在不同的情况下可能是深复制也可能事浅复制.


只有一种情况是浅拷贝:不可变对象调用copy方法时,其他情况都为深拷贝;


二, 关于属性
http://blog.csdn.net/chenyufeng1991/article/details/49685527
【atomic/nonatomic】

(1)atomic[默认属性]:OC使用的一种线程保护技术,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。

(2)nonatomic:非原子性访问,属性赋值的时候不加锁,多线程并发访问会提高性能。但可能会造成不安全。如果不加该属性,则默认setter/getter两个方法都是原子性事务访问。所以,atomic和nonatomic用来决定编译器生成的getter,setter是否为原子操作。

【assign/copy/retain】

(1)assign[默认值]:新建一个指向对象的指针(引用),但是不更改对象的引用计数,不考虑内存管理;常常用于基础数据类型(NSInteger)和C语言数据类型(int ,float,double,char等)。

(2)copy:建立一个引用计数为1的对象,原有对象不做改变;常常用于NSString。在赋值时传入值的一份拷贝,拷贝工作由copy方法执行。

(3)retain: 释放旧的对象,将旧对象的值赋给输入对象,再把输入对象的引用计数+1. ;常常用于NSObject和其子类。需要对变量release,再retain新值。指定retain会在赋值时唤醒传入值的retain消息,此属性只能用于OC对象类型,而不能用于Core Foundation(retain会增加对象的引用计数,而基本数据类型或者Foundation对象都没有引用计数的概念)。

小结:assign和retain的区别,就是使用引用计数,可以对一个内存的释放方便很多。copy就是把原来的内存复制一遍,使各自都拥有一个内存,这样释放的时候也不会出错。

【copy和retain的区别】

copy是创建一个新对象。retain是创建一个指针,引用计数+1.

如:一个NSString对象,内存地址为:0x1111,内容为@“Hello”。

(1)copy到另外一个NSString后,地址为0x2222,内容相同(新建一个内容,内容拷贝),新的对象引用计数为1,旧的对象没有改变。

(2)retain到另外一个NSString后,地址相同(新建一个指针,指针拷贝),内容相同,对象的引用计数+1.

【assign和retain的区别】

(1)接触过C,那么假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。此时a和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的。因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉。

(2)知道了上述assign的问题,那么如何解决呢?最简单的一个方法就是使用引用计数。我们给那块内存设一个引用计数,当内存被分配并且赋值给a时,引用计数是1。当把a赋值给b时,引用计数增加到2.这时如果a不再使用这块内存,他只需要引用计数减1,表明自己不再拥有这块内存。b不再使用这块内存时也把引用计数-1.当引用计数变为0的时候,代表该内存不再被任何指针所引用,系统可以把它直接释放掉。

小结:assign就是直接赋值,从而可能引起1中的问题,当数据为int,float等原生类型时,可以使用assign。retain使用了引用计数,retain引起引用计数+1,release引起引用计数-1.当引用计数为0时,dealloc函数被调用,内存被回收。
【strong和weak】

(1)strong[默认值]:表明这是一个强引用。相当于retain属性。只要引用存在,对象就不能被销毁。当所有强引用消除时,对象才能被释放。

(2)weak:弱引用。相当于assign属性。一般为了避免retain cycles(就是父类中含有子类,父类retain了子类;子类中又调用了父类,子类又retain了父类),导致无法release,才会使用weak。

小结:strong和weak只有你在使用ARC的时候才能使用,此时你是不能使用retain,release,autorelease这些方法的,因为ARC会在必要的地方自动插入这些语句。所以我们需要在对象属性加上strong或者weak。

三,循环引用
循环引用的三种场景
1.定时器

 _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(handleTimer:)
                                                 userInfo:nil repeats:YES];

- (void)dealloc
 {
    [_timer invalidate];
     _timer = nil;
}

这种情况就是 timer 是 self的成员变量,从timer的角度看,在dealloc事 会清除定时器,顺便销对象. 但是self的角度来看是 定时器不停止,那么就不能调用dealloc 这要就循环引用了 这种情况就是 取消定时器的时机不对.如果是控制器 那么就是即将消失时 先取消定时器

  1. block
    block在copy时都会对block内部用到的对象进行强引用(ARC)或者retainCount增1(非ARC)。在ARC与非ARC环境下对block使用不当都会引起循环引用问题,一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,简单说就是self.someBlock = ^(Type var){[self dosomething];或者self.otherVar = XXX;或者_otherVar = ...};block的这种循环引用会被编译器捕捉到并及时提醒。

声明block

@interface ViewController ()
@property(nonatomic,copy)NSString *string;
@property(nonatomic,copy) NSString * oristr;
@property(nonatomic,copy) void (^blocktest)(NSString * blockname) ;
@end
-(void)block{
    __weak ViewController * weekself = self;
    self.blocktest = ^(NSString *blockname ){
    weekself.string = @"dddd";
    };
    [UIView animateWithDuration:1.0 animations:^{
        self.string = @"dddd";
    }];
}

这里就是有一个问题 我们调用系统的block 为什么不会有循环引用,之前有朋友说是 因为系统给处理了 ,原因其实不是这个, 应该是没有达成形成循环引用的条件 比如 上面的这个UIview的block 他的持有者并不是self 而是UIview.所以不会形成循环引用

weak-strong dance

这时候我们进入到 AFNetworking 这个框架里,看看大牛是如何解决这个问题的~

__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;

strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
    strongSelf.networkReachabilityStatusBlock(status);
}

};

strong typeof(weakSelf)strongSelf = weakSelf;就是解决这个问题的关键~先将强引用的对象转为弱引用指针,防止了 Block 和对象之间的循环引用。再在 Block 的第一句代码出将 weakSelf 的弱引用转换成 strongSelf 这样的强引用指针,防止了多线程和 ARC 环境下弱引用随时被释放的问题(因为强引用的引用计数至少为1)。

这里大家一定要有一个认识,weakSelf 位于 Block 的外面,strongSelf 位于 Block 的里面。从内存管理的角度来看,weakSelf 是要比 strongSelf 的声明周期要长的。这样就形成了从弱引用到强引用,再从强引用到弱引用的一种变化,也称作 weak-strong dance。

3.代理
delegate 用week 修饰即可

四,引用计数
ARC中引入的引用计数


自己生成的对象,自己持有
非自己生成的对象,自己也能持有
不在需要自己持有的对象时释放
非自己吃藕的对象无法释放


自己生成的对象,自己持有
alloc new copy mutablecopy
非自己生成的对象,自己也能持有

id obj = [nsmutablearray array];

生成的对象存在,但是并不持有会造成内存泄漏

五.block
使用block时 遇到的问题有两个常见的一个是上面提到的循环引用,一个就是捕获变量
循环引用是因为,block从栈复制到堆上时,对block中引用的数据进行了强引用,那么就会形成循环引用.解决办法就是__week.__week使得引用计数不加1,不去持有该对象.当原对象没有任何强引用的时候,弱引用指针也会被设置为nil。
还有就是捕获变量,block外的变量,被block捕获以后是不能改变的,即使改变了,block以外的也是没有改变的.
如果block中是一个可变的数组,那么对一个数组进行改变那么外面的是有效的.持有数组时,获取的是一个指针地址,那么修改的是同一个指针的内容,所以有效.
__block可以使得修改

1.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
2.__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
3.__block对象可以在block中被重新赋值,__weak不可以。
4.__block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用,__weak只在ARC下使用,可以避免循环引用。


block下循环引用的问题
    __block本身并不能避免循环引用,避免循环引用需要在block内部把__block修饰的obj置为nil
    __weak可以避免循环引用,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong
    的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题

__block与__weak功能上的区别。
    __block会持有该对象,即使超出了该对象的作用域,该对象还是会存在的,直到block对象从堆上销毁;而__weak仅仅是将该对象赋值给weak对象,当该对象销毁时,weak对象将指向nil;
    __block可以让block修改局部变量,而__weak不能。

另外,MRC中__block是不会引起retain;但在ARC中__block则会引起retain。所以ARC中应该使用__weak。

为什么block要用copy,而不是assign,retain?

因为block默认申请的是栈内存,当block所在的方法执行完之后内存空间就被释放。为了可以在外部使用block,需要把block放入堆内存中。MRC中只有copy做到这一点,ARC中copy可以。

为什么NSString要用copy,而不用用strong? (block 字典 数组同理)

一个场景:如果用NSString指向一个NSMutableString的内存空间的话,用strong修饰。当NSMutableString中内容改变时,NSString指针指向的也会改变,而用copy不会

必须使用copy的场景:A对象持有string记做A.string,然后赋值给B对象,记做B.string,若希望B.string的内容改变时A.string不改变就必须用copy
必须用strong的场景:若希望B.string的内容改变时同时A.string也改变则必须用strong

相关面试题
http://www.cocoachina.com/ios/20160301/15498.html
http://www.jianshu.com/p/0ad9957e3716

相关文章

  • iOS内存管理详解

    目录 block内存管理 autorelease内存管理 weak对象内存管理 NSString内存管理 new、...

  • 第10章 内存管理和文件操作

    1 内存管理 1.1 内存管理基础 标准内存管理函数堆管理函数虚拟内存管理函数内存映射文件函数 GlobalMem...

  • 操作系统之内存管理

    内存管理 包括内存管理和虚拟内存管理 内存管理包括内存管理概念、交换与覆盖、连续分配管理方式和非连续分配管理方式(...

  • JavaScript —— 内存管理及垃圾回收

    目录 JavaScript内存管理内存为什么需要管理?内存管理概念JavaScript中的内存管理JavaScri...

  • OC - OC的内存管理机制

    导读 一、为什么要进行内存管理 二、内存管理机制 三、内存管理原则 四、MRC手动内存管理 五、ARC自动内存管理...

  • 3. 内存管理

    内存管理 内存管理包含: 物理内存管理; 虚拟内存管理; 两者的映射 除了内存管理模块, 其他都使用虚拟地址(包括...

  • Go语言——内存管理

    Go语言——内存管理 参考: 图解 TCMalloc Golang 内存管理 Go 内存管理 问题 内存碎片:避免...

  • jvm 基础第一节: jvm数据区

    程序内存管理分为手动内存管理和自动内存管理, 而java属于自动内存管理,因此jvm的职能之一就是程序内存管理 j...

  • 内存管理

    内存管理的重要性。 不进行内存管理和错误的内存管理会造成以下问题。 内存泄露 悬挂指针 OC内存模型 内存管理是通...

  • 11-AutoreleasePool实现原理上

    我们都知道iOS的内存管理分为手动内存管理(MRC)和自动内存管理(ARC),但是不管是手动内存管理还是自动内存管...

网友评论

      本文标题:内存管理

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