First, preface
Project Introduction
Number of objective-c files to convert: 1000 or so.
Development tools: Xcode 8.0.1
Conversion Mode
I am using the Arc conversion feature provided by Xcode itself. Of course you can also manually convert manually, that is not part of this article, and its workload will definitely make you crash.
Second, the conversion process
Code Backup
Before making such a large-scale change, be sure to make a code backup: Copy the code directly locally, or remember the version number on the VCS before changing the code.
Filtering files that do not need to be converted
Identify the third-party libraries referenced in your project that still use manual memory management, or some files that you do not want to convert, add-fno-objc-arc tags to them.
The Xcode auto-conversion tool is only for OBJECTIVE-C objects and only handles objective-c/objective-c++, which is the two files with the suffix. m/.mm, so that the other C + + + corresponding. C/.cpp don't bother.
Perform a check operation
Using the Xcode transform tool Portal:
Clicking Convert to Objective-c arc will enter the check operation entrance,
This step selects which files need to be converted, and Xcode will automatically help you identify which files need to be converted, which can be selected entirely.
When you click the Check button, Xcode helps us to check for errors or warnings in the code that do not conform to the ARC usage rules, and only after all the errors are resolved can you perform a true conversion operation.
Resolve Errors/Alarms
After performing the check operation, a prompt is given:
More than 300 errors, as well as more than 1200 warning messages, are going to cry ...
Errors and warnings are resolved in more detail and are described separately later.
Perform a conversion operation
After all the error is resolved, the following prompt interface will pop up:
The idea is that Xcode will convert your project to use ARC to manage the memory, and all the changed code will be shown in a review interface before it is actually changed. At the same time, when all the changes are complete, Xcode will speak the project target corresponding to the engineering settings using ARC settings (objective-c Automatic Reference counting) will be set to Yes (the warning sign in the upper right corner is telling us that the project already supports arc, but there are files in the project that are not supported):
This is not far from success, victory in sight!
Click the Next button to jump to the review interface, which is similar to using Xcode to submit an SVN confirmation submission interface, as shown in:
This interface lists all the files that require code changes, while being able to directly compare changes in code before and after conversion. To be on the safe side, I chose to take a glance at every file, which gave us a chance to check if we missed a file that can't be converted. Make sure everything is correct, click the Save button in the bottom right corner and everything is done!
Error/Warning resolution
Error
ARC forbids synthesizing a property of an Objective-c object with unspecified ownership or storage attribute
The property attribute must specify a memory management keyword and add the strong keyword to the attribute definition.
ARC forbids explicit message send of ' release '
This is usually done by using a macro definition that contains release, and deleting the macro and the place where the macro is used.
Init methods must return a type related to the receiver type
The reason for the error is that a method in Class A starts with Init and returns a type B, OK, just change the method name.
Cast of C pointer type ' Ivpointer ' (aka ' void ') to objective-c pointer type ' Iflyttsmanager_old ' requires a bridged cast
Cast_pointer_objective-c
This is the toll-free bridging conversion problem, under the arc under the situation to use the corresponding conversion keywords on the line, the article will be specifically described.
Warning
The goal of resolving the warning is to eliminate the danger of code in the warning, since Xcode gives the hint, then every warning message deserves our serious treatment.
Capturing self in this block are likely to leads to a retain cycle
This is a typical block circular reference problem, changing the self in block to use the weak pointer to self.
Using ' Initwitharray: ' With a literal is redundant
Well, it turns out to be unnecessary. Alloc operation, just press the Xcode prompt to delete the alloc:
Init methods must return a type related to the receiver type
It turns out that a method in Class A starts with Init and returns a type B, OK, just change the method name.
Property follows Cocoa naming convention for returning ' owned ' objects
This is because the name of the @property property begins with new, hateful ... The method of modification is to change the corresponding getter method to a name other than the new start:
Arc under the method name if it is new/alloc/init and so on, and is not the initialization method of the class, it should be careful, either error, or warning, the reason you understand.
Block implicitly retains ' self '; Explicitly mention ' self ' to indicate the is intended behavior
This means that the instance variable _selectedmodemarkerview of self is used in block, so the block implicitly retain self. Xcode thinks this may be confusing to the developer, or therefore inherited a circular reference, so warn us to show the use of self in block to achieve block display retain self.
There are two ways to change this warning: ① follow the Xcode prompts and change to Self->_selectedmodemarkerview:
② Direct this warning off warning name: implicit retain of ' self ' within blocks the corresponding clang keyword is:-wimplicit-retain-self
Weak unpredictably set to nil and Weak property ' delegate ' are accessed multiple times in this method Be unpredictably set to nil; Assign to a strong variable to keep the object alive
This is the highest number of warnings in the project, because all delegate properties are weak, and Xcode turns on the two warning settings in the default setting:
Capturing ' self ' strongly in this block are likely to leads to a retain cycle
This is evident in the case of block causing circular reference memory leaks, before the code pits AH! To modify a scenario:
Method parameter of type ' Nserror __autoreleasing ' with no explicit ownership
Needless to say, add the __autoreleasing keyword as prompted in the warning.
The above list of errors and warnings is only a large number, there are many others are not listed here.
In addition, recommend Mattt Thompson God about clang in the name of almost all warning and corresponding error prompt website: http://fuckingclangwarnings.com/, solve warning class problem in the future is much simpler!
Xcode Auto Convert
Keyword Conversion
Xcode automatically converts certain keywords to the corresponding version of Arc.
Retain automatically turns into strong,
Assign keyword turns into weak
The Assign keyword that modifies the Objective-c object or the ID type object is turned into weak,
But the assign of a numeric variable such as a modified Int/bool is not automatically converted to weak,
Keyword deletion
and manual memory management related to several keywords, such as: Release/retain/autorelease/super Dealloc, etc. will be deleted;
If, in addition to the Release/super dealloc statement, the Dealloc method is preserved if other code is dealloc in the method,
If no entire method is removed:
Keyword substitution
The Block keyword is automatically replaced with weak when converting:
@autoreleasepool
NSAutoreleasePool does not support arc and will be replaced with @autoreleasepool:
About being macro commented code
Releasing code using an object defined by a macro
The macro definition is as follows:
12 |
#define RELEASE_SAFELY(__POINTER) { \ [(__POINTER) release]; (__POINTER) = nil; } |
When you perform an arc conversion check operation, Xcode will use this macro to error:
Delete the macro and the place where you used the macro.
The code that is commented out by the macro is not processed by Xcode at the time of conversion.
PS: This is quite a bit of a pit, because you simply can't anticipate how many macros are being used in the project, and how much code has been commented out. When you're done with the conversion, you think it's time to do it, but one day, because of a macro opening, you encounter a bunch of new arcs that aren't completely complete. This kind of problem also did not recruit, can only meet one to change one.
ARC and Block
Memory leaks, whether manual memory management or Arc,block circular references, are a daunting issue. In MRC, resolving block circular references only requires the use of the __block keyword, which is slightly more complex to solve with block using arc:
__block keywords
Modify external definition variables within block
As with manual memory management, ARC if you need to modify variables defined outside of block in block, you need to use the __block keyword modifier, such as:
1234 |
__block NSString *name = @ "foggry" ; self.expireCostLabel.completionBlock = ^(){ name = @ "wangzz" ; }; |
The name variable in the previous example needs to be modified in the block, so you must use the __block keyword.
The difference between __block in MRC and ARC
When you use the __block keyword-decorated object in a block under Arc, the block retain the object, but the MRC does not retain. This is described in detail in the official document transitioning to ARC Release notes:
In manual reference counting mode, block ID x; Has the effect of retaining X. In ARC mode, block ID x; defaults to retaining X (just as all other values).
The following code has a memory leak in both the MRC and Arc Mycontroller objects:
12345 |
MyViewController *myController = [[MyViewController alloc] init…]; // ... myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; }; |
Memory leak issues can be changed in MRC as follows:
12345 |
MyViewController * __block myController = [[MyViewController alloc] init…]; // ... myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; }; |
In arc, however, this is not a change. As we said at the beginning, the Mycontroller.completionhandler block in Arc will retainmycontroller the object, making the memory leak problem still exists!!
There are two solutions to this problem in arc, the first of which:
123456 |
MyViewController * __block myController = [[MyViewController alloc] init…]; // ... myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; myController = nil; }; |
When the method is finished using Mycontroller in the block, it points to nil. When a pointer with no strong type points to an object pointed to by Mycontroller, the object is freed.
The second solution is to use the weak instead of the BLOCK keyword directly:
123456 |
MyViewController *myController = [[MyViewController alloc] init…]; // ... MyViewController * __weak weakMyViewController = myController; myController.completionHandler = ^(NSInteger result) { [weakMyViewController dismissViewControllerAnimated:YES completion:nil]; }; |
This method avoids the retain of block to Mycontroller object directly.
There is a circular reference relationship
If self has a strong reference to the block, either directly or indirectly, the Self keyword is used in the block, and the self and the Block have a circular reference relationship. At this point, you must define a pointer to self using the __weak keyword, and use that pointer in the block to refer to self:
1234 |
MessageListController * __weak weakSelf = self; self.messageLogic.loadMoreBlock = ^(IcarMessage * theMessage) { [weakSelf.tableView setPullTableIsLoadingMore:YES]; }; |
It is important to note that although the weakself in the example above refers to a weak reference to self, the self has a strong reference to the block, and the self's life cycle must be longer than the block, so you don't have to worry about using the weakself pointer in the block. The self that it points to will be freed.
There is no circular reference relationship
The following example:
1234567891011121314 |
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyController = myController;
myController.completionHandler = ^(NSInteger result) {
MyViewController *strongMyController = weakMyController;
if
(strongMyController) {
// ...
[strongMyController dismissViewControllerAnimated:YES completion:nil];
// ...
}
else
{
// Probably nothing...
}
};
|
As stated earlier, The Mycontroller.completionhandler block does not use the Mycontroller object directly, causing a memory leak, so you need to first point to the Mycontroller object with a weak pointer, and then use the weak pointer in the block. But to ensure that the Mycontroller object was not released when the block was executed, At the beginning of the block, a temporary pointer to the strong type is defined Strongmycontroller points to the weak pointer Weakmycontroller, in fact the final result is a strong reference to the Mycontroller object in the block. When the block is destroyed, the Strongmycontroller pointer variable is destroyed and the Mycontroller object that eventually points to it is destroyed. This ensures that the object is present when the object is used, and discards the ownership of the object when it is finished.
Arc and toll-free bridging
The toll-freebridging of the MRC does not involve the transfer of memory management, OBJECTIVE-C (hereinafter referred to as OC) and Core Foundation (hereinafter referred to as CF) each manages their own memory, which can be exchanged directly with each other, for example:
12 |
NSLocale *gbNSLocale = [[NSLocale alloc] initWithLocaleIdentifier:@ "en_GB" ]; CFLocaleRef gbCFLocale = (CFLocaleRef)gbNSLocale; |
And under Arc, things get a little bit more complicated. Because Arc is able to manage the memory of OC objects, but cannot manage CF objects, CF objects still require us to manage memory manually. When the bridge object is between CF and OC, the problem arises, and the compiler does not know what to do with the OC pointer and the object that the CF pointer points to. At this point, you need to use __bridge, __bridge_retained, __bridge_transfer and other modifiers to tell the compiler how to do it.
__bridge
It tells the compiler that it is still responsible for managing the reference count at one end of the OC, and the developer continues to manage things on the CF side, such as:
1234 |
CFStringRef cfString = CFStringCreateWithCString(kCFAllocatorDefault, "CFString" , kCFStringEncodingUTF8); NSString *ocString = (__bridge NSString *)cfString; CFRelease(cfString); NSLog(@ "%@" ,ocString); |
__bridge_retained or Cfbridgingretain
The two functions are the same, but the usage is different.
Tells the compiler that a retain object is required, and the developer is responsible for releasing it at the CF end. This way, if the object is released at one end of the OC, the object will not be destroyed if the developer does not release the object at one end of the CF.
123456 |
NSArray *ocArray = [[NSArray alloc] initWithObjects:@ "foggry" , nil]; CFArrayRef cfArray = (__bridge_retained CFArrayRef)ocArray; /** 使用cfArray **/ CFRelease(cfArray); |
__bridge_transfer or Cfbridgingrelease
The effect is the same, but the usage is different.
This keyword tells the compiler bridge that it also transfers ownership of the object, such as:
1234 |
CFStringRef cfString = CFStringCreateWithCString(kCFAllocatorDefault, "CFString" , kCFStringEncodingUTF8); NSString *ocString = (__bridge_transfer NSString *)cfString; //CFRelease(cfString); //不再需要释放操作 NSLog(@ "%@" ,ocString); |
In the conversion process, we only need to choose the appropriate keywords according to the specific needs.
In addition, the IDs and void * In ARC cannot be converted directly to each other and must be decorated with the appropriate keywords through toll-freebridging.
Arc and Iboutlet
For the Iboutlet attribute should be used strong or weak always have doubts. This is what the official document is about:
From a practical perspective, in IOS and OS X outlets should is defined as declared properties. Outlets should generally is weak, except for those from File's Owner to top-level objects in a nib >>>file (or, I N IOS, a storyboard scene) which should be strong. Outlets That's create should therefore typically be weak.
What a long paragraph of English want to say is: If the nib file constructed view is directly referenced by the controller top-level view, the corresponding Iboutlet property should be strong;
If the view is a child view on the top view, the View property should be weak, because the top view is referenced by the controller using the strong property, and the top view itself holds the view;
If the controller requires a separate reference to a view, or if the controller does not reference a view's parent view, then its properties should also be strong.
Well, in fact, I can say that if you really lazy to distinguish when to use strong, when with weak, then will be the Iboutlet attributes are set to strong it! When the controller is destroyed, the corresponding Iboutlet instance variable is also destroyed, the strong pointer is set to nil, so there is no memory problem.
Reference documents
Transitioning to ARC Release Notes
Managing the lifetimes of Objects from Nib Files
Nib Memory Management
MRC to Arc for iOS