I have several ivar NSArrays that I initialize in my -viewDidLoad method. One contains strings, one contains IBOutlets. However, when I initialize, all of the objects in the array are out of scope, and the memory address is 0x0 (according to the Xcode debugger). However, when I have a local NSArray with the same objects, it works fine. Initializing an ivar NSString or NSDictionary both work fine.
The code:
//.h file
#import <UIKit/UIKit.h>
#interface myViewController : UIViewController
{
NSArray *myArray;
}
#end
//.m file
#import "myViewController.h"
#implementation myViewController
- (void)viewDidLoad
{
[super viewDidLoad];
myArray = [[NSArray alloc] initWithObjects:#"aString", #"another string", nil];
NSLog(#"myArray equals: %#.", myArray);
}
#end
When I try using the array, I get an EXC_BAD_ACCESS runtime error. Is this an Xcode bug, or am I missing something about NSArray? UPDATE: I am using ARC. After I turn ARC off and do a clean build, I no longer get this problem. Is this a bug in ARC?
I don't know what the problem was, but I switched to a stable version of Xcode (4.0.2) and I had NO problems at all. Thanks everyone for trying to help!
That you are using ARC is important to note when asking such a question.
How are you trying to use the array (show the code)? There is a known bug in certain versions of ARC (which can't be discussed on the iOS side, but the same bug is in the Lion release of ARC) where fast enumeration of a collection under ARC can cause a crash.
Sounds like the framework has not instantiated the IBOutlet instances yet. Can you hold off and populate the arrays in the viewWillAppear method? This will be called before the user sees anything. Otherwise pull them off of IB, just manage them manually and alloc them at whatever point you want.
Your sample code seems to be allocating a new (and local) version of myArray rather than setting the iVar you declare in your header file. Try changing:
NSArray *myArray = [[NSArray alloc] initWithObjects:#"aString", #"another string", nil];
to
myArray = [[NSArray alloc] initWithObjects:#"aString", #"another string", nil];
With ARC off, I would presume that any attempt you make to access myArray would do nothing because it would be set to nil and ignore all messages. I'm not sure why this code would work any differently with ARC enabled.
Related
UPDATE: I thought the default behavior under ARC is assign, but it is strong. So, don't bother reading the question, it is useless :)
Consider the following code:
#import "AppDelegate.h"
#interface TestClass: NSObject
#property (atomic) NSMutableArray *strings; //"assign" by default
#end
#implementation TestClass
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
TestClass *testObject = [TestClass new];
testObject.strings = [[NSMutableArray alloc] init];
//(*) Why isn't strings array deallocated here under ARC? There are no strong references to it.
[testObject.strings addObject:#"str1"];
[testObject.strings addObject:#"str2"];
[testObject.strings addObject:#"str3"];
NSLog(#"Strings: %#", testObject.strings);
return YES;
}
#end
Here strings property is declared as assign (by default). So, if I'm not mistaken, there are no strong references to this array in the code at all. So, from my point of view strings should be deallocated at (*). However, the code works and prints the array.
Why? My possible explanation is that there are some implementation details related to the NSMutableArray, so there are some internal references left to the strings array, so it remains valid. So it is just pure luck. Am I right? I've tricked the system into returning retainCount, it was equal to 2 at the point of NSLog.
If I change the property declaration to (atomic, weak), the array is nil as expected.
I use Xcode 7.1.1 (7B1005) with OS X 10.11.2 (15C30). I checked the DEBUG version in the iOS simulator.
I found a code like this on the internet, and expected it to crash, but it didn't. Hence the question.
Under ARC the default ownership is strong not assign, see the Clang ARC documentation for details.
I'm a little confused about synthesized properties. I have an array that I want to be accessible from other classes so this is my code:
MyClass.h
#interface MyClass : CCLayer {
NSMutableArray *myArray;
}
#property (nonatomic, retain) NSMutableArray *myArray;
MyClass.m
#synthesize myArray;
-(id)init
{
myArray = [[NSMutableArray alloc] init];
}
-(void)dealloc
{
[myArray release];
myArray = nil;
}
I am a little confused now..is myArray the same as self.myArray? Do I have to release self.myArray as well? Thanks.
You declared your property as retain, it means that it will be retained automatically if you will set is using self.myArray. So, you can simply create autoreleased array in your init method and set it as
myArray = [NSMutableArray array];
self.myArray = myArray;
in this case you are not have to release it in the dealloc method or anything else. And as dasblinkenlight said you have to use #synthesize if you want to be sure that self.myArray is linked with your myArray instance.
Assuming that your #synthesize directive looks like this
#synthesize myArray;
you do not need to do anything in addition to what you are already doing: your property stores its value in the instance variable of the same name.
EDITED : Removed the alternative that suggests setting self.myArray in the dealloc method.
Yes you do, the best method is to set the property nil and release your variable.
-(void)dealloc{
self.myArray = nil;
[myArray release];
[super dealloc];
}
The code you provided is not really correct.
No, accessing a property and accessing the field itself are not the same.
My guess is that you are looking at old obj C examples where it was necessary to create the field with the property.
You also have no #synthesize directive in your code.
In current obj C code there is no need to declare a field to back the property, the field and the getter and setter will be autosynthesized (generated by the compiler) for you.
The default field generation is the name of your property with an underscore in front of it.
When you access the field directly via _myArray you will bypass any retain or release code that is contained in the generated getter/setter and have to manually manage memory in a non ARC project.
So to sum up, you dont need your field definition, and you dont need a synthesize directive.
You access your field directly with _myArray, or the property via self.myArray
They are not the same thing, one goes through generated code which obeys your property definition as to retain, assign, copy and accessing the field directly bypasses these semantics altogether.
If you define your property as retain you will need to release it in dealloc
You can use either
self.myArray = nil;
which will handle the release or
[_myArray release];
_myArray = nil;
Although someone in a previous post said setting the property to nil in dealloc might cause a problem Ive never seen it actually happen in my apps, ymmv
To answer your questions:
I am a little confused now..is myArray the same as self.myArray?
Yes, but no. Both point to the same object, the same area in memory. If you read myArray or self.myArray, they're identical in behavior minus the message send overhead for self.myArray.
However if you assign to myArray, the object will not be retained. It will only be retained if you assign to self.myArray.
Do I have to release self.myArray as well?
No.
You can also choose to either release or set the property to nil. As long as the property is #synthesize'd both examples do the same thing:
-(void) dealloc
{
[super dealloc];
[myArray release];
}
-(void) dealloc
{
[super dealloc];
self.myArray = nil;
}
See here for a discussion of the pros/cons to each approach.
From the question I think you're the developer who should really be using ARC. You'll have less to learn and fewer technical problems down the road. I can't understate how important using ARC is in these days, specifically if you don't have much ObjC experience. Read this how to enable ARC for cocos2d or just use Kobold2D to be able to work with an ARC-enabled cocos2d out of the box.
I have a property for my MainView class, arr
#property NSMutableArray *arr;
In my ViewController.m, inside viewDidLoad, if I use
MainView *mainView = (MainView *) self.view;
mainView.arr = [[NSMutableArray alloc] init];
It compiled, but gave a warning of "Assigning retained object to unsafe property; object will be released after assignment". But if I change the second line above to
mainView.arr = NSMutableArray.new;
then there will be no warning. I thought alloc init is the same as new? Why does the first version give warning and actually is it dangerous or can it be made so there is no warning?
If you use [NSMutableArray new] syntax, you will see the warning, so it's not an issue that your problem went away, but rather that your nonstandard syntax of NSMutableArray.new didn't generate the warning. The problem is that your property is defaulting to an unsafe_unretained, and whenever you assign a retained object to the unsafe_unretained object, ARC will immediately release it for you. Just try adding an object to your array and then NSLog'ing it, and you'll see the EXC_BAD_ACCESS which illustrates the problem. Change your property to:
#property (strong, nonatomic) NSMutableArray *arr;
and your problem goes away.
Again, the lack of warning from your nonstandard usage of NSMutableArray.new is not an indication that there's no problem, but rather that the compiler just didn't generate the warning for you. (Frankly, I'm really surprised that the dot syntax for invoking a method worked at all. The dot notation is generally used for accessing properties, not for invoking methods.) Use the [NSMutableArray new] syntax if you really want to use new. But the preferred syntax is really [[NSMutableArray alloc] init].
Heed the warnings.
You must tell type of property. For example:
#property (retain) NSMutableArray *arr;
See property type in documentation.
I think that the reason you're getting the warning is because arr is not a strong reference. If you try "#property (strong, nonatomic) NSMutableArray *arr;", the warning will go away.
P.S. "(strong, nonatomic)" is the approach to use when employing automatic reference counting (ARC). I too am surprised that NSMutableArray.new worked. I think it's better to use "[[NSMutableArray alloc] init]". That way, Xcode will tell you if the class you are instantiating has a more appropriate, specialty initializer (e.g. "initWithFrame:").
I have been developing an app for a while and now I have gotten to the "Instruments-Leaks" part.
I remember a thing that puzzled me about ivars a few months back when I was learning. I took the whole thing a bit on faith and just followed the way Apple and others did it. As far as I can read, the accessors generated by the SDK will take care of the memory management.
But how are ivars themselves initialized?
If I have an ivar like this in my interface;
#interface
{
NSArray *results;
}
#property(nonatomic, retain) NSArray *results;
#end
#implementation
#synthesize results;
If I during run time try to do this:
[self setResults:allReadyInitializedArray];
It will crash, telling me that this result object was not initialized. If I however do this:
self.results = [[NSArray alloc] init]; //Im assigning this property memory, but hasn't the SDK already done that?
[self setResults:allReadyInitializedArray];
it will work but it will apparently leak memory.
I was under the impression that using the generated
accessors would release the old value before setting the new, meaning
the above ought to come out with the old value released and the new with a +1 retain count.
Does it specifically have to do with the ivar being of type NSArray/NSMutableArray, I can't recall it has been a problem with other ivars.
The problem has been particular prominent in my XML parser, where I continuously need to set an ivar value, use it, overwrite this value, use the new value etc.
Would someone please help me outline the correct way "of going from": #property() -> #synthesize -> using the ivar -> to dealloc?
I have read the memory management documents, I have tried looking for some in debt documentation that was within my understanding, but it seems that even though I use ivars on a daily basis I don't understand what goes on behind the scenes.
All ivars initially set to nil so you need to instantiate them before use. It is really hard to say why setResults may produce errors without seeing its implementation.
self.results = [[NSArray alloc] init];
Here you create new array object using alloc method - its retain count equals 1. After that your setter method retains your array once more and so your 1st objects retain remains "unhandled" resulting in memory leak. To remove leak you can rewrite your code like:
self.results = [[[NSArray alloc] init] autorelease];
// or
self.results = [NSArray arrayWith...]; // any NSArray's convenience method that returns autoreleased object.
My understanding is that
self.results = anArray;
is the same as
[self setResults:anArray];
just because results is a property in this case.
The way setResults: is implemented is set by the #property (in this case it will retain the new value). So this means anArray will have a retain count of 1. After setting self.results, anArray will have a retain count of 2. This is why you want to release the previously used anArray.
That said, I don't understand why setResults: crashes when you're setting it. (Maybe it just crashes only when you try to use self.results, instead of setting it?)
I'm just a beginner myself, if something is wrong I strongly encourage everyone who reads this to let me know what is wrong or correct. Still learning this myself.
The way Apple would do this:
In the .h file
#property (nonatomic, retain) NSArray *results
In the .m file
#synthesize results;
- (id)init {
NSArray *anArray = [[NSArray alloc] init]; // retainCount = 1
self.results = anArray; // retainCount = 2
[anArray release]; // retainCount = 1, only one "left" is in self.results
}
- (void)dealloc {
[results release];
}
I'm pretty new to objective-c and try to create a small app for the iphone.
I'm nearly done beside this little error here. Actually, I've searched hours with google to find a proper solution but unfortunately I'm not able to find a solution which works.
I'm using this tutorial here to build up an UITableView: UITableView Tutorial
The full error message looks like this:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '* -[NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
This is the Data Controller Header:
MyLinksDataController.h
#interface MyLinksDataController : NSObject {
NSMutableArray *tableList; //<---important part
}
- (unsigned)countOfList;
- (id)objectInListAtIndex:(unsigned)theIndex;
- (void)addData:(NSString *)data; //<---important part
- (void)removeDataAtIndex:(unsigned)theIndex;
#property (nonatomic, copy, readwrite) NSMutableArray *tableList; //<---important part
.....
And the Data Controller Method:
MyLinksDataController.m
#import "MyLinksDataController.h"
#implementation MyLinksDataController
#synthesize tableList;
- (id)init {
if (self = [super init]) {
NSLog(#"Initilizing DataController");
//Instantiate list
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.tableList = [localList copy];
[localList release];
//Add initial Data
[self addData:#"AAAAAAAAAAAAAA"];
[self addData:#"BBBBBBBBBBBBBB"];
}
return self;
}
-------------------------------later on in the source code---------------------------------
- (void)addData:(NSString*)data; {
[tableList addObject:data]; //<---- here the app crashes
}
I would pretty much appreciate any help.
Thank you for your support in advance.
Daniel
Sending the copy message to an NSMutableArray -- as in the following statement in init -- returns an immutable copy.
self.tableList = [localList copy];
Cocoa documentation uses the word immutable to refer to read-only, can't-be-changed-after-initialization objects. Hence the subsequenct call to addObject: fails with an error message.
Note how the assignment statement above doesn't trigger any compiler warning. copy returns an id, which fits comfortably -- as far as the compiler is concerned -- in the NSMutableArray* tableList. There's no runtime error here either, as no messages get passed around; an NSArray pointer is just placed in an NSMutableArray pointer variable.
To obtain a mutable copy, use mutableCopy instead.
Note that both copy and mutableCopy create a new array and copy the content of the original to it. A change in the copy will not be reflected in the original. If you just need another reference to the original array, use retain instead.
You can find more detail in the discussion section of the copyWithZone reference and in the NSMutableCopying protocol reference.
You're running into, basically, the memory management rules of Cocoa (specifically, these details). If there is an object with an immutable version and a mutable version, then sending -copy to an object will return an immutable object.
Let's step through the relevant part.
NSMutableArray *localList = [[NSMutableArray alloc] init];
This creates a new, empty mutable array that you own. Fine.
self.tableList = [localList copy];
This creates an immutable copy of the empty array. Furthermore, you own this freshly created copy. That's two objects you own at the moment.
This also assigns your copied object to the tableList property. Let's look at the property declaration:
#property (nonatomic, copy, readwrite) NSMutableArray *tableList;
This property is declared with the copy attribute, so whenever a new value is assigned to it, another -copy method is sent to it. This third copy, however, is not owned by you—it's owned by the object.
[localList release];
That releases the original empty mutable array. Fine, but there's still the one you made in the second line floating around, unreleased. That's a memory leak.
If you actually need a mutable copy of something, you want the -mutableCopy method. (The documentation for these methods is found under NSCopying and NSMutableCopying.) However, you're never going to get a mutable version of something into a property with the copy attribute, since it will send -copy to whatever it is assigned. Your property should use the retain attribute instead of the copy attribute, and the code to initialize it should look something like this:
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.tableList = localList;
[localList release];
Or, a shorter version:
self.tableList = [NSMutableArray array];
There's no need to copy anything in this situation, you're just creating a fresh object.
If u are assigning localList from another object may be that is not Mutable in that case it can through this kind of error.
I hope it will be helpful.
self.tableList = [localList mutableCopy];
Hi instead of mutableCopy i believe "strong" can also be used to tackle this problem. I had similar problem in my code as well because of using "copy" instead of "strong." So the below line:
#property (copy, nonatomic) NSMutableArray *computers;
It should be:
#property (strong, nonatomic) NSMutableArray *computers;
Hope it will be of immense help for beginners making mistakes like me.
This will resolve the issue:
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.localList = [[NSMutableArray alloc]initWithArray:localList];