ios中mrc是什么意思啥意思?

iOS-旧项目中手动内存管理(MRC)转ARC
时间: 20:56:52
&&&& 阅读:84
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&
在ARC之前,iOS内存管理无论对资深级还是菜鸟级开发者来说都是一件很头疼的事。我参 加过几个使用手动内存管理的项目,印象最深刻的是一个地图类应用,由于应用本身就非常耗内存,当时为了解决内存泄露问题,每周都安排有人值班用 Instruments挨个跑功能,关键是每次都总能检查出来不少。其实不管是菜鸟级还是资深级开发者都避免不了写出内存泄露的代码,规则大家都懂,可是 天知道什么时候手一抖就少写了个release?
好在项目决定转成ARC了,下面将自己转换的过程和中间遇到的问题写出来和大家共享,希望能减少大家解决同类问题的时间。
需要转换的Objective-C文件数量:1000个左右。
开发工具:Xcode 6.0.1
我使用的是Xcode本身提供的ARC转换功能。当然你也可以手动手动转换,那不属于本文范畴,而且其工作量绝对能让你崩溃。
二、转换过程
在进行如此大规模的更改之前,一定要先进行代码备份:直接在本地将代码复制一份,或者记住更改前代码在VCS上的版本号。
过滤无需转换的文件
找出项目中引用的仍使用手动内存管理的第三方库,或者某些你不希望转换的文件,对其添加-fno-objc-arc标记。
Xcode自动转换工具只针对Objective-C对象,只会处理Objective-C/Objective-C++即后缀名为.m/.mm的两种文件,因此其他的C/C++对应的.c/.cpp都无需理会。
执行检查操作
使用Xcode转换工具入口如图所示:
点击Convert to Objective-C ARC后会进入检查操作入口,如图:
该步骤要选择哪些文件需要转换,如果前面将无需转换的文件都添加了-fno-objc-arc标记后,这里可以全选。
点击check按钮后Xcode会帮助我们检查代码中存在的不符合ARC使用规则的错误或警告,只有所有的错误都解决以后才能执行真正的转换操作。
解决错误/告警
执行完check操作后,会给出提示:
三百多个错误,同时还有一千两百多个警告信息,都要哭了。。。
错误和警告的解决内容较多,后面会单独介绍。
执行转换操作
解决完所有的error后,会弹出下述提示界面:
大意是Xcode将要将你的工程转换成使用ARC管理内存,所有更改的代码在真正更改之前
会在一个review界面展示。同时所有的更改完成以后,Xcode会讲项目Target对应的工程设置的使用ARC设置(Objective-C
Automatic Reference
Counting)会被置成YES(上图右上角的警告标识就是在告诉我们项目已经支持ARC了,但工程中有文件还不支持):
这时离成功就不远了,胜利在望!
点击next按钮后跳转到review界面,样式类似于使用Xcode提交SVN的确认提交界面,如下图所示:
该界面列出了所有需要有代码更改的文件,同时能够直接对比转换前和转换后的代码变化。为了稳妥起见,我选择了每个文件都点进去扫了一眼,这也给我们一次机会检查是否漏掉了不能转换的文件。确定一切无误以后,点击右下角的save按钮,一切就大功告成了!
错误/警告解决
ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute
property属性必须指定一个内存管理关键字,在属性定义处增加strong关键字即可。
ARC forbids explicit message send of &release&
这种情况通常是使用包含release的宏定义,将该宏和使用该宏的地方删除即可。
Init methods must return a type related to the receiver type
错误原因是A类里的一个方法以init开头,而且返回的是B类型,好吧,乖乖改方法名。
Cast of C pointer type &ivPointer& (aka &void &) to Objective-C pointer type &iFlyTTSManager_old & requires a bridged cast
cast_pointer_objective-c
这是Toll-Free Bridging转换问题,在ARC下根据情况使用对应的转换关键字就行了,后文会专门介绍。
解决警告的目的是消除警告处代码存在的隐患,既然Xcode给了提示,那么每一个警告信息都值得我们认真对待。
Capturing self in this block is likely to lead to a retain cycle
这是典型的block循环引用问题,将block中的self改成使用指向self的weak指针即可。
Using &initWithArray:& with a literal is redundant
好吧,原来是没必要的alloc操作,直接按Xcode提示将alloc删除即可:
Init methods must return a type related to the receiver type
原来是A类里的一个方法以init开头,而且返回的是B类型,好吧,乖乖改方法名。
Property follows Cocoa naming convention for returning &owned& objects
这是因为@property属性的命名以new开头了,可恶。。。修改方法是将对应的getter方法改成非new开头命名的:
ARC下方法名如果是以new/alloc/init等开头的,而且还不是类的初始化方法,就该小心了,要么报错,要么警告,原因你懂的。
Block implicitly retains &self&; explicitly mention &self& to indicate this is intended behavior
意思是block中使用了self的实例变量
_selectedModeMarkerView,因此block会隐式的retain住self。Xcode认为这可能会给开发者造成困惑,或者因此而
因袭循环引用,所以警告我们要显示的在block中使用self,以达到block显示retain住self的目的。
该警告有两种改法: ①按照Xcode提示,改成self-&_selectedModeMarkerView:
②直接将该警告关闭 警告名称为:Implicit retain of &self& within blocks 对应的Clang关键字是:-Wimplicit-retain-self
Weak property may be unpredictably set to
nil 和 Weak property &delegate& is accessed multiple times in this method
but may be unpr assign to a strong variable to
keep the object alive
这是工程中数目最多的警告,这是因为所有的delegate属性都是weak的,Xcode默认开启了下图中的两个警告设置,将其关闭即可:
Capturing &self& strongly in this block is likely to lead to a retain cycle
这是明显的block导致循环引用内存泄露的情况,之前代码中坑啊!修改方案:
Method parameter of type &NSError __autoreleasing & with no explicit ownership
这种就不用说了,按警告中的提示添加__autoreleasing关键字即可。
以上列出的错误和警告只是数量较多的,还有很多其他就不在这里一一列举了。
另外,推荐&&大神关于Clang中几乎所有warning的名称和对应报错提示语的网站:,以后解决warning类问题就简单多了!
Xcode自动转换
关键字转换
Xcode会自动将某些关键字自动转换成ARC的对应版本。
retain自动转成strong,如图:
assign关键字转成weak
修饰Objective-C对象或者id类型对象的assign关键字会被转成weak,如图:
但是修饰Int/bool等数值型变量的assign不会自动转换成weak,如图:
关键字删除
和手动内存管理相关的几个关键字,比如:release/retain/autorelease/super dealloc等会被删除;
dealloc方法中如果除了release/super dealloc语句外,如果别的代码,dealloc方法会保留,如图:
如果没有整个方法都会被删除:
关键字替换
在转换时block关键字会被自动替换成weak:
@autoreleasepool
NSAutoreleasePool不支持ARC,会被替换成@autoreleasepool:
关于被宏注释代码
使用宏定义的对象释放代码
宏定义如下所示:
#define&RELEASE_SAFELY(__POINTER)&{&\
[(__POINTER)&release];&(__POINTER)&=&&}
在执行ARC转换检查操作时,Xcode会在使用该宏的地方报错:
将该宏和使用该宏的地方删除即可。
被宏注释掉的代码,Xcode在转换时是不会处理的,如图:
PS:这是相当坑的一点,因为你根本预料不到工程中使用了多少宏,注释掉了多少代码。当你执行完转换操作,以为就大功告成的时候,却在某天因为一个宏的开启遇到了一堆新的转ARC不彻底的问题。这种问题也没招,只能遇到一个改一个了。
ARC和block
不管是手动内存管理还是ARC,block循环引用导致的内存泄露都是一个令人头疼的问题。在MRC中,解决block循环引用只需要使用__block关键字,在ARC下解决与block的使用就略显复杂了:
__block关键字
block内修改外部定义变量
和手动内存管理一样,ARC如果在block中需要修改block之外定义的变量需要使用__block关键字修饰,比如:
__block&NSString&*name&=&@"foggry";
pletionBlock&=&^(){
&&&&name&=&@"wangzz";
上例中name变量需要在block中修改,因此必须使用__block关键字。
__block在MRC和ARC中的区别
在ARC下的block中使用__block关键字修饰的对象时,block会retain该对象;而在MRC下却不会retain。关于这点在官方文档Transitioning to ARC Release Notes中有详细的描述:
In manual reference counting mode, has the effect of not
retaining x. In ARC mode, defaults to retaining x (just like
all other values).
下面的代码不管在MRC还是ARC中myController对象都是有内存泄露的:
MyViewController&*myController&=&[[MyViewController&alloc]&init&];
pletionHandler&=&&^(NSInteger&result)&{
&&&[myController&dismissViewControllerAnimated:YES&completion:nil];
内存泄露问题在MRC中可以按如下方式更改:
MyViewController&*&__block&myController&=&[[MyViewController&alloc]&init&];
pletionHandler&=&&^(NSInteger&result)&{
&&&&[myController&dismissViewControllerAnimated:YES&completion:nil];
然而在ARC中这么改就不行了。正如开始所说的那样,在ARC中pletionHandler的block会retainmyController对象,使得内存泄露问题仍然存在!!
在ARC中该问题有两种解决方案,第一种:
MyViewController&*&__block&myController&=&[[MyViewController&alloc]&init&];
pletionHandler&=&&^(NSInteger&result)&{
&&&&[myController&dismissViewControllerAnimated:YES&completion:nil];
&&&&myController&=&
该方法在block中使用完myController时,是它指向nil。没有strong类型的指针指向myController指向的对象时,对象会被释放掉。
第二种种解决方案,直接使用weak代替block关键字:
MyViewController&*myController&=&[[MyViewController&alloc]&init&];
MyViewController&*&__weak&weakMyViewController&=&myC
pletionHandler&=&&^(NSInteger&result)&{
&&&&[weakMyViewController&dismissViewControllerAnimated:YES&completion:nil];
该方法直接避免了对block对myController对象的retain。
存在循环引用关系
如果self直接或者间接的对block存在强引用,在block中又需要使用self关键字,此时self和block就存在循环引用的关系。此时必须使用__weak关键字定义一个指针指向self,在block中使用该指针来引用self:
MessageListController&*&__weak&weakSelf&=&
self.messageLogic.loadMoreBlock&=&^(IcarMessage&*&theMessage)&{
&&&&[weakSelf.tableView&setPullTableIsLoadingMore:YES];
需要说明的是,尽管上例中weakSelf指针对self只是弱引用,但是self对block却是强引用,self的生命周期一定是长于block的,因此不用担心在block中使用weakSelf指针时,其指向的self会被释放掉。
不存在循环引用关系
下面的例子:
MyViewController&*myController&=&[[MyViewController&alloc]&init&];
MyViewController&*&__weak&weakMyController&=&myC
pletionHandler&=&&^(NSInteger&result)&{
&&&&MyViewController&*strongMyController&=&weakMyC
&&&&if&(strongMyController)&{
&&&&&&&&[strongMyController&dismissViewControllerAnimated:YES&completion:nil];
&&&&else&{
如前面所说,pletionHandler的block中不能直接使用myController对象,会造成内存泄露,
因此需要先用一个weak的指针指向myController对象,然后在block中使用该weak指针。但是为了确保在block执行的时候
myController对象没有被释放掉,就在block一开始的地方定义了一个临时的strong类型的指针strongMyController指
向weak指针weakMyController,其实最终的结果就是block中对myController对象强引用了。在block执行完被销毁的
时候,strongMyController指针变量会被销毁,其最终指向的myController对象因此也会被销毁。这样在使用一个对象的时候做就
保证了该对象是存在的,使用完了再放弃该对象的所有权。
ARC和Toll-Free Bridging
MRC下的Toll-FreeBridging不涉及内存管理的转移,Objective-C(后文简称OC)和Core Foundation(后文简称CF)各自管理各自的内存,相互之间可以直接交换使用,比如:
NSLocale&*gbNSLocale&=&[[NSLocale&alloc]&initWithLocaleIdentifier:@"en_GB"];
CFLocaleRef&gbCFLocale&=&(CFLocaleRef)gbNSL
而在ARC下,事情就会变得复杂一些。因为ARC能够管理OC对象的内存,却不能管理CF对象,CF对象依然需要我们手动管理内存。在CF和OC之间
bridge对象的时候,问题就出现了,编译器不知道该如何处理这个同时有OC指针和CF指针指向的对象。这时候,需要使用__bridge,
__bridge_retained, __bridge_transfer等修饰符来告诉编译器该如何去做。
它告诉编译器仍然负责管理好在OC一端的引用计数的事情,开发者也继续负责管理好在CF一端的事情,比如:
CFStringRef&cfString&=&CFStringCreateWithCString(kCFAllocatorDefault,&"CFString",&kCFStringEncodingUTF8);
NSString&*ocString&=&(__bridge&NSString&*)cfS
CFRelease(cfString);
NSLog(@"%@",ocString);
__bridge_retained 或 CFBridgingRetain
二者作用是一样的,只是用法不同。
告诉编译器需要retain对象,而开发者在CF一端负责释放。这样就算对象在OC一端被释放,只要开发者不释放CF一端的对象, 对象就不会被真的销毁。
NSArray&*ocArray&=&[[NSArray&alloc]&initWithObjects:@"foggry",&nil];
CFArrayRef&cfArray&=&(__bridge_retained&CFArrayRef)ocA
CFRelease(cfArray);
__bridge_transfer 或 CFBridgingRelease
二者作用也是一样的,只是用法不同。
该关键字告诉编译器bridge的同时,也转移了对象的所有权,比如:
CFStringRef&cfString&=&CFStringCreateWithCString(kCFAllocatorDefault,&"CFString",&kCFStringEncodingUTF8);
NSString&*ocString&=&(__bridge_transfer&NSString&*)cfS
NSLog(@"%@",ocString);
转换过程中大家只需要根据具体需求选用适当的关键字即可。
另外,在ARC中id和void *也不能直接相互转换了,必须通过Toll-FreeBridging使用适当的关键字修饰。
ARC和IBOutLet
对于IBOutLet属性应该用strong还是weak一直都有疑惑。关于这一点官方文档是这么介绍的:
From a practical perspective, in iOS and OS X outlets should be defined
as declared properties. Outlets should generally be weak, except for
those from File&s Owner to top-level objects in a nib &&&file
(or, in iOS, a storyboard scene) which should be strong.
Outlets that you create should therefore typically be weak.
那么长的一段英文想说的是:如果nib文件构建的view是直接被Controller引用的顶层view,对应的IBOutLet属性应该是strong;
如果view是顶层view上的一个子view,那么该view的属性应该是weak,因为顶层view被Controller使用strong属性引用了,而顶层view本身又持有该view;
如果Controller对某个view需要单独引用,或者Controller没有引用某个view的父view,那么其属性也应该是strong。
好吧,其实我能说如果你实在懒得区分什么时候用strong,什么时候用weak,那就将所以后的IBOutLet属性都设成strong吧!在
Controller销毁的时候,对应的IBOutLet实例变量也会被销毁,strong指针会被置成nil,因此也不会有内存问题。
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&原文:/CoderAlex/p/5232206.html
教程昨日排行
&&国之画&&&& &&&&&&
&& &&&&&&&&&&&&&&
鲁ICP备号-4
打开技术之扣,分享程序人生!Pages: 1/3
主题 : MRC 下 的block,一个非常容易被忽视的问题
级别: 版主
UID: 123750
发帖: 2117
可可豆: 3539 CB
威望: 3460 点
在线时间: 1623(时)
发自: Web Page
来源于&&分类
MRC 下 的block,一个非常容易被忽视的问题&&&
block 目前大有慢慢取代传统delegate的趋势,确实,好用、便捷,但是产生的问题也不容忽视。我今天说的并不是循环引用的问题,这话题已经被讨论烂了。这次说的是在mrc下使用block 时一个非常容易被忽视的问题。我相信不少人碰到过,但是,大部分人并未解决。像我这种c系的程序员,坚守mrc 并不是单纯的为了装逼,毕竟,我还要写c 代码。所以手动维护内存只是习惯并非洁癖。block早在4.0就已经加入oc,但我很奇怪为啥这个问题至今讨论的人都异常的少。不废话,直接上代码@interface SecondVC (){ EXModel *}@end@implementation SecondVC- (void)dealloc{    [model release];    [super dealloc];}- (void)viewDidLoad {    [super viewDidLoad];
model = [[EXModel alloc] init];
self.view.backgroundColor = [UIColor yellowColor];
__block __typeof(self)weakSelf =
model.EXModel_CallBack = ^void(NSString *string){
if(weakSelf)        {            [weakSelf doSomething];        }    };}- (void)doSomething{ NSLog(@&hi i'm here!&);}比较简单的一个demo,我觉得分析拆解一个问题,代码越简单越好。 EXModel 只是一个NSObject 子类  里面只有一个block属性 EXModel_CallBack,EXModel_CallBack这个属性模拟的就是 一个回调事件。但是这个回调时间调起的时间点比较特殊,是延迟3秒之后才执行,下面是重点: 如果3秒还没过。SecondVC 已经被释放了,但是我们知道EXModel_CallBack 是copy到堆了,并未跟随一起释放,3秒时间点到,代码执行到block内部,这时候我的判断 if(weakSelf) 你猜会怎么着??好多人都认为weakSelf 指向的是self,只是self 的一个”不会增加引用计数“的引用(请允许我这么啰嗦,毕竟mrc 下不允许使用__weak,还是有所区别),既然SecondVC 已经释放,那么 if(weakSelf) 肯定不会执行下去。然而,实时证明,代码会继续执行到   [weakSelf doSomething]; 之所以这样,原因非常简单。。。weakSelf 这时候是个野指针。。。野指针也是指针对吧?反正,这个野指针并不为NULL,虽然它指向的内存并未有什么鸟用,然而代码并不知道。所以 执行[weakSelf doSomething]; 必然闪退。这个问题看起来并不复杂,但是很多人使用block出现莫名去剥的 bad_exc_.....这种引用空对象的错误。并未自习思考问题所在。 我见过另外一种解决方案。就是在 if(weakSelf) 上面一行加上__strong __typedef(weakSelf)strongSelf=weakS然而我要告诉你的是这并没有什么卵用。。。weakSelf 已经是野指针,strong它有和意义?哪怕你retain copy 都不会有效果。之所以很多人写类似的代码没有碰到过闪退,是因为 model.EXModel_CallBack = ^void(NSString *string){} 一般情况下而言都是立即返回。不会给你释放SecondVC的机会。不过既然使用了block,相信就不能禁止异步回调的存在,我让model.EXModel_CallBack = ^void(NSString *string){} 延迟3秒回调只是模拟异步回调的过程,亦或者,一个耗时的for循环,你会想到把它放进子线程执行,但是执行完毕之后回调呢?就是这种情况下面提一下arc,事实证明arc下 这样写并不会出现问题,即时是异步回调也不会。就其原因,当回调到来判断if(weakSelf) 的时候 weakSelf这时等于nil,看来arc 自动内存管理还是非常有好处的。手动管理内存,并不能保证释放的对象指针等于nil 也就是哪都不指向,请仔细区别 空指针与指针指向的内存为空的区别。原因只有一个,那就是:mrc下 当SecondVC 对象释放了之后,它的引用指针并不指向空内存,而是随机指向的野指针(真他妈可怕。。。)也不废话了,直接说解决办法:将 if(weakSelf) 改成 if(malloc_zone_from_ptr(weakSelf))   malloc_zone_from_ptr这函数还是之前在写c 的时候 偶尔会用到,机会很少,是标准库的东西, 作用就判断weakself 这个指针是否有有效的内存分配,所谓有效的内存分配就是不管是系统还是你手动或者讲动态分配的内存,这都叫有效分配,野指针,以上都不包含。最后说一句,有时候,别人写的代码,照猫画虎也不是不可以,但是真正碰到问题了,还是得自己亲自去研究,与其如此,何必不开始就把它弄清楚呢?
级别: 新手上路
UID: 356746
可可豆: 45 CB
威望: 44 点
在线时间: 681(时)
发自: Web Page
所以说ARC的weak属性是很好用的啊,MRC下一个对象的引用计数为0的时候其实是不会进行置nil操作的
级别: 版主
UID: 123750
发帖: 2117
可可豆: 3539 CB
威望: 3460 点
在线时间: 1623(时)
发自: Web Page
回 1楼(nn1243) 的帖子
是的 这点mrc 确实不如arc&&
级别: 侠客
UID: 526285
可可豆: 432 CB
威望: 333 点
在线时间: 227(时)
发自: Web Page
没怎么了解过MRC&& 新手需要详细了解MRC如何使用么。求大神指点下
级别: 侠客
UID: 501860
可可豆: 228 CB
威望: 185 点
在线时间: 678(时)
发自: Web Page
级别: 精灵王
UID: 34556
发帖: 1368
可可豆: 1105 CB
威望: 2171 点
在线时间: 1675(时)
发自: Web Page
mark mark mark mark mark mark mark mark mark
级别: 版主
UID: 123750
发帖: 2117
可可豆: 3539 CB
威望: 3460 点
在线时间: 1623(时)
发自: Web Page
回 3楼(微末凡尘) 的帖子
这个。。。。怎么说呢。。mrc 相对于arc 其实大部分都只是手动添加一个release的问题。既然习惯使用arc 了 ,了不了解并不太重要
级别: 新手上路
可可豆: 59 CB
威望: 59 点
在线时间: 94(时)
发自: Web Page
学习了,感谢楼主分享,尤其解决方法那里感觉自己对C及其标准库的了解太少了。。
级别: 侠客
UID: 500204
可可豆: 389 CB
威望: 285 点
在线时间: 194(时)
发自: Web Page
级别: 骑士
UID: 530891
可可豆: 720 CB
威望: 512 点
在线时间: 819(时)
发自: Web Page
Pages: 1/3
关注本帖(如果有新回复会站内信通知您)
发帖、回帖都会得到可观的积分奖励。
按"Ctrl+Enter"直接提交
关注CocoaChina
关注微信 每日推荐
扫一扫 关注CVP公众号
扫一扫 浏览移动版[iOS]MRC和ARC - CSDN博客
[iOS]MRC和ARC
objective-c中提供的两种内存管理机制MRC(MannulReference Counting) 和ARC(Automatic Reference Counting),分别提供内存的手动和自动管理,
retain,release,autorelease。
retain,作用将内存数据的所有权赋给另一个指针变量,引用计数加1,即retainCount +=1
release,释放指针变量对内存数据的所有权,引用计数减1,即retainCount -+1
autorelease,该方法将内存的管理放到autoreleasepool中
reatin和release操作的是引用计数。引用计数为0,便自动释放内存。
//假设Number为预定义的类
Number* num = [[Number alloc] init];
Number* num2 = [num retain];//此时引用记数+1,现为2
[num2 release]; //num2 释放对内存数据的所有权 引用记数-1,现为1;
[num release];//num释放对内存数据的所有权 引用记数-1,现为0;
@autoreleasepool的用法
@autoreleasepool {
&&&&&&&Number* num = [[Number alloc] init];
&&&&&&&&&&&&& [numautorelease];//由autoreleasepool来管理其内存的释放
对与Objective-c中属性的标识符可以总结为:
@property (nonatomic/atomic,retain/assign/copy, readonly/readwrite) Number*
(1)&nonatomic/atomic,表示该属性是否是对多线程安全的,是不是使用线程锁,默认为atomic,
(2)&retain/assign/copy,是有关对该属性的内存管理的,
assign&is the default. In the setter that is created by @synthesize, the value will simply be assigned to the attribute, don’t operate the retain count. My understanding is that &assign& should be used for non-pointer
attributes.
l&& &retain&is needed when the attribute is a pointer to an object. The setter generated by@synthesize will retain (aka add a retain count) the object. You will need to release the object when you are finished with it.
l&& &copy&is needed when the object is mutable. Use this if you need the value of the object as it is at this moment, and you don't want that value to reflect any changes made by other owners of the object. You will
need to release the object when you are finished with it because you are retaining the copy.
(3)&&&&&&readwrite /readonly -&readwrite& is the default. When you @synthesize, both a getter and a setter will be created for you. If you use &readonly&, no setter will be created. Use it for a value you don't want
to ever change after the instantiation of the object.
ARC(AutomaticReference Counting)
在ARC中与内存管理有关的标识符,可以分为变量标识符和属性标识符,对于变量默认为__strong,而对于属性默认为unsafe_unretained。也存在autoreleasepool。
ARC主要提供了4种修饰符,他们分别是:(有两个下横线)
__autoreleasing,
&__unsafe_unretained。
对于变量标识符的用法:
__strong Number* num = [[Number alloc]init];
或者 Number* __strong num = [[Number alloc]init];
后一种才是官方推荐的写法。
在ARC内存管理模式下,其属性的标识符存在以下几种:
@property (nonatomic/atomic, & assign/retain/strong/weak/unsafe_unretained/copy,& & readonly/readwrite) Number*//默认为unsafe_unretained
基本数据类型默认关键字是atomic,readwrite,assign
对于普通的OC对象atomic,readwrite,strong
其中assign/retain/copy与MRC下property的标识符意义相同,strong类似与retain,assign类似于unsafe_unretained,strong/weak/unsafe_unretained与ARC下变量标识符意义相同,只是一个用于属性的标识,一个用于变量的标识(带两个下划短线__)。所列出的其他的标识符与MRC下意义相同。
(1)对于assign,你可以对标量类型(如int)使用这个属性。你可以想象一个float,它不是一个对象,所以它不能retain、copy。
(2)对于copy,指定应该使用对象的副本(深复制),前一个值发送一条release消息。基本上像retain,但是没有增加引用计数,是分配一块新的内存来放置它。特别适用于NSString,如果你不想改变现有的,就用这个,因为NSMutableString,也是NSString。
strong,weak,unsafe_unretained都是是用来声明属性的,如果想声明变量就用_strong,_weak,_unsafe_unretained,_autoreleasing
对于变量的标识符有:
(1) __strong,默认,只要有强指针指向它,就保持活跃,引用计数加1。
(2) __weak,一个有weak修饰符的对象a,其所引用的对象b,如果只有弱指针指向b,b也会被销毁,nil被赋值给a。
(3)__unsafe_unretained,类似于一个在引用的对象没有强指针是,不会被自动赋值为nil的weak指针,容易造成野指针,引起程序崩溃。可以理解为assign:纯粹只是将引用指向对象,没有任何额外的操作,在指向对象被释放时依然原原本本地指向原来被释放的对象(所在的内存区域)。所以非常不安全。现在完全可以忽略。
(4)__autoreleasing,表示在autorelease pool中自动释放对象的引用,和MRC时代autorelease的用法相同。定义property时不能使用这个修饰符,任何一个对象的property都不应该是autorelease型的。
以下两行代码的意义是相同的。
NSString *str = [[[NSString alloc] initWithFormat:@&hehe&] autorelease]; // MRC
NSString *__autoreleasing str = [[NSString alloc] initWithFormat:@&hehe&]; // ARC
__autoreleasing在ARC中主要用在参数传递返回值(out-parameters)和引用传递参数(pass-by-reference)的情况下
比如常用的NSError的使用:
NSError *__&
if (![data writeToFile:filename options:NSDataWritingAtomic error:&error])&
  NSLog(@&Error: %@&, error);&
(在上面的writeToFile方法中error参数的类型为(NSError *__autoreleasing *))
*error指向的对象在创建出来后,被放入到了autoreleasing pool中,等待使用结束后的自动释放,函数外error的使用者并不需要关心*error指向对象的释放。
另外一点,在ARC中,所有这种指针的指针 (NSError **)的函数参数如果不加修饰符,编译器会默认将他们认定为__autoreleasing类型。
比如下面的两段代码是等同的:
- (NSString *)doSomething:(NSNumber **)value
& & & & // do something &
- (NSString *)doSomething:(NSNumber * __autoreleasing *)value
& & & & // do something &
除非显式给value声明了__strong,否则value默认就是__autoreleasing的。
栈中指针默认值为nil
无论是被strong,weak还是autoreleasing修饰,声明在栈中的指针默认值都会是nil。所有这类型的指针不用再初始化的时候置nil了。虽然好习惯是最重要的,但是这个特性更加降低了“野指针”出现的可能性。
在ARC中,以下代码会输出null而不是crash:)
- (void)myMethod&
& & NSString *
& & NSLog(@&name: %@&, name);
变量标识符eg:
& & 1.&__strong NSString *yourString = [[NSString alloc] initWithUTF8String:&string 1&]; &&
& & 2. & &__weak &NSString *myString = yourS &&
& & 3. & &__unsafe_unretained NSString *theirString = myS &
& & 4. & &yourString = &&
& & 5. & &//现在yourString与myString的指针都为nil,而theirString不为nil,但是是野指针。 &
如果NSString *str = @&str test&;这样将声明一个字符串常量,这样声明的不受上面所说的限制。
& & 1. & &__strong NSString *yourString = @&test string&; &&
& & 2. & &__weak &NSString *myString = yourS &&
& & 3. & &yourString = &&
& & 4. & &//现在myString还是有值的 &
NSString *str = [[NSString alloc] &initWithString:@&test&];这样返回的也是字符串常量, 效果与 NSString *str = @&test&;是一样的。 &但得遵循苹果内存管理,在非ARC的情况下还是要调用release,其实不需要调用也不会内存泄漏。
__autoreleasing的用法??
ARC与Block
在MRC时代,Block会隐式地对进入其作用域内的对象(或者说被Block捕获的指针指向的对象)加retain,来确保Block使用到该对象时,能够正确的访问。
这件事情在下面代码展示的情况中要更加额外小心。
MyViewController *myController = [[MyViewController alloc] init…];
// 隐式地调用[myController retain];造成循环引用
pletionHandler =& ^(NSInteger result) {
&& [myController dismissViewControllerAnimated:YES completion:nil];
[self presentViewController:myController animated:YES completion:^{
&& [myController release]; // 注意,这里调用[myController release];是在MRC中的一个常规写法,并不能解决上面循环引用的问题
在这段代码中,myController的completionHandler调用了myController的方法[dismissViewController...],这时completionHandler会对myController做retain操作。而我们知道,myController对completionHandler也至少有一个retain(一般准确讲是copy),这时就出现了在内存管理中最糟糕的情况:循环引用!简单点说就是:myController
retain了completionHandler,而completionHandler也retain了myController。循环引用导致了myController和completionHandler最终都不能被释放。我们在delegate关系中,对delegate指针用weak就是为了避免这种问题。
不过好在,编译器会及时地给我们一个警告,提醒我们可能会发生这类型的问题:
对这种情况,我们一般用如下方法解决:给要进入Block的指针加一个__block修饰符。
这个__block在MRC时代有两个作用:
o说明变量可改
o说明指针指向的对象不做这个隐式的retain操作
一个变量如果不加__block,是不能在Block里面修改的,不过这里有一个例外:static的变量和全局变量不需要加__block就可以在Block中修改。
使用这种方法,我们对代码做出修改,解决了循环引用的问题:
MyViewController * __block myController = [[MyViewController alloc] init…];
pletionHandler =& ^(NSInteger result) {
& & [myController dismissViewControllerAnimated:YES completion:nil];
//之后正常的release或者retain
在ARC引入后,没有了retain和release等操作,情况也发生了改变:在任何情况下,__block修饰符的作用只有上面的第一条:说明变量可改。即使加上了__block修饰符,一个被block捕获的强引用也依然是一个强引用。这样在ARC下,如果我们还按照MRC下的写法,completionHandler对myController有一个强引用,而myController对completionHandler有一个强引用,这依然是循环引用,没有解决问题:(
于是我们还需要对原代码做修改。简单的情况我们可以这样写:
__block MyViewController * myController = [[MyViewController alloc] init…];
pletionHandler =& ^(NSInteger result) {
& & [myController dismissViewControllerAnimated:YES completion:nil];
& & myController =& // 注意这里,保证了block结束myController强引用的解除
在completionHandler之后将myController指针置nil,保证了completionHandler对myController强引用的解除,不过也同时解除了myController对myController对象的强引用。这种方法过于简单粗暴了,在大多数情况下,我们有更好的方法。
这个更好的方法就是使用weak。(或者为了考虑iOS4的兼容性用unsafe_unretained,具体用法和weak相同,考虑到现在iOS4设备可能已经绝迹了,这里就不讲这个方法了)(关于这个方法的本质我们后面会谈到)
为了保证completionHandler这个Block对myController没有强引用,我们可以定义一个临时的弱引用weakMyViewController来指向原myController的对象,并把这个弱引用传入到Block内,这样就保证了Block对myController持有的是一个弱引用,而不是一个强引用。如此,我们继续修改代码:
MyViewController *myController = [[MyViewController alloc] init…];
MyViewController * __weak weakMyViewController = myC
pletionHandler =& ^(NSInteger result) {
& & [weakMyViewController dismissViewControllerAnimated:YES completion:nil];
这样循环引用的问题就解决了,但是却不幸地引入了一个新的问题:由于传入completionHandler的是一个弱引用,那么当myController指向的对象在completionHandler被调用前释放,那么completionHandler就不能正常的运作了。在一般的单线程环境中,这种问题出现的可能性不大,但是到了多线程环境,就很不好说了,所以我们需要继续完善这个方法。
为了保证在Block内能够访问到正确的myController,我们在block内新定义一个强引用strongMyController来指向weakMyController指向的对象,这样多了一个强引用,就能保证这个myController对象不会在completionHandler被调用前释放掉了。于是,我们对代码再次做出修改:
MyViewController *myController = [[MyViewController alloc] init…];
MyViewController * __weak weakMyController = myC
pletionHandler =& ^(NSInteger result) {
& & MyViewController *strongMyController = weakMyC
  if (strongMyController) {
& & & & // ...
& & & & [strongMyController dismissViewControllerAnimated:YES completion:nil];
& & & & // ...
& & else {
& & & & // Probably nothing...
到此,一个完善的解决方案就完成了:)
为了更清楚地说明问题,这里用一个简单的程序举例。比如我们有如下程序:
#include &stdio.h&
int main()
& & int b = 10;
& & int *a = &b;
& & void (^blockFunc)() = ^(){
& & & & int *c =
& & blockFunc();
& & return 1;
程序中,同为int型的指针,a是被Block捕获的变量,而c是在Block内定义的变量。我们用clang -rewrite-objc处理后,可以看到如下代码:
原main函数:
int main()
& & int b = 10;
& & int *a = &b;
& & void (*blockFunc)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a);
& & ((void (*)(__block_impl *))((__block_impl *)blockFunc)-&FuncPtr)((__block_impl *)blockFunc);
& & return 1;
Block的结构:
struct __main_block_impl_0 {
& struct __block_
& struct __main_block_desc_0* D
& int *a; // 被捕获的引用 a 出现在了block的结构体里面
& __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_a, int flags=0) : a(_a) {
& & impl.isa = &_NSConcreteStackB
& & impl.Flags =
& & impl.FuncPtr =
& & Desc =
实际执行的函数:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
& int *a = __cself-&a; // bound by copy
& & & & int *c = // 在block中声明的引用 c 在函数中声明,存在于函数栈上
我们可以清楚得看到,a和c存在的位置完全不同,如果Block存在于堆上(在ARC下Block默认在堆上),那么a作为Block结构体的一个成员,也自然会存在于堆上,而c无论如何,永远位于Block内实际执行代码的函数栈内。这也导致了两个变量生命周期的完全不同:c在Block的函数运行完毕,即会被释放,而a呢,只有在Block被从堆上释放的时候才会释放。
回到我们的MyViewController的例子中,同上理,如果我们直接让Block捕获我们的myController引用,那么这个引用会被复制后(引用类型也会被复制)作为Block的成员变量存在于其所在的堆空间中,也就是为Block增加了一个指向myController对象的强引用,这就是造成循环引用的本质原因。对于MyViewController的例子,Block的结构体可以理解是这个样子:(准确的结构体肯定和以下这个有区别,但也肯定是如下这种形式:)
struct __main_block_impl_0 {
& struct __block_
& struct __main_block_desc_0* D
& MyViewController * __strong myC& // 被捕获的强引用myController
& __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_a, int flags=0) : a(_a) {
& & impl.isa = &_NSConcreteStackB
& & impl.Flags =
& & impl.FuncPtr =
& & Desc =
而反观我们给Block传入一个弱引用weakMyController,这时我们Block的结构:
struct __main_block_impl_0 {
& struct __block_
& struct __main_block_desc_0* D
& MyViewController * __weak weakMyC& // 被捕获的弱引用weakMyController
& __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_a, int flags=0) : a(_a) {
& & impl.isa = &_NSConcreteStackB
& & impl.Flags =
& & impl.FuncPtr =
& & Desc =
再看在Block内声明的强引用strongMyController,它虽然是强引用,但存在于函数栈中,在函数执行期间,它一直存在,所以myController对象也一直存在,但是当函数执行完毕,strongMyController即被销毁,于是它对myController对象的强引用也被解除,这时Block对myController对象就不存在强引用关系了!加入了strongMyController的函数大体会是这个样子:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
& MyViewController * __strong strongMyController = __cself-&weakMyC&
& & // ....
综上所述,在ARC下(在MRC下会略有不同),Block捕获的引用和Block内声明的引用无论是存在空间与生命周期都是截然不同的,也正是这种不同,造成了我们对他们使用方式的区别。
以上就解释了之前提到的所有问题,希望大家能看明白:)
好的,最后再提一点,在ARC中,对Block捕获对象的内存管理已经简化了很多,由于没有了retain和release等操作,实际只需要考虑循环引用的问题就行了。比如下面这种,是没有内存泄露的问题的:
TestObject *aObject = [[TestObject alloc] init];
aObject.name = @&hehe&;
self.aBlock = ^(){
& & NSLog(@&aObject's name = %@&,aObject.name);
我们上面提到的解决方案,只是针对Block产生循环引用的问题,而不是说所有的Block捕获引用都要这么处理,一定要注意!
本文已收录于以下专栏:
相关文章推荐
在写一些工程时我们总是要引入一些第三方文件,但是这些文件有些是ARC下的有些是非ARC下的。所以我们要进行转换。
引入三方文件时首先要阅读引入的文件的.h 文件头部信息
如下面的文件:头部文件要求:H...
在IOS中内存管理几乎是每个人必须知道的一个知识点。首先我们总结一下MRC,再通过MRC来认识ARC以及自动释放池
1.1 淘汰的技术
1.2 引用计数(RC)是指alloc自动分配...
接触iOS差不多有一年的时间了,从大四开学后开始自学起步,这一路走过来,都是看着前辈们的博客和苹果官方的开发者文档(虽然我的英文水平一般)。今年(2013)7月份正式从学校毕业了,到现在差不两个月的试...
对于iOS程序员来说,内存管理是入门的必修课。引用计数、自动释放等概念,都是与C语言完全不同的。搞明白这些,代码才有可能不 crash。然而就是这么牛逼的内存管理,着实让我这个从 C 转过来的老程序员...
Xcode5之后,新建iOS工程,默认都是ARC模式,但是有时候我们的项目中需要用到一些第三方框架或者接手一个旧项目,我们拿过来来却发现是非ARC的,这时候我们需要进行ARC和MRC混编。
Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处。对于iOS程序员来说,内存管理是入门...
1.单例模式
1.1 概念相关
(1)单例模式在程序运行过程,一个类只有一个实例(2)使用场合在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)
1.2 ARC实现单例
(1)步骤01 在...
*********************************************************************************
ARC 自动引用计数
MRC手动...
一、* Core Foundation与objective-c Object进行交换 *对于Core Foundation与objective-cObject进行交换时,需要用到的ARC管理机制有:(...
想驾驭一门语言,首先要掌握它的内存管理特性。iOS开发经历了MRC到ARC的过程,下面就记录一下本人对iOS内存管理方面的一些理解。
说到iOS开发,肯定离不开objective-c语言(以下简...
他的最新文章
讲师:王禹华
讲师:宋宝华
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 mrc是什么意思 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信