问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何mrc工程中兼容编译arc文件

发布网友 发布时间:2022-04-28 13:18

我来回答

1个回答

热心网友 时间:2023-10-10 01:22

例子很简单,这是一个查找歌手的应用,包含一个简单的UITableView和一个搜索框,当用户在搜索框搜索时,调用MusicBrainz的API完成名字搜索和匹配。MusicBrainz是一个开放的音乐信息平台,它提供了一个免费的XML网页服务,如果对MusicBrainz比较有兴趣的话,可以到它的官网逛一逛。
Demo的起始例子可以从这里下载,为了照顾新人,在这边进行简单说明。在Xcode中打开下载的例子,应该可以看到如下内容(Xcode和iOS开发熟练者请跳过此段)
AppDelegate.h/m 这是整个app的delegate,没什么特殊的,每个iOS/Mac程序在main函数以后的入口,由此进入app的生命周期。在这里加载了最初的viewController并将其放到Window中展示出来。另外appDelegate还负责处理程序开始退出等系统委托的事件
MainViewController.h/m/xib 这个demo最主要的ViewController,含有一个TableView和一个搜索条。 SoundEffect.h/m 简单的播放声音的类,在MusicBrainz搜索完毕时播放一个音效。 main.m 程序入口,所有c程序都从main函数开始执行
AFHTTPRequestOperation.h/m 这是有名的网络框架AFNetworking的一部分,用来帮助等简单地处理web服务请求。这里只包含了这一个类而没有将全部的AFNetworking包括进来,因为我们只用了这一个类。完整的框架代码可以在github的相关页面上找
SVProgresHUD.h/m/bundle 是一个常用的进度条指示,当搜索的时候出现以提示用户正在搜索请稍后。bundle是资源包,里面包含了几张该类用到的图片,打进bundle包的目的一方面是为了资源容易管理,另一方面也是主要方面时为了不和其他资源发生冲突(Xcode中资源名字是资源的唯一标识,同名字的资源只能出现一次,而放到bundle包里可以避免这个潜在的问题)。SVProgresHUD可以在这里找到
快速过一遍这个应用吧:MainViewController是UIViewController的子类,对应的xib文件定义了对应的UITableView和UISearchBar。TableView中显示searchResult数组中的内容。当用户搜索时,用AFHTTPRequestOperation发一个HTTP请求,当从MusicBrainz得到回应后将结果放入searchResult数组中并用tableView显示,当返回结果是空时在tableView中显示没找到。主要的逻辑都在MainViewController.m中的-searchBarSearchButtonClicked:方法中,生成了用于查询的URL,根据MusicBrainz的需求替换了请求的header,并且完成了返回逻辑,然后在主线程中刷新UI。整个程序还是比较简单的~
MRC到ARC的自动转换
回到正题,我们讨论的是ARC,关于REST API和XML解析的技术细节就暂时先忽略吧..整个程序都是用MRC来进行内存管理的,首先来让我们把这个demo转成ARC吧。基本上转换为ARC意味着把所有的retain,release和autorelease关键字去掉,在之前我们明确几件事情:
* Xcode提供了一个ARC自动转换工具,可以帮助你将源码转为ARC
* 当然你也可以自己动手完成ARC转换
* 同时你也可以指定对于某些你不想转换的代码禁用ARC,这对于很多庞大复杂的还没有转至ARC的第三方库帮助很大,因为不是你写的代码你想动手修改的话代码超级容易mess…
对于我们的demo,为了说明问题,这三种策略我们都将采用,注意这仅仅只是为了展示如何转换。实际操作中不需要这么麻烦,而且今后的绝大部分情况应该是从工程建立开始就是ARC的。

首先,ARC是LLVM3.0编译器的特性,而老的工程特别是Xcode3时代的工程的默认编译器很可能是GCC或者LLVM-GCC,因此第一步就是确认编译器是否正确。在Project设置面板,选择target,在Build Settings中将Compiler for C/C++/Objective-C选为Apple LLVM compiler 3.0或以上。为了确保之后转换的顺利,在这里我个人建议最好把Treat Warnings as Errors和 Run Static Analyzer都打开,确保在改变编译器后代码依旧没有警告或者内存问题(虽然静态分析可能不太能保证这一点,但是聊胜于无)。好了~clean(Shift+Cmd+K)以后Bulid一下试试看,经过修改后的demo工程没有任何警告和错误,这是很好的开始。(对于存在警告的代码,这里是很好的修复的时机..请在转换前确保原来的代码没有内存问题)。

接下来就是完成从MRC到ARC的伟大转换了。还是在Build Settings页面,把Objective-C Automatic Reference Counting改成YES(如果找不到的话请看一看搜索栏前面的小标签是不是调成All了..这个选项在Basic里是不出现的),这样我们的工程就将在所有源代码中启用ARC了。然后…试着编译一下看看,嗯..无数的错误。

这是很正常的,因为ARC里不允许出现retain,release之类的,而MRC的代码这些是肯定会有的东西。我们可以手动一个一个对应地去修复这些错误,但是这很麻烦。Xcode为我们提供了一个自动转换工具,可以帮助重写源代码,简单来说就是去掉多余的语句并且重写一些property关键字。

这个小工具是Edit->Refactor下的Convert to Objective-C ARC,点击后会让我们选择要转换哪几个文件,在这里为了说明除了自动转换外的方法,我们不全部转换,而只是选取其中几个转换(MainViewController.m和AFHTTPRequestOperation.m不做转换)。注意到这个对话框上有个警告标志告诉我们target已经是ARC了,这是由于之前我们在Build Settings里已经设置了启用ARC,其实直接在这里做转换后Xcode会自动帮我们开启ARC。点击检查后,Xcode告诉我们一个不幸的消息,不能转换,需要修复ARC readiness issues..后面还告诉我们要看到所有的所谓的ARC readiness issues,可以到设置的General里把Continue building after errors勾上…What the f**k…好吧~先乖乖听从Xcode的建议”Cmd+,“然后Continue building after errors打勾然后再build。

问题依旧,不过在issue面板里应该可以看到所有出问题的代码了。在我们的例子里,问题出在SoundEffect.m里:
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
if (fileURL != nil)
{
SystemSoundID theSoundID;
OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)fileURL, &theSoundID);
if (error == kAudioServicesNoError)
soundID = theSoundID;
}

这里代码尝试把一个NSURL指针强制转换为一个CFURLRef指针。这里涉及到一些Core Services特别是Core Foundation(CF)的东西,AudioServicesCreateSystemSoundID()函数接受CFURLRef为参数,这是一个CF的概念,但是我们在较高的抽象层级上所建立的是NSURL对象。在Cocoa框架中,有很多顶层对象对底层的抽象,而在使用中我们往往可以不加区别地对这两种对象进行同样的对待,这类对象即为可以”自由桥接”的对象(toll-free bridged)。NSURL和CFURLRef就是一对好基友好例子,在这里其实CFURLRef和NSURL是可以进行替换的。
通常来说为了代码在底层级上的正确,在iOS开发中对基于C的API的调用所传入的参数一般都是CF对象,而Objective-C的API调用都是传入NSObject对象。因此在采用自由桥接来调用C API的时候就需要进行转换。但是在使用ARC编译的时候,因为内存管理的原因,编译器需要知道对这些桥接对象要实行什么样的操作。如果一个NSURL对象替代了CFURLRef,那么在作用区域外,应该由谁来决定内存释放和对象销毁呢?为了解决这个问题,引入了bridge,bridge_transfer和__bridge_retained三个关键字。关于选取哪个关键字做转换,需要由实际的代码行为来决定。如果对于自由桥接机制感兴趣,大家可以自己找找的相关内容,比如适用类型、内部机制和一个简介~之后我也会对这个问题做进一步说明
回到demo,我们现在在上面的代码中加上__bridge进行转换。然后再运行ARC转换工具,这时候检查应该没有其他问题了,那么让我们进行转换吧~当然在真正转换之前会有一个预览界面,在这里我们最好检查一下转换是不是都按照预想进行了..要是出现大面积错误又没有备份或者出现各种意外的话就可以哭了…
前后变化的话比较简单,基本就是去掉不需要的代码和改变property的类型而已,其实有信心的话不太需要每次都看,但是如果是第一次执行ARC转换的操作的话,我还是建议稍微看一下变化,这样能对ARC有个直观上的了解。检查一遍,应该没什么问题了..需要注意的是main.m里关于autoreleasepool的变化以及所有dealloc调用里的[super dealloc]的删除,它们同样是MRC到ARC的主要变化..
好了~转换完成以后我们再build看看..应该会有一些警告。对于原来retain的property,比较保险的做法是转为strong,在LLVM3.0中自动转换是这样做的,但是在3.1中property默认并不是strong,这样在使用property赋值时存在警告,我们在property声明里加上strong就好了~然后就是SVProgressHUD.m里可能存在问题,这是由于原作者把release的代码和其他代码写在一行了.导致自动转换时只删掉了部分,而留下了部分不应该存在的代码,删掉对变量的空调用就好了..
自动转换之后的故事
然后再编译,没有任何错误和警告了,好棒~等等…我们刚才没有对MainViewController和AFHTTPRequestOperation进行处理吧,那么这两个文件里应该还存在release之类的东西吧..?看一看这两个文件,果然有各种release,但是为什么能编译通过呢?!明明刚才在自动转换前他们还有N多错的嘛…答案很简单,在自动转换的时候因为我们没有勾选这两个文件,因此编译器在自动转换过后为这两个文件标记了”不使用ARC编译”。可以看到在target的Building Phases下,MainViewController.m和AFHTTPRequestOperation.m两个文件后面被加上了-fno-objc-arc的编译标记,被加上该标记的文件将不使用ARC规则进行编译。

提供这样的编译标记的原因是显而易见的,因为总是有一部分的第三方代码并没有转换为ARC(可能是由于维护者犯懒或者已经终止维护),所以对于这部分代码,为了迅速完成转换,最好是使用-fno-objc-arc标记来禁止在这些源码上使用ARC。
为了方便查找,再此列出一些在转换时可能出现的问题,当然在我们使用ARC时也需要注意避免代码中出现这些问题:
“Cast … requires a bridged cast”
这是我们在demo中遇到的问题,不再赘述
Receiver type ‘X’ for instance message is a forward declaration
这往往是引用的问题。ARC要求完整的前向引用,也就是说在MRC时代可能只需要在.h中申明@class就可以,但是在ARC中如果调用某个子类中未覆盖的父类中的方法的话,必须对父类.h引用,否则无法编译。
Switch case is in protected scope
现在switch语句必须加上{}了,ARC需要知道局部变量的作用域,加上{}后switch语法更加严格,否则遇到没有break的分支的话内存管理会出现问题。
A name is referenced outside the NSAutoreleasePool scope that it was declared in
这是由于写了自己的autoreleasepool,而在转换时在原来的pool中申明的变量在新的@autoreleasepool中作用域将被局限。解决方法是把变量申明拿到pool的申请之前。
ARC forbids Objective-C objects in structs or unions
可以说ARC所引入的最严格的*是不能在C结构体中放OC对象了..因此类似下面这样的代码是不可用的
typedef struct {
UIImage *selectedImage;
UIImage *disabledImage;
} ButtonImages;

这个问题只有乖乖想办法了..改变原来的结构什么的..

手动转换
刚才做了对demo的大部分转换,还剩下了MainViewController和AFHTTPRequestOperation是MRC。但是由于使用了-fno-objc-arc,因此现在编译和运行都没有问题了。下面我们看看如何手动把MainViewController转为ARC,这也有助于进一步理解ARC的规则。
首先,我们需要转变一下观念…对于MainViewController.h,在.h中申明了两个实例变量:
@interface MainViewController : UIViewController
{
NSOperationQueue *queue;
NSMutableString *currentStringValue;
}

我们不妨仔细考虑一下,为什么在interface里出现了实例变量的申明?通常来说,实例变量只是在类的实例中被使用,而你所写的类的使用者并没有太多必要了解你的类中有哪些实例变量。而对于绝大部分的实例变量,应该都是protected或者private的,对它们的操作只应该用setter和getter,而这正是property所要做的工作。可以说,将实例变量写在头文件中是一种遗留的陋习。更好的写实例变量名字的地方应当与类实现关系更为密切,为了隐藏细节,我们应该考虑将它们写在@implementation里。好消息是,在LLVM3.0中,不论是否开启ARC,编译器是支持将实例变量写到实现文件中的。甚至如果没有特殊需要又用了property,我们都不应该写无意义的实例变量申明,因为在@synthesize中进行绑定时,我们就可以设置变量名字了,这样写的话可以让代码更加简洁。
在这里我们对着两个实例变量不需要property(外部成员不应当能访问到它们),因此我们把申明移到.m里中。修改后的.h是这样的,十分简洁一看就懂~
#import
@interface MainViewController : UIViewController
@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
@end

然后.m的开头变成这样:
@implementation MainViewController
{
NSOperationQueue *queue;
NSMutableString *currentStringValue;
}

这样的写法让代码相当灵活,而且不得不承认.m确实是这些实例变量的应该在的地方…build一下,没问题..当然对于SoundEffect类也可以做相似的操作,这会让使用你的类的人很开心,因为.h越简单越好..P.S.另外一个好处可以减少.h里的引用,减少编译时间(虽然不明显=。=)
然后就可以在MainViewController里启用ARC了,方法很简单,删掉Build Phases里相关文件的-fno-objc-arc标记就可以了~然后..然后当然是一大堆错误啦。我们来手动一个个改吧,虽然谈不上乐趣,但是成功以后也会很有成就~(如果你不幸在启用ARC后build还是成功了,恭喜你遇到了Xcode的bug,请Cmd+Q然后重新打开Xcode把=_=)
dealloc
红色最密集的地方是dealloc,因为每一行都是release。由于在这里dealloc并没有做除了release和super dealloc之外的任何事情,因此简单地把整个方法删掉就好了。当然,在对象被销毁时,dealloc还是会被调用的,因此我们在需要对非ARC管理的内存进行管理和必要的逻辑操作的时候,还是应该保留dealloc的,当然这涉及到CF以及以下层的东西:比如对于retain的CF对象要CFRelease(),对于malloc()到堆上的东西要free()掉,对于添加的observer可以在这里remove,schele的timer在这里invalidate等等~[super dealloc]这个消息也不再需要发了,ARC会自动帮你搞定。
另外,在MRC时代一个常做的事情是在dealloc里把指向自己的delegate设成nil(否则就等着EXC_BAD_ACCESS吧 ),而现在一般delegate都是weak的,因此在self被销毁后这个指针自动被置成nil了,你不用再为之担心,好棒啊..
去掉各种release和autorelease
这个很直接,没有任何问题。去掉就行了~不再多说
讨论一下Property
在MainViewController.m里的类扩展中定义了两个property:
@interface MainViewController ()
@property (nonatomic, retain) NSMutableArray *searchResults;
@property (nonatomic, retain) SoundEffect *soundEffect;
@end

申明的类型是retain,关于retain,assign和copy的讨论已经烂大街了,在此不再讨论。在MRC的年代使用property可以帮助我们使用dot notation的时候简化对象的retain和copy,而在ARC时代,这就显得比较多余了。在我看来,使用property和点方法来调用setter和getter是不必要的。property只在将需要的数据在.h中暴露给其他类时才需要,而在本类中,只需要用实例变量就可以。因此我们可以移去searchResults和soundEffect的@property和@synthesize,并将起移到实例变量申明中:
@implementation MainViewController
{
NSOperationQueue *queue;
NSMutableString *currentStringValue;
NSMutableArray *searchResults;
SoundEffect *soundEffect;
}

相应地,我们需要将对应的self.searchResult和self.soundEffect的self.都去去掉。在这里需要注意的是,虽然我们去掉了soundEffect的property和synthesize,但是我们依然有一个lazy loading的方法- (SoundEffect *)soundEffect,神奇之处在于(可能你以前也不知道),点方法并不需要@property关键字的支持,虽然大部分时间是这么用的..(property只是对setter或者getter的申明,而点方法是对其的调用,在这个例子的实现中我们事实上实现了-soundEffect这个getter方法,所以点方法在等号右边的getter调用是没有问题的)。为了避免误解,建议把self.soundEffect的getter调用改写成[self soundEffect]。

热心网友 时间:2023-10-10 01:22

例子很简单,这是一个查找歌手的应用,包含一个简单的UITableView和一个搜索框,当用户在搜索框搜索时,调用MusicBrainz的API完成名字搜索和匹配。MusicBrainz是一个开放的音乐信息平台,它提供了一个免费的XML网页服务,如果对MusicBrainz比较有兴趣的话,可以到它的官网逛一逛。
Demo的起始例子可以从这里下载,为了照顾新人,在这边进行简单说明。在Xcode中打开下载的例子,应该可以看到如下内容(Xcode和iOS开发熟练者请跳过此段)
AppDelegate.h/m 这是整个app的delegate,没什么特殊的,每个iOS/Mac程序在main函数以后的入口,由此进入app的生命周期。在这里加载了最初的viewController并将其放到Window中展示出来。另外appDelegate还负责处理程序开始退出等系统委托的事件
MainViewController.h/m/xib 这个demo最主要的ViewController,含有一个TableView和一个搜索条。 SoundEffect.h/m 简单的播放声音的类,在MusicBrainz搜索完毕时播放一个音效。 main.m 程序入口,所有c程序都从main函数开始执行
AFHTTPRequestOperation.h/m 这是有名的网络框架AFNetworking的一部分,用来帮助等简单地处理web服务请求。这里只包含了这一个类而没有将全部的AFNetworking包括进来,因为我们只用了这一个类。完整的框架代码可以在github的相关页面上找
SVProgresHUD.h/m/bundle 是一个常用的进度条指示,当搜索的时候出现以提示用户正在搜索请稍后。bundle是资源包,里面包含了几张该类用到的图片,打进bundle包的目的一方面是为了资源容易管理,另一方面也是主要方面时为了不和其他资源发生冲突(Xcode中资源名字是资源的唯一标识,同名字的资源只能出现一次,而放到bundle包里可以避免这个潜在的问题)。SVProgresHUD可以在这里找到
快速过一遍这个应用吧:MainViewController是UIViewController的子类,对应的xib文件定义了对应的UITableView和UISearchBar。TableView中显示searchResult数组中的内容。当用户搜索时,用AFHTTPRequestOperation发一个HTTP请求,当从MusicBrainz得到回应后将结果放入searchResult数组中并用tableView显示,当返回结果是空时在tableView中显示没找到。主要的逻辑都在MainViewController.m中的-searchBarSearchButtonClicked:方法中,生成了用于查询的URL,根据MusicBrainz的需求替换了请求的header,并且完成了返回逻辑,然后在主线程中刷新UI。整个程序还是比较简单的~
MRC到ARC的自动转换
回到正题,我们讨论的是ARC,关于REST API和XML解析的技术细节就暂时先忽略吧..整个程序都是用MRC来进行内存管理的,首先来让我们把这个demo转成ARC吧。基本上转换为ARC意味着把所有的retain,release和autorelease关键字去掉,在之前我们明确几件事情:
* Xcode提供了一个ARC自动转换工具,可以帮助你将源码转为ARC
* 当然你也可以自己动手完成ARC转换
* 同时你也可以指定对于某些你不想转换的代码禁用ARC,这对于很多庞大复杂的还没有转至ARC的第三方库帮助很大,因为不是你写的代码你想动手修改的话代码超级容易mess…
对于我们的demo,为了说明问题,这三种策略我们都将采用,注意这仅仅只是为了展示如何转换。实际操作中不需要这么麻烦,而且今后的绝大部分情况应该是从工程建立开始就是ARC的。

首先,ARC是LLVM3.0编译器的特性,而老的工程特别是Xcode3时代的工程的默认编译器很可能是GCC或者LLVM-GCC,因此第一步就是确认编译器是否正确。在Project设置面板,选择target,在Build Settings中将Compiler for C/C++/Objective-C选为Apple LLVM compiler 3.0或以上。为了确保之后转换的顺利,在这里我个人建议最好把Treat Warnings as Errors和 Run Static Analyzer都打开,确保在改变编译器后代码依旧没有警告或者内存问题(虽然静态分析可能不太能保证这一点,但是聊胜于无)。好了~clean(Shift+Cmd+K)以后Bulid一下试试看,经过修改后的demo工程没有任何警告和错误,这是很好的开始。(对于存在警告的代码,这里是很好的修复的时机..请在转换前确保原来的代码没有内存问题)。

接下来就是完成从MRC到ARC的伟大转换了。还是在Build Settings页面,把Objective-C Automatic Reference Counting改成YES(如果找不到的话请看一看搜索栏前面的小标签是不是调成All了..这个选项在Basic里是不出现的),这样我们的工程就将在所有源代码中启用ARC了。然后…试着编译一下看看,嗯..无数的错误。

这是很正常的,因为ARC里不允许出现retain,release之类的,而MRC的代码这些是肯定会有的东西。我们可以手动一个一个对应地去修复这些错误,但是这很麻烦。Xcode为我们提供了一个自动转换工具,可以帮助重写源代码,简单来说就是去掉多余的语句并且重写一些property关键字。

这个小工具是Edit->Refactor下的Convert to Objective-C ARC,点击后会让我们选择要转换哪几个文件,在这里为了说明除了自动转换外的方法,我们不全部转换,而只是选取其中几个转换(MainViewController.m和AFHTTPRequestOperation.m不做转换)。注意到这个对话框上有个警告标志告诉我们target已经是ARC了,这是由于之前我们在Build Settings里已经设置了启用ARC,其实直接在这里做转换后Xcode会自动帮我们开启ARC。点击检查后,Xcode告诉我们一个不幸的消息,不能转换,需要修复ARC readiness issues..后面还告诉我们要看到所有的所谓的ARC readiness issues,可以到设置的General里把Continue building after errors勾上…What the f**k…好吧~先乖乖听从Xcode的建议”Cmd+,“然后Continue building after errors打勾然后再build。

问题依旧,不过在issue面板里应该可以看到所有出问题的代码了。在我们的例子里,问题出在SoundEffect.m里:
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
if (fileURL != nil)
{
SystemSoundID theSoundID;
OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)fileURL, &theSoundID);
if (error == kAudioServicesNoError)
soundID = theSoundID;
}

这里代码尝试把一个NSURL指针强制转换为一个CFURLRef指针。这里涉及到一些Core Services特别是Core Foundation(CF)的东西,AudioServicesCreateSystemSoundID()函数接受CFURLRef为参数,这是一个CF的概念,但是我们在较高的抽象层级上所建立的是NSURL对象。在Cocoa框架中,有很多顶层对象对底层的抽象,而在使用中我们往往可以不加区别地对这两种对象进行同样的对待,这类对象即为可以”自由桥接”的对象(toll-free bridged)。NSURL和CFURLRef就是一对好基友好例子,在这里其实CFURLRef和NSURL是可以进行替换的。
通常来说为了代码在底层级上的正确,在iOS开发中对基于C的API的调用所传入的参数一般都是CF对象,而Objective-C的API调用都是传入NSObject对象。因此在采用自由桥接来调用C API的时候就需要进行转换。但是在使用ARC编译的时候,因为内存管理的原因,编译器需要知道对这些桥接对象要实行什么样的操作。如果一个NSURL对象替代了CFURLRef,那么在作用区域外,应该由谁来决定内存释放和对象销毁呢?为了解决这个问题,引入了bridge,bridge_transfer和__bridge_retained三个关键字。关于选取哪个关键字做转换,需要由实际的代码行为来决定。如果对于自由桥接机制感兴趣,大家可以自己找找的相关内容,比如适用类型、内部机制和一个简介~之后我也会对这个问题做进一步说明
回到demo,我们现在在上面的代码中加上__bridge进行转换。然后再运行ARC转换工具,这时候检查应该没有其他问题了,那么让我们进行转换吧~当然在真正转换之前会有一个预览界面,在这里我们最好检查一下转换是不是都按照预想进行了..要是出现大面积错误又没有备份或者出现各种意外的话就可以哭了…
前后变化的话比较简单,基本就是去掉不需要的代码和改变property的类型而已,其实有信心的话不太需要每次都看,但是如果是第一次执行ARC转换的操作的话,我还是建议稍微看一下变化,这样能对ARC有个直观上的了解。检查一遍,应该没什么问题了..需要注意的是main.m里关于autoreleasepool的变化以及所有dealloc调用里的[super dealloc]的删除,它们同样是MRC到ARC的主要变化..
好了~转换完成以后我们再build看看..应该会有一些警告。对于原来retain的property,比较保险的做法是转为strong,在LLVM3.0中自动转换是这样做的,但是在3.1中property默认并不是strong,这样在使用property赋值时存在警告,我们在property声明里加上strong就好了~然后就是SVProgressHUD.m里可能存在问题,这是由于原作者把release的代码和其他代码写在一行了.导致自动转换时只删掉了部分,而留下了部分不应该存在的代码,删掉对变量的空调用就好了..
自动转换之后的故事
然后再编译,没有任何错误和警告了,好棒~等等…我们刚才没有对MainViewController和AFHTTPRequestOperation进行处理吧,那么这两个文件里应该还存在release之类的东西吧..?看一看这两个文件,果然有各种release,但是为什么能编译通过呢?!明明刚才在自动转换前他们还有N多错的嘛…答案很简单,在自动转换的时候因为我们没有勾选这两个文件,因此编译器在自动转换过后为这两个文件标记了”不使用ARC编译”。可以看到在target的Building Phases下,MainViewController.m和AFHTTPRequestOperation.m两个文件后面被加上了-fno-objc-arc的编译标记,被加上该标记的文件将不使用ARC规则进行编译。

提供这样的编译标记的原因是显而易见的,因为总是有一部分的第三方代码并没有转换为ARC(可能是由于维护者犯懒或者已经终止维护),所以对于这部分代码,为了迅速完成转换,最好是使用-fno-objc-arc标记来禁止在这些源码上使用ARC。
为了方便查找,再此列出一些在转换时可能出现的问题,当然在我们使用ARC时也需要注意避免代码中出现这些问题:
“Cast … requires a bridged cast”
这是我们在demo中遇到的问题,不再赘述
Receiver type ‘X’ for instance message is a forward declaration
这往往是引用的问题。ARC要求完整的前向引用,也就是说在MRC时代可能只需要在.h中申明@class就可以,但是在ARC中如果调用某个子类中未覆盖的父类中的方法的话,必须对父类.h引用,否则无法编译。
Switch case is in protected scope
现在switch语句必须加上{}了,ARC需要知道局部变量的作用域,加上{}后switch语法更加严格,否则遇到没有break的分支的话内存管理会出现问题。
A name is referenced outside the NSAutoreleasePool scope that it was declared in
这是由于写了自己的autoreleasepool,而在转换时在原来的pool中申明的变量在新的@autoreleasepool中作用域将被局限。解决方法是把变量申明拿到pool的申请之前。
ARC forbids Objective-C objects in structs or unions
可以说ARC所引入的最严格的*是不能在C结构体中放OC对象了..因此类似下面这样的代码是不可用的
typedef struct {
UIImage *selectedImage;
UIImage *disabledImage;
} ButtonImages;

这个问题只有乖乖想办法了..改变原来的结构什么的..

手动转换
刚才做了对demo的大部分转换,还剩下了MainViewController和AFHTTPRequestOperation是MRC。但是由于使用了-fno-objc-arc,因此现在编译和运行都没有问题了。下面我们看看如何手动把MainViewController转为ARC,这也有助于进一步理解ARC的规则。
首先,我们需要转变一下观念…对于MainViewController.h,在.h中申明了两个实例变量:
@interface MainViewController : UIViewController
{
NSOperationQueue *queue;
NSMutableString *currentStringValue;
}

我们不妨仔细考虑一下,为什么在interface里出现了实例变量的申明?通常来说,实例变量只是在类的实例中被使用,而你所写的类的使用者并没有太多必要了解你的类中有哪些实例变量。而对于绝大部分的实例变量,应该都是protected或者private的,对它们的操作只应该用setter和getter,而这正是property所要做的工作。可以说,将实例变量写在头文件中是一种遗留的陋习。更好的写实例变量名字的地方应当与类实现关系更为密切,为了隐藏细节,我们应该考虑将它们写在@implementation里。好消息是,在LLVM3.0中,不论是否开启ARC,编译器是支持将实例变量写到实现文件中的。甚至如果没有特殊需要又用了property,我们都不应该写无意义的实例变量申明,因为在@synthesize中进行绑定时,我们就可以设置变量名字了,这样写的话可以让代码更加简洁。
在这里我们对着两个实例变量不需要property(外部成员不应当能访问到它们),因此我们把申明移到.m里中。修改后的.h是这样的,十分简洁一看就懂~
#import
@interface MainViewController : UIViewController
@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
@end

然后.m的开头变成这样:
@implementation MainViewController
{
NSOperationQueue *queue;
NSMutableString *currentStringValue;
}

这样的写法让代码相当灵活,而且不得不承认.m确实是这些实例变量的应该在的地方…build一下,没问题..当然对于SoundEffect类也可以做相似的操作,这会让使用你的类的人很开心,因为.h越简单越好..P.S.另外一个好处可以减少.h里的引用,减少编译时间(虽然不明显=。=)
然后就可以在MainViewController里启用ARC了,方法很简单,删掉Build Phases里相关文件的-fno-objc-arc标记就可以了~然后..然后当然是一大堆错误啦。我们来手动一个个改吧,虽然谈不上乐趣,但是成功以后也会很有成就~(如果你不幸在启用ARC后build还是成功了,恭喜你遇到了Xcode的bug,请Cmd+Q然后重新打开Xcode把=_=)
dealloc
红色最密集的地方是dealloc,因为每一行都是release。由于在这里dealloc并没有做除了release和super dealloc之外的任何事情,因此简单地把整个方法删掉就好了。当然,在对象被销毁时,dealloc还是会被调用的,因此我们在需要对非ARC管理的内存进行管理和必要的逻辑操作的时候,还是应该保留dealloc的,当然这涉及到CF以及以下层的东西:比如对于retain的CF对象要CFRelease(),对于malloc()到堆上的东西要free()掉,对于添加的observer可以在这里remove,schele的timer在这里invalidate等等~[super dealloc]这个消息也不再需要发了,ARC会自动帮你搞定。
另外,在MRC时代一个常做的事情是在dealloc里把指向自己的delegate设成nil(否则就等着EXC_BAD_ACCESS吧 ),而现在一般delegate都是weak的,因此在self被销毁后这个指针自动被置成nil了,你不用再为之担心,好棒啊..
去掉各种release和autorelease
这个很直接,没有任何问题。去掉就行了~不再多说
讨论一下Property
在MainViewController.m里的类扩展中定义了两个property:
@interface MainViewController ()
@property (nonatomic, retain) NSMutableArray *searchResults;
@property (nonatomic, retain) SoundEffect *soundEffect;
@end

申明的类型是retain,关于retain,assign和copy的讨论已经烂大街了,在此不再讨论。在MRC的年代使用property可以帮助我们使用dot notation的时候简化对象的retain和copy,而在ARC时代,这就显得比较多余了。在我看来,使用property和点方法来调用setter和getter是不必要的。property只在将需要的数据在.h中暴露给其他类时才需要,而在本类中,只需要用实例变量就可以。因此我们可以移去searchResults和soundEffect的@property和@synthesize,并将起移到实例变量申明中:
@implementation MainViewController
{
NSOperationQueue *queue;
NSMutableString *currentStringValue;
NSMutableArray *searchResults;
SoundEffect *soundEffect;
}

相应地,我们需要将对应的self.searchResult和self.soundEffect的self.都去去掉。在这里需要注意的是,虽然我们去掉了soundEffect的property和synthesize,但是我们依然有一个lazy loading的方法- (SoundEffect *)soundEffect,神奇之处在于(可能你以前也不知道),点方法并不需要@property关键字的支持,虽然大部分时间是这么用的..(property只是对setter或者getter的申明,而点方法是对其的调用,在这个例子的实现中我们事实上实现了-soundEffect这个getter方法,所以点方法在等号右边的getter调用是没有问题的)。为了避免误解,建议把self.soundEffect的getter调用改写成[self soundEffect]。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
小鹿的过冬方式是什么 我弟弟生气,拍了下电脑桌,结果再开机就说电源按钮关闭,显示器休眠 为什么进入屏保后几分钟显示器又亮了起来 我的手碰电脑桌显示器经常闪一下 下一站江湖所有隐藏功法 最新隐藏功法级别 《下一站江湖》玄龟软甲获得方法介绍_《下一站江湖》玄龟软甲获得方法是... 如何选购前锋热水器 近年美国校园电影,青春喜剧 最好是08-11年的,新的。不要悲剧的。 推荐几部美国校园喜剧电影,谢谢! 美国 八九十年代 的 电视剧 电影 讲美国 八十年代的也可以 内容要有摇... 从广州火车站坐地铁二号线到广州火车南站要多久? 如何快速截取多行文字(例如一段文本的文字) iOS AVAudioPlayer播放音频时,已经将音频下载了本地,怎么还出现下面的错误啊 广州火车站坐地铁到广州南站要多久啊 苹果手机发信息给小天才手表出现Osstatus1685348671是怎么回事? 为什么我的苹果电脑装的pptv for mac安装好不能用,电脑进去显示:未能完成该操作。(OSStatus 错误 -43。 小天才电话手表语音显示osstatus错误 小天才osstatus错误怎么解决 重庆有哪些比较热闹的商业街,除了观音桥,南坪,沙坪坝, 南坪商圈的介绍 重庆市主城区有几大商圈? 重庆主城区有几个商圈,是哪几个? 跪求经典二战海战电影大全,【在线观看】免费百度云资源 电影中途岛之战中马特死了吗 中途岛战役高清完整版下载 重庆市南坪附近哪里买衣服和鞋子(20岁左右的)最便宜又最好看? 中途岛海战电影国语百度云 重庆南坪共有几家大型超市 《决战中途岛》拍得太糟糕,军事武器错漏百出,为何不能算历史正剧呢? 美国二战优秀电影 广州南至广州火车站地铁要多久时间 从广州火车站坐地铁到广州南站要多久 Unable to build the NVIDIA kernel module.ubuntu13.10安装Nvidia显卡驱动报错,求助 广州到广州南要多久 gpuimage一直报错,什么情况 苹果恢复的时候出现 未知错误47 从广州站到广州南站,坐地铁要多长时间? 广州火车站坐地铁到广州南站需要多长时间,中间需要多少时间换乘 数学资料上"丨"是什么意思 为什么我mac制作mac系统盘的时候最后显示未能验证来源资源忙 丨一丨是什么天气符号 使用英语类APP做听力测试,显示“OSStatus 错误 1685348671”,无法听到声音。 高中数学符号“丨”是什么意思? “丨”符号的拼音是什么? 数学符号 “丨”是什么意思? 丨符号什么拼音 数学中的|符号是什么意思? 丨这个符号怎么用? 数学符号 丨 是什么意思? 赣州的房价现在怎么样?