I was working on non-ARC based project which was very old, and added some new UIViewControllers which are based on ARC (can do it by setting -fobjc-arc flag in build phase).
As being mixed use of ARC and non-ARC, sometimes memory leak occurs because forgotten to release somewhere in non-ARC code, and switching from here to there can cause this.
Thus, I have decided to convert non-ARC project to ARC project and do it by following;
Convert non-ARC to ARC project without recreate it
And just removed dealloc, viewDidUnload function contents by removing release or autorelease related things.
After successfully done it, it seems okay to working on but sometimes crashes like message sent to deallocated instance.
I could find what is the reason of that crashes and can be fixed.
What I want to know from here is;
When converting, is there any specific guide or rules to do this rather than simple remove of release, autorelease related statements?
Any input will be very appreciated!
Automatic Reference Counting (ARC) is a compiler feature that provides automatic memory management of Objective-C objects.
Refer this url :-
https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
As mentioned above, in ARC, we need not add release and retain methods since that will be taken care by the compiler. Actually, the underlying process of Objective-C is still the same. It uses the retain and release operations internally making it easier for the developer to code without worrying about these operations, which will reduce both the amount of code written and the possibility of memory leaks.
There was another principle called garbage collection, which is used in Mac OS-X along with MRR, but since its deprecation in OS-X Mountain Lion, it has not been discussed along with MRR. Also, iOS objects never had garbage collection feature. And with ARC, there is no use of garbage collection in OS-X too.
Here is a simple ARC example. Note this won't work on online compiler since it does not support ARC.
#import <Foundation/Foundation.h>
#interface SampleClass:NSObject
- (void)sampleMethod;
#end
#implementation SampleClass
- (void)sampleMethod
{
NSLog(#"Hello, World! \n");
}
- (void)dealloc
{
NSLog(#"Object deallocated");
}
#end
int main()
{
/* my first program in Objective-C */
#autoreleasepool{
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass sampleMethod];
sampleClass = nil;
}
return 0;
}
get the following output...
demo :- Hello, World!
demo :- Object deallocated
xcode could do the most of the conversion alone. in most cases its enough to remove this statements. you need to check the property declarations #property(nonatomic, weak/strong/copy).
Here is a litte tutorial:
https://objectpartners.com/2013/07/17/converting-an-ios-project-to-use-arc-automatic-reference-counting/
Just follow the things
STEP 1: Go to Project Target and click Build Phases
STEP 2: Click Compile Sources.Where you can see the all the .m files
STEP 3: Double click that,white box will appear with cursor.
STEP 4: give -fno-objc-arc in where the non arc files are.
Related
I was reading this official guide: https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW13 and I'm not sure if it's referring to old ways of dealing with reference counting or is just for demonstration how it works - but should one use retain/release manually like in example with accessors?
You are not allowed to use retain with ARC. However it still works the same in the background as without ARC (and as described in that documentation you linked), but the retain and release calls are added by the compiler as necessary. You don't have to deal with that.
This setter:
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[_count release];
// Make the new assignment.
_count = newCount;
}
should look like this, when using ARC:
- (void)setCount:(NSNumber *)newCount {
// Make the new assignment.
_count = newCount;
}
you not use retain with ARC but
It is possible to disable ARC for individual files by adding the -fno-objc-arc compiler flag for those files.
You add compiler flags in Targets -> Build Phases -> Compile Sources. You have to double click on the right column of the row under Compiler Flags. You can also add it to multiple files by holding the cmd button to select the files and then pressing enter to bring up the flag edit box.
I had an app that was working just fine. And then I tried to embed the navigation controller into a tabbarcontroller and next thing I know I started getting these errors during compiling.
Would anyone know why these happen? Did some setting get unchecked or checked by accident?
Thanks,
Alex
Seems your previously working code did not use ARC, now you tried to embed it into code which uses ARC ... Refactor your code using "Edit->Convert->Convert to Object-C ARC"
ARC is enabled per translation -- every compiled source file and everything it sees via inclusion must abide by the ARC or MRC. And yes, the modes can coexist (i.e. you can have ARC on for some files, but not all and the libraries you link to can use either).
You have two modes:
ARC
The expression [obj autorelease] is forbidden. ARC will add it for you (unless you have unusual reference counting sequences).
Under typical scenarios, you can just write:
// a method which returns an autoreleased object
- (NSArray *)something
{
return [[NSArray alloc] initWithObjects:…YOUR_OBJECTS…];
}
and then ARC will add the autorelease for you.
But if you write:
- (NSArray *)something
{
return [[[NSArray alloc] initWithObjects:…YOUR_OBJECTS…] autorelease];
}
in ARC, it will be a compile error (like the one in your title).
MRC
And this is the MRC form:
- (NSArray *)something
{
return [[[NSArray alloc] initWithObjects:…YOUR_OBJECTS…] autorelease];
}
Your project probably uses ARC by default (i.e. it is defined in an xcconfig, at the project level, or at the target level), though you have added a source file which was written for MRC.
Since the file is either compiled as ARC, you can either remove the autorelease message or disable ARC for the single file.
The errors are on the new code?
In that case I think your projects is ARC-enabled and when you tried to embed the UINavigationController you inserted some non-ARC code.
Did you change the compiler?
LLVM compiler introduces ARC. If you were developing a non-ARC project, maybe you just compiled with LLVM and that broke your code.
Try refactoring the code. Check this.
I have an ARC enabled application that using a MRC(non-ARC) static library. In the static library, retain/release are overridden to provide some custom weak ref/cache behavior ([super retain/release] is called of course). The problem is that since retain/release are not allowed in ARC-enabled code, is it OK to use classes that override retain/release in ARC-enabled code? For now it seems to be working well, but I am not sure if this relies on undefined behavior which may break in the future.
Also what is the reason to forbid overriding retain/release? Is it because some special optimization was done by the compiler that bypasses the message binding process to speed up the method call? I know that _objc_storeStrong calls are generated by the compiler that do the reference counting, so does this mean that the overridden retain/release are not guaranteed to be called under ARC?
As long as the classes are compiled without ARC (which you can control on a file by file basis; go to Build Phases and add -fno-objc-arc as a flag to any file that should be compiled MRR in an otherwise ARC'd project), then the MRR compiled classes can override retain/release/autorelease to their heart's content.
Retain/release/autorelease are verboten under ARC because ARC is designed to handle all of the memory management for you at compile time while also forcing you to separate memory management from other roles that seemingly can be piled onto memory management, but really don't belong there.
For example, the most typical override of release involves checking the retainCount and, if it is 2, then the transition to 1 means "put this object back into a cache for later retrieval" whereas the cache is responsible for the final retained reference to the object.
It works, but it is horribly fragile and there are better solutions that do not involve colluding caching with memory management.
overriding of retain/release is incorrect. But if you need it:
-(id)retain
{
NSIncrementExtraRefCount(self);
return self;
}
-(void)release
{
if(NSDecrementExtraRefCountWasZero(self))
{
NSDeallocateObject(self);
}
}
-(id)autorelease
{ // Add the object to the autorelease pool
[NSAutoreleasePool addObject:self];
return self;
}
I haven't tested them for ARC. And an original article:
link
I'm starting to make use of static code analysis to find memory management problems in my code. I've found it very useful, but there are a couple of bits of code I've written that I'm sure aren't causing memory leaks (instruments doesn't report any) but are being reported by the analyser. I think it's a question of me writing the code in a non-friendly manner. Here's an example
for (glyphUnit *ellipsisDot in ellipsisArray) {
CGPathRef newDot = CGPathCreateCopyByTransformingPath(ellipsisDot.glyphPath, &ellipsisTransform);
CGPathRelease(ellipsisDot.glyphPath); // Incorrect decrement of the reference count of an object that is not owned at this point by the caller
ellipsisDot.glyphPath = newDot;
}
where glyphUnit is a simple custom class that has a GCPathRef as a property, which the custom class releases in its dealloc method. So in this loop I'm transforming the path and storing it in anewDot then releasing the original glyphPath so I can assign the newly created one to it. I can see how this is getting the code analyser confused, with it giving a message I'm decrementing an object I don't own. Is there another way swap in the new path without confusing it?
It should be,
for (glyphUnit *ellipsisDot in ellipsisArray) {
CGPathRef newDot = CGPathCreateCopyByTransformingPath(ellipsisDot.glyphPath, &ellipsisTransform);
ellipsisDot.glyphPath = newDot;
CGPathRelease(newDot);
}
You are creating newDot by doing CG CreateCopy operation and you need to do release on that variable. So the analyser is warning that you dont own ellipsisDot.glyphPath param to release it. You are trying to release the wrong param here. When you put that release statement in the second line as in question, ellipsisDot.glyphPath and newDot are pointing to two separate instances. Only on the third line, you were assigning newDot to ellipsisDot.glyphPath.
It turns out that I forgot about defining setters in my custom glyphUnit class. Being in the ARC world for objects and used to synthesizing my methods I had forgotten the need to manage my retain counts for core foundation references. I had been releasing glyphPath in my dealloc, but was not using a setter method. As #Sven suspected, I was simply using a synthesized assign and making up for my lack of setter method by doing some less than intuitive releases in my code snippet above. I've now added a setter method as below to glyphUnit
- (void)setGlyphPath:(CGPathRef)newPath
{
if (_glyphPath != newPath)
{
CGPathRelease(_glyphPath);
_glyphPath = CGPathRetain(newPath);
}
}
After adding this, I now had the necessary retain in place to change my code snippet to the one #ACB described and my code ran nicely (without it, it obviously caused an EXC_BAD_ACCESS).
Kudos to #Sven for inferring my mistake and setting me in the right direction... no pun intended.
I've discovered some inconsistencies when using the ARC migration tool multiple times on the whole project.
For example:
- (void)dealloc {
[ivar release], ivar = nil;
}
The first iteration convertis this to:
- (void)dealloc {
ivar = nil;
}
The second iteration gets rid of -dealloc alltogether. Assuming that it makes no sense to set ivars to nil in -dealloc since ARC does that anyways automatically, it's strange that ARC leaves the ivar = nil in the first place.
To prevent possibly strange bugs, is there a way to use the ARC migration tool only for one special file rather than the whole project?
When you choose Edit > Refactor > Convert to Objective-C ARC, a sheet opens with a list of your project's targets. Click the disclosure triangle next to a target to show a list of source files. From there you can choose the files to convert to ARC.
I don't believe so.
You don't have to do much to "convert" a project to use ARC support.
Remove all dealloc, release, and nil (where needed) calls.
No strange bugs should occur after the conversion, since the compiler takes care of everything for you and not buggy XCode.