Just have confusion to improve the code quality in project. Is it right way to assign NSArray object to another NSArray object. But i haven't alloc the one NSArray object.
Is it right below code? Is there any issue for memory management issue?
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, weak) NSArray *array1;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"A",#"B",#"C", nil];
NSLog(#"array2 count : %lu",(unsigned long)array2.count);
self.array1 = array2;
NSLog(#"array1 count : %lu",(unsigned long)self.array1.count);
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Output :-
2016-04-06 22:22:56.057 PhotoViewer[969:56203] array2 count : 3
2016-04-06 22:22:56.057 PhotoViewer[969:56203] array1 count : 3
My question, Is it required alloc for array1 object?
Ex : self.array1 = [[NSArray alloc] init];
self.array1 = array2;
Your question addresses a core topic which is vital to understand when programming in any language - the difference between value and reference types and semantics.
In summary: for an assignment between two variables of value type the actual bits used to represented the value are copied and after the assignment there is no connection between the two variables - altering one does not effect the other. As the bits are stored directly in the variable there is no need to allocate seperate storage for them. In Objective-C types such as double are value types.
For reference types the bits used to represent the value are stored in storage distinct from the variable and the address of this distinct storage is what is stored in the variable. Assignment copies this address so after assignment the two variables refer to the same value. For mutable values altering the value via one variable effects what the other variable sees as well. Storage for reference types need to be managed separately from the variables, with new storage being allocated, and value bits being copied into it, when a new distinct value is required. Each distinct value is often called an object. For example, in Objective-C NSString is an immutable reference type, while NSMutableArray is a mutable one. In either case new distinct objects are created with alloc/init, new etc.. Simple assignment causes sharing.
In your example the assignment is sharing an immutable array, there is no need to alloc a copy.
There is much written on this, here are some slides from a university which explain it all quite clearly.
HTH
No, it's not needed. array1 is merely a pointer to the memory already pointed to by array2, you don't need to allocate any memory for array1 as the two pointers point to the same memory.
Related
I am hoping not to need to use an NSMutableArray here. I have an array with 10 elements. I want to change the value at index 4. Is there a way to do this without having to use NSMutableArray? The problem with NSMutableArray is that we can change anything about it, including its size. I don't want the size of this array to change accidentally. I just want to change the value at index 4 from say 22 to 25. How might I do that? doing array[4]=25 is not working.
NSArray *ar1 = #[#"1",#"2"];
NSMutableArray *ar1update = [ar1 mutableCopy];
ar1update[1] = #"Changed";
ar1 = [NSArray arrayWithArray:ar1update];
The only way is to create a new NSArray and change your pointer to a new NSArray. I can give an example...
In interface:
#property (strong, nonatomic) NSArray *myArray;
In implementation:
- (void) updateMyArray{
NSMutableArray *myArrayMut = [self.myArray mutableCopy];
myArrayMut[4] = #"new item";
self.myArray = [myArrayMut copy];
}
So basically, you can create a mutable copy temporarily, make the change you need, and then make an immutable copy. Once you have the immutable copy, you can point myArray to the new copy. As long as you are only changing existing items in updateMyArray and the myArray starts out with 10 items or less, you will never be able to have more than 10 items.
If you don't wish to use NSMutableArray how about a plain old C array? E.g.:
int array[10];
...
array[4] = 25;
You can store Objective-C objects in such an array and ARC will handle the memory management.
If you really want a fixed-sized NSArray/NSMutableArray you can do that by subclassing those types yourself - subclassing NSArray only requires implementing two methods and you can use an underlying C array or NSMutableArray for the actual storage.
HTH
Here is code I am referring to.
// Person.h
#interface Person : NSObject {
NSString *firstName;
NSString *lastName;
}
#end
// Person.m
#implementation Person
- (id)init {
if (![super init]) return nil;
firstName = #"John";
lastName = #"Doe";
}
#end
// MyClass.m
#implementation MyClass
.....
- (NSArray *)getPeople {
NSMutableArray *array = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
}
return array;
}
.....
#end
Now, I know there is no memory-management going on in this sample code. What would be required?
In the getPeople loop, I am alloc'ing a Person (retainCount 1), then adding it to array. The retain count is now 2, right? If it is two, should I be [p release]'ing after adding it to the array, bringing the retainCount back down to 1?
Am I right in that it is the caller's responsibility to release the array returned by the method? (Which would also free the memory of the Person's, and their instance variables, assuming their counts are at 1).
I have read Apple's memory management document, but I guess what I am most unclear about, is what increases an objects retain count? I think I grasp the idea of who's responsibility it is to release, though. This is the fundamental rule, according to Apple:
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
bobDevil's sentence "only worry about the retain counts you add to the item explicitly" made it click for me. After reading the Ownership policy at Apple, essentially, the object/method that created the new object, is the one responsible for releasing /it's/ interest in it. Is this correct?
Now, let's say I a method, that receives an object, and assigns it to a instance variable. I need to retain the received object correct, as I still have an interest in it?
If any of this is incorrect, let me know.
You are correct that the retain count is 2 after adding it to an array. However, you should only worry about the retain counts you add to the item explicitly.
Retaining an object is a contract that says "I'm not done with you, don't go away." A basic rule of thumb (there are exceptions, but they are usually documented) is that you own the object when you alloc an object, or create a copy. This means you're given the object with a retain count of 1(not autoreleased). In those two cases, you should release it when you are done. Additionally, if you ever explicitly retain an object, you must release it.
So, to be specific to your example, when you create the Person, you have one retain count on it. You add it to an array (which does whatever with it, you don't care) and then you're done with the Person, so you release it:
Person *p = [[Person alloc] init]; //retain 1, for you
[array addObject:p]; //array deals with p however it wants
[p release]; //you're done, so release it
Also, as I said above, you only own the object during alloc or copy generally, so to be consistent with that on the other side of things, you should return the array autoreleased, so that the caller of the getPeople method does not own it.
return [array autorelease];
Edit:
Correct, if you create it, you must release it. If you invest interest in it (through retain) you must release it.
Retain counts are increased when you call alloc specifically, so you'll need to release that explicitly.
factory methods usually give you an autoreleased object (such as [NSMutableArray array] -- you would have to specifically retain this to keep it around for any length of time.).
As far as NSArray and NSMutableArray addObject:, someone else will have to comment. I believe that you treat a classes as black boxes in terms of how they handle their own memory management as a design pattern, so you would never explicitly release something that you have passed into NSArray. When it gets destroyed, its supposed to handle decrementing the retain count itself.
You can also get a somewhat implicit retain if you declare your ivars as properties like #property (retain) suchAndSuchIvar, and use #synthesize in your implementation. Synthesize basically creates setters and getters for you, and if you call out (retain) specifically, the setter is going to retain the object passed in to it. Its not always immediately obvious, because the setters can be structured like this:
Person fart = [[Person alloc] init];
fart.firstName = #"Josh"; // this is actually a setter, not accessing the ivar
// equivalent to [fart setFirstName: #"Josh"], such that
// retainCount++
Edit:
And as far as the memory management, as soon as you add the object to the array, you're done with it... so:
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
}
Josh
You should generally /not/ be worried about the retain count. That's internally implemented. You should only care about whether you want to "own" an object by retaining it. In the code above, the array should own the object, not you (outside of the loop you don't even have reference to it except through the array). Because you own [[Person alloc] init], you then have to release it.
Thus
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
Also, the caller of "getPeople" should not own the array. This is the convention. You should autorelease it first.
NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
You'll want to read Apple's documentation on memory management: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
I have a Singleton object that manages all my lists. We'll call it ListStore.
ListStore has a mutable array, which stores Lists.
#interface ListStore : NSObject
#property (nonatomic, copy) NSMutableArray *lists; // an array of List objects
end
Lists has a mutable array, which stores Things.
#interface Wanderlist : NSObject <NSCoding, NSCopying>
#property (nonatomic, copy) NSMutableArray *things; // an array of Thing objects
#end
At any time, a background process might go through ListStore and loop through and process all Lists, while a user might be interacting with a List.
To guard against "object was mutated while being enumerated" type errors, I do this:
// all of this is in a background thread
NSArray *newLists = [[ListStore sharedStore] lists] copy];
for (List *list in newLists) {
// yay, no more crashes, because I'm enumerating over a copied object, so the user
// can do whatever they want while I'm here
for(Thing *thing in list.things) {
// oh crap, my copy and the original object both reference the same list.things,
// which is why i'm seeing the 'mutation while enumerating" errors still
...
}
}
I originally thought that because I made a copy into newLists that all of its members would be properly copied. I now understand that not to be the case: I'm still seeing the "object was mutated while enumerated" errors, but this time it's happening on list.things.
Can I use NSCopying with my setup so that when I say:
[[ListStore sharedStore] copy];
It calls copyWithZone: on Lists, so I can then copyWithZone: on things?
I tried to set it up like this but copyWithZone: wasn't getting called.
I know I could simply say NSArray *newList = [list.things copy] but I'd like to get a better understanding of NSCopying at the very least.
Right before submitting this question I clicked on a question in SO's list of related questions, and found my solution.
Figured it doesn't hurt to post my solution.
Instead of this:
NSArray *newLists = [[ListStore sharedStore] lists] copy];
I had to do:
NSArray *newLists = [[NSArray alloc] initWithArray:[[ListStore sharedStore] lists] copyItems:true];
From the NSArray docs:
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag
flag:
If YES, each object in array receives a copyWithZone: message to create a copy of the object—objects must conform to the NSCopying protocol. In a managed memory environment, this is instead of the retain message the object would otherwise receive. The object copy is then added to the returned array.
Once I used initWithArray:copyItems:, it automatically sent copyWithZone to all my List objects, and I was able to then manually perform a copyWithZone on list.things.
my.h file
#interface myObject : NSObject {
NSMutableDictionary *myDictn ;
}
i have a property
#property (nonatomic,retain) NSMutableDictionary *myDictn ;
then in .m File
i have a allocated it from id
- (id)init {
if (self=[super init]) {
myDictn= [NSMutableDictionary alloc]init];
}
}
my Question is i have mentioned Retain in Property and i have allocated memory also (is Retain Count goes to 2 in (id)init )
so how to manage memory in this case ?
i am new to this so dnt have much idea regarding memory management .
one more thing if i have a method x in my code and i also allocated memory to myDictn then in that case also how can i use release .??
Thanks in Advance .
The proper way to initialize a retained property is:
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
self.myDictn = newDict;
[newDict release];
or alternatively:
self.myDict = [NSMutableDictionary dictionary];
The first method makes sure not to increase the already retained property's retain count by performing alloc,init (an action that increments retain count) on a temporary object.
The second method uses a convenience method for obtaining an autoreleased instance of the dictionary.
You should first refer to Cocoa Fundamentals Guide. Then, if you're targeting iOS > 4.3, you can rely on Automatic Reference Counting for memory management. But be sure to understand the base concepts and read the fundamental guide or you'll waste a lot of time programming in the dark.
I am doing some research on reference count increase. Please help on finding it.
Below is sample code and research i'm doing what would happen of reference counting for each line below.
.h file
NSArray *tempArray;
#property (nonatomic, retain) NSArray *tempArray;
.m file
#synthesize tempArray;
-(void) sampleFunction
{
NSArray *myArray = [[NSArray alloc] init]; // Thinking reference count increases to "1"
tempArray = myArray;// reference count increases and tempArray gets retain count "1" now.
tempArray = myArray;// reference count increases and tempArray gets retain count "2" now.
tempArray = [NSArray arrayWithObject:#"SomeString"]; // retain count = ?
}
I know this code may not be for functioning, but this is for only researching about what will happen on reference counting for such scenarios. I tried printing retainCount, but it doesn't show the correct result. Please advise me how does the reference count works on this each line?
In lines 2, 3 and 4 you are affecting the instance variable tempArray to the same object as myArray. But if you write it this way, you try to affect an instance variable. As a matter of fact, if you didn't write any #synthesize tempArray or #synthesize tempArray = tempArray in your code, by default the instance variable generated automatically to store the property value is the same name as the property name, but prefixed with an underscore. So as the property name is tempArray, the instance variable is named _tempArray. The instance variable tempArray itself does not exist and your line of code is invalid.
So if we suppose you wrote instead:
-(void) sampleFunction
{
NSArray *myArray = [[NSArray alloc] init]; // (1)
self.tempArray = myArray; // (2)
self.tempArray = myArray; // (3)
self.tempArray = [NSArray arrayWithObject:#"SomeString"]; // (4)
}
In (1) you are creating a brand new instance of NSArray. "alloc" always initialize new instance with a reference count of 1
In (2) you write self.tempArray = myArray (which is equivalent to [self setTempArray:myArray]; and thus call the property setter), so you set the property to point to the same array you created in (1). This array is thus retained by the property, and its retainCount increses by one, because it is retained by myArray and by the self.tempArray property.
In (3) you affect the property to the very same object as before. This the ref count does not change at all. You could understand that as if you replaced the value of the self.tempArray with another value, so the setter of the property release the old value (decrementing its ref count), then retain the new value (thus incrementing its ref count). As in your case the old and new values are the same object, you would decrement the ref count of your array then re-increment it again. In practice, the ref count does not even change at all (instead of decrementing+incrementing again) to avoid any potential dealloc of the object, because the default implementation of a property setter is as follow:
-(void)setTempArray:(NSArray*)newValue
{
// check if old and new value are different. Only do sthg if they are different
if (newValue != _tempArray)
{
[_tempArray release]; // release old value
[newValue retain]; // retain new value
_tempArray = newValue; // store new value in the backing variable associated with the property
}
}
In (4) you replace again the value of the property tempArray, but this time with a completely new object. So the property will release its old value and retain the new one. Thus the first array you created in (1) which had a refcount of 2 (retained by myArray and by self.tempArray) decrease its ref count to 1 (because the property won't retain it anymore), and the new instance you created [NSArray arrayWithObject:#"SomeString"] is retained by the property, so its ref count is +1.
If you replaced self.tempArray = ... (so the use of the property) with the direct use of the instance variable, using instance variables don't retain the objects they are affected to (except if you are using ARC but it seems you don't), so the ref count of the object wouldn't have changed at all in (2), (3) and (4).
Actually, retain count increase only in new, alloc, retain and copy condition but if we are providing ownership to an object through this for retain count will increase other than that there is no possibility to increase retain count.
First things first, don't even try to rely upon retainCount.
After that: you're wondering which scenario happens among the ones you enumerated. Well, neither one.
Why? Because, in first palce, you're assigning to an instance variable directly - that won't change retain count. At all. Except if you use ARC, but it seems you don't.
You probably wanted to assign stuff to the property of the object, that is, write
self.tempArray = myArray;
etc. Now because the property itself (and not its backing ivar!) is declared as retain, the corresponding accessor method will increase the reference count of the object being assigned to the property. However, in order not to leak memory, an accessor method is usually implemented by releasing the previously assigned object when assigning and thus retaining the new one, i. e.
- (void)setTempArray:(NSArray *)tmp
{
[tmp retain];
[tempArray release];
tempArray = tmp;
}
So basically, when you reassign myArray to the self.tempArray property, it looses and gains a reference, thus its reference count doesn't chnage at all.
When you assign another, new array to the property, then again myArray loses a refcount, dropping to 0 it is deallocated, then the new array, created using + [NSArray arrayWithObject:] is retained. Its exact reference count is supposed to be 1 after this, since it was created using alloc - init - autorelease (that's how the method is implemented), and it has been retained by the property. However, the value returned by - retainCount is still (and never) to be relied upon.
In your particular example, you are assigning to tempArray directly and not self.tempArray, so the retainCount will stay at 1 throughout. But let's go through what would happen if you did what I think you meant.
In objective-c, a synthesized retained property will have a setter functionally equivalent to this:
-(void) setTempArray:(NSArray *value)
{
if(tempArray != value) {
[tempArray release];
tempArray = [value retain];
}
}
This increases the retain count when a new object is assigned to it, essentially does nothing when it is set to the same object, and releases it when something else is assigned to it. So the retain counts in your example go something like this:
-(void) sampleFunction
{
NSArray *myArray = [[NSArray alloc] init]; // Retain count of 1
self.tempArray = myArray; // 2
self.tempArray = myArray; // still 2
self.tempArray = [NSArray arrayWithObject:#"SomeString"];
// myArray.retainCount is 1,
// tempArray.retainCount is 2 but with 1 autorelease
// myArray leaks
}