ARC MRC 属性 循环引用 深浅copy
一 ,先说深浅copy
概念很简单 浅copy 就是copy指针,不copy对象
深copy就是copy 对象 完全是新的对象
(1) 非容器类对象 nsstring
按照网上找到了的 对 字符串 copy copy的是同一个地址 mutablecopy 是copy的对象. 但是试了了一下 感觉不太对:
无论是copy 还是 mutablecopy 其指针地址全部是不一样的.
问题就是出在 &object 和 object 的去别 还有
NSLog(@" %p ",&object);
//他的意思是打印了该对象指针地址的指针地址.比较绕需要好好理解一下
我觉得很多人会犯这个毛病
图片.png这才是他的正确打开方式
(2) 接下来呢是系统容器类的 数组字典
可以看出来,array对象的情况和刚才的字符串一样, 但是 发现数组中的对象还是浅copy 可以看出来 元素的的内存地址是没变的
我试了一下 把数组长得元素改成可变字符串也是这个效果
既然mutablearray数组中的元素是一样的,那么对数组操作一下看看效果
图片.png可以看出对mutablecopy 出来的数组进行对象操作不会影响原数组
但是 如果去修改数组中的元素 那么数组就全部变化了如图
图片.png
由此可见,对于容器而言,其元素对象始终是指针复制
图片.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 这要就循环引用了 这种情况就是 取消定时器的时机不对.如果是控制器 那么就是即将消失时 先取消定时器
- 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
网友评论