What happen when "retain" NSArray or NSMutableArray? - iphone

In the source codes
#property(retain) NSString* str;
#sythesize str;
self.str = newStr;
I understand actually following will happen
if( str != newStr ){
[str release];
str = [newStr retain];
}
So how about the case for NSArray or NSMutableArray ? Seem like it is complex,shallow copy and deep copy should be considered.

It's the same. Setting a property only changes the ownership of that array, not the content of the array (the content is owned by the same array). Therefore, only the array needs to be -retain'ed.
In fact, the runtime doesn't care about the specific Objective-C type of the property. The same setter procedure will be applied to every #property(retain) properties.
To make the setter perform shallow copying, make it #property(copy). There no way to make it deep-copy.

Related

Usage of NSMutableString vs NSString?

I am confused between NSString and NSMutable string usage.
Suppose I have instance variables one and two declared in class like this:
NSString *one;
NSMutableString *two;
let us suppose I have created setters and getters of them using properties.
lets say I have changed my "two" string like these:
1. [two appendString:#"more string"];
2. two = #"string"
3. self.two = #"string"
Questions:
would 1st line release previous string and allocate new object and assign value to it.
if yes, then does that mean creating getters and setters are unnecessary in this case ? OR its unnecessary to create properties in NSMutablestring
In this case would the previous allocated string object released ?
Its for sure that first object would be released as we are calling setters here. is this code necessary or we can just use the code line 2 to assign string.
Now about NSString:
As can modify the string like this also :
one = [one stringByAppendingString:#" more string"];
self.one = [one stringByAppendingString:#" more string"];
Which is better using NSMutablestring or NSString ?
Sorry for long post, but I needed to understand these concepts.
For the first part of your question:
Almost definitely not. I expect the memory will be allocated dynamically, rather than released and reallocated.
If the previous answer is no, this is also, no.
The second and third options don't even work with the warning Incompatible pointer types assigning NSMutableString to NSString
I expect NSMutableString will be slightly more efficient in terms of memory as you are indicating early on that the program may need memory dynamically. NSString is likely to be allocated a single, suitably sized block of memory.
Your views of NSMutableString seem to be what the stringBy.. methods of NSString will do:
With NSString and its stringBy... methods, you are creating new objects, releasing the old one (if need be) and making the new object autorelease. (Take care if you are changing from non-autorelease to autorelease, you may have a release in your dealloc that isn't needed anymore)
NSString is a not mutable string object, which means, after initialization, you cannot change it, NSMutableString is mutable meaning you can append another string to it or other modifications.
when you do [two appendString:#"more string"], the pointer still pointed to the same location in memory and you don't have to worry about allocation or deallocation.
When you do two = #"string" , it means you make your string pointer pointed to a specific location in memory which contains static string with value "string" inside.
When you do self.two = #"string", you've already have a property named two declared. By using property in xcode, you don't have to worry about the memory since you've already specified in your property declaration. It is a nice tool xcode provide to you, and you should definitely use it whenever you can.
NSMutableString will save some memory as NSString objects are always constants. So reassigning a new value to an NSString variable will allocate new memory for the new string.
However, please note a few errors in your code. You will have to initialize the objects before sending messages to them:
// this won't work
NSString *one;
[one stringByAppendingString:#"more string"];
// nor this
NSMutableString *two;
[two appendString:#"more string"];
// first do this:
NSString *one = #"an initial string constant"; // or
NSString *one = [NSString string];
NSMutableString *two = [NSMutableString string];
NSString objects are immutable this means that when you "modify" a NSString you are in fact creating a new string and not modifying the old which is not very effective. With NSMutableString the string is kept in a buffer than can be modified.
So the simple rule is that if you need to modify the string in any way use a NSMutableString and if not NSString. You can also cast a NSMutableString to a NSString but not the other way around (then you need to make a copy)

Objective C: Allocating Memory

I am new to Objective C and here is my confusion:
When is it applicable to allocate memory for an instance?. Like this:
When is it applicable to use this...
NSString *str = [[NSString alloc]init];
and to use this...
- (NSString *) formatStr:(NSString *) str{
NSString *str = (NSString *) str;
...
.....
.......
}
and even creating UIActionSheet, it uses alloc but in other UI elements, it does not..
What exactly the reason and when shall it be done?
Thanks fellas.. :D
In addition to the "normal" allocation route (i.e. through [[MyClass alloc] init]) some classes provide so called "factory methods". These are class methods that allocate objects internally. The advantage of using factory methods is that they can create a suitable subclass to return to the caller. In both cases, though, the allocation is ultimately done by alloc/init.
Objective C's alloc method handles allocating memory, you don't have to worry about allocating, just managing the retain and release cycles.
checkout this About Memory Management article from Apple
when you create an instance using alloc+init OR you get an instance through a method that has init in the name (a convention, e.g. initWithString) you are said to own the object, this is, you must not retain it (its ref counter is already set to 1) and need to eventually release it when you are done with it. When you receive an instance by calling a method which hasn't get init in the name (rule on thumb but you should always check documentation) this means that you are not the owner of the object, i.e. the object might be released at any time, even while you are using it. Usually methods such as stringWithFormat will return autoreleased objects which will be around until the end of the event cycle (unless you claim ownership by calling retain on the string).
I strongly recommend reading the cocoa memory management guide.
NSString *str = [[NSString alloc]init]; //you own the object pointed to by str. Its retain count is 1. If you don't call release this will be a memory leak.
- (NSString *) formatStr:(NSString *) str{
NSString *str = (NSString *) str; //you don't own str. btw, you don't need casting here
//using str here might throw exception if its owner has released it
[str retain]; //you own str now. you can do whatever you want with it. It's yours
.......
}

Convert NSString into NSMutableArray

I am trying to convert (or copy?) a NSString into a NSMutableArray. I guess my problem is that I don't really understand the structure of a MutableArray. In my limited knowledge, an Array could look like this:
NoteBook = [[NSMutableArray alloc] init];
for (int temp = 0; temp < 3; temp++) {
[NoteBook insertObject:#"Page" atIndex:temp];
}
Which would give me an Array of PagePagePage. Let's assume I wanted to open a txt file which contains PagePagePage, but the words were separated by a defined string so that I can keep the individual objects in my array apart, like so: Page--- end of page ---Page--- end of page ---Page.
Now, my next step would be to read this information from the txt file:
NSString *tempTextOut = [NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:&error];
NoteBook = [tempTextOut componentsSeparatedByString: #"\n--- end of page ---\n"];
However, the last line does not work and I'm told by xCode: Incompatible Objective-C types assigning 'struct NSArray*', expected 'struct NSMutableArray*'. I don't really understand this - NSArray and MutableArray should be compatible (as one is the subclass of the other). Shouldn't xCode tell me that the problem is that I've been trying to convert a NSString into an NSMutableArray?
Would I perhaps need to re-set my MutableArray before putting something back into it, because right now, it still contains PagePagePage which I have assigned to it in the first step. I thought my NoteBook mutable array would simply be replaced by the string, but I guess that won't be the case.
I'd very much appreciate any help in this matter. Thanks!
componentsSeparatedByString: returns a plain immutable NSArray, not an NSMutableArray. You can pass the array to [NSMutableArray arrayWithArray:] or use mutableCopy on the array to get a mutable array from it, or you can use addObjectsFromArray: on an existing NSMutableArray to add objects to it.
If you go the mutableCopy route, do remember that you are responsible for calling release or autorelease on it.
Assigning a mutable object to the same immutable type will lead to runtime errors if you want to manipulate the immutable instance.
You can get your mutable copy by calling:
NoteBook = [[tempTextOut componentsSeparatedByString: #"\n--- end of page ---\n"] mutableCopy];
If NoteBook is a retained property you should assign to it this way:
self.NoteBook = [[[tempTextOut componentsSeparatedByString: #"\n--- end of page ---\n"] mutableCopy] autorelease];
so the mutable copy doesn't get over retained. You can release in your dealloc method then as normal.

NSArray randomly turning into different things!

I am having a problem with memory management I think. My NSArray (called arr) is turning into different things at random times. I have no idea why. I have a .h file that that declares the array, and then I initialize the array using
NSString *input = [[NSString alloc] initWithData:myData encoding:NSACIIStringEncoding];
arr = [input componentsSeperatedByString:#"\n"];
and then I use it throughout the program and suddenly it changes into different things (UITouch, sometimes, for example). I never called a release on it. Why is it doing this? How do I prevent objects from randomly being changed due to memory issues?
Thanks!
What happens, is that the memory, once occupied by your NSArray, is occupied by another object. This may be any object, and since you're touching the screen a lot a UITouch is very common.
This means you're not retaining the NSArray when you should, so it is released prematurely. You don't show the code declaring arr, but if you declare arr as
#property (nonatomic,retain) NSArray *arr;
and synthesize it using
#synthesize arr;
then the retaining is handled by simply assiging to self.arr instead of arr:
self.arr = [input componentsSeperatedByString:#"\n"];
In cocoa-speak, your object now "owns" the array. In the dealloc method of this class, you should [self.arr release].
Should you assign some other array to self.arr, the object assigned to self.arr previously will get released, and the new one retained.
Try to retain arr.
arr = [[input componentsSeperatedByString:#"\n"] retain];
Or Initalize a new Array with this array:
arr = [[NSArray alloc] initWithArray:input];

problem related to NSString

I have 1 NSString *abc = #"Hardik";
i have NSMutableArray *array;
now i had written [array addobject:abc];
then i'm printing,NSLog(#"array = %#", array);
but i'm getting NULL
why?
I have declared NSMutableArray *array; in a.h file
i had set #property(nonatomic,retain)NSMutableArray *array;
#synthesize array;
and i have synthesize it but getting value NULL
I'm not able to understand it?
You also need to initialise your array:
array = [[NSMutableArray alloc] initWithCapacity:10];
This is pretty fundamental stuff. Have you read the "Learning Objective C Primer" yet?
It sounds like you haven't actually allocated array. Generally, you would do this in your initializer. (Don't forget to add a release to your dealloc method, too.) #synthesize creates the getter and setter, but you still have to handle allocating/deallocating the object yourself.
It sounds like your NSMutableArray* array property may not have been initialised?
Can you post your class init method?
To trigger the synthesized accessor within a class itself, you must use self. If you don't, you access the attribute's address directly bypassing the accessor methods. You need:
NSString *abc = #"Hardik";
[self.array addobject:abc];
NSLog(#"array = %#", self.array);
The reason this is important is that the synthesized methods usually also initialize the property. The internals of the synthesize array method would look something like:
-(NSArray *) array{
if (array!=nil) {
return array;
}
array=[[NSMutableArray alloc] initWithCapacity:1];
return array;
}
self.propertyName is really just shorthand for [self propertyName] and self.propertyName=someValue is just shorthand for [self setPropertyName:someValue].
Until you call self.array at least once, the array property is not initialized.
However, just to confuse things, once you have called self.array once it is initialized so you can just call array directly. So...
[self.array addObject:abc];
NSLog(#"array = %#", array);
...works while the converse would return just an empty array.
So the rules are:
Within a class implementation
(including subclasses), calling just
propertyName gives you the address
of the property but does not call
the getter/setter accessor methods.
Within a class implementation
(including subclasses), using
self.propertyName calls the
getter/setter accessor methods but
does not access attribute directly.
From outside the class
implementation e.g.
myClass.propertyName calls the
getter/setter accessor methods.