Managing Memory of NSMutableArray with NSMutableStrings - objective-c

My apologies if this has already been asked and answered. I am new to Objective-C and I am trying to create a project that contains an object with NSMutableString and I am creating an NSMutableArray of those objects.
Each of the strings in the object are declared as follows:
#property (assign) NSMutableString* propname;
In the initialization routine (initStringObject) for the object I am setting each of the strings as follows:
self.propname = [NSMutableString stringWithCapacity:16];
[self.propname setString:#"Name"];
There are a number of properties with multiple NSMutableStrings, a couple of NSInteger and a float. All NSMutableStrings are allocated with different capacities.
For the NSMutableArray, I am declaring that in the interface section of the view controller .m file as follows:
#property (strong) NSMutableArray *objectarray;
In the loadview, routine I am initializing the array as follows:
self.objectarray = [NSMutableArray array];
[self.objectarray addObject:[[StringObject alloc] initStringObject];
The project builds fine but when I go to populate the view with the information in the object, the strings are corrupted. The integer and float values in the object are correct. I figure I must be losing the pointer to the correct location in memory but I cannot figure out what is going on. Eventually, if I keep running the program, I get an EXC_BAD_ACCESS error message.
This is an OS X application.
Can anyone see what I am doing wrong?
Thank you in advance.

I suspect it's the assign attribute in #property (assign). Use strong (the default) instead to have ARC properly manage the string objects.

Related

Xcode: #dynamic class variable not working

I have a class which declares a User and includes these variables:
(User.h file):
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * unid;
(User.m file):
#dynamic name;
#dynamic unid;
I have an array of values by parsing a string.
I then want to set the values accordingly:
(ViewController.m file):
[user setName:[returned objectAtIndex:1]];
[user setUnid:[returned objectAtIndex:2]];
When this is run the compiler gives me the following error:
unrecognized selector sent to instance
*** WebKit discarded an uncaught exception in the webView:shouldInsertText:replacingDOMRange:givenAction: delegate: <NSInvalidArgumentException> -[User setName:]: unrecognized selector sent to instance
When I comment out the setName line it works fine.
I then looked at the classes of the two objects from the returned array and they were both: __NSCFString
I then tried this piece of code:
(ViewController.m file):
[user setName:[returned objectAtIndex:2]];
Again the same error.
Why would the same input fail in one case and succeed in another if they are both expecting the same input?
Thanks.
EDIT:
This error is weird as this part of the app does not interact with any webviews. This returned array is parsed from a string gather from a webpage:
(ViewController.m file):
NSString *string = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://url_goes_here.com" encoding:NSUTF8StringEncoding error:&error];
NSArray *returned = [returned componentsSeparatedByString:#"#"];
However the unid is also parsed from this array without any problem.
The #dynmaic keyword means that you'll be providing the accessors yourself. If you want the compiler to create accessors for you, simply delete the #dynamic name declaration. (Because #synthesize is now the default, you don't have to use it explicitly.) Otherwise, you'll need to create the -name, -setName:, -unid, and -setUnid: methods yourself.
it is actually a set up class by xcode using the NSManagedObject subclass for core data
This is an important detail. In the case of managed objects, Core Data will provide the accessors for you and you just need the #dynamic property declaration to let the compiler know that it shouldn't generate accessors itself.
I'm a little confused as to why this error is coming from a web view delegate method. It might help if you could explain a little more about how your Core Data classes are interacting with a web view.
why would this work for the unid, but not the name
The error you're getting is an run time error -- an exception is being thrown. It's likely that the name accessor is simply the first one to be used; the same thing might happen for unid if that property were to be set first.
This error is weird as this part of the app does not interact with any webviews.
Another important clue. At this point, it sounds very much like you've got a bad pointer. You're sending -setName: to an object that's not what you think it is, and in this case it turns out to be a web view delegate. Try turning on NSZombies to help you track this down.

Setting WEAK to a non #property variable

Will need someone with knowledge of ARC to help me.
Basically, I have declared some variables as such in my class
#interface Class{
NSString* one;
NSString* two;
}
#property(nonatomic,weak) NSString* one;
As you can see, I can set the weak identifier to NSString* one. However, I do not need a getter/setter/synthesizer for NSString* two as it is just a common variable. How can I set a weak label to it so the memory is deallocated? Or is automatically set?
You can do it like this:
__weak NSString *two;
But you probably do not want to do it in this case.
Declaring an instance variable __weak means that the reference to the target object (a string in your case) will exist only as long as some other object holds a reference. When the last object holding a strong reference releases the string, your variable two will get nil-ed out automatically. This is very useful when objects hold references to each other, such as in parent-child hierarchies. Since your NSString *two could not possibly hold a reference to your object, using the __weak reference for it is highly questionable.
You can do this without worrying:
NSString* two = [[NSString alloc] init];
When your instance of the class Class is release for some reason, since is the only one (in theory) referencing two, it will be deallocated.
My advice (and I think Apple's although I could be wrong) would be to get into the habit of always using properties for your iVars, then this problem goes away.

Are Mutable classes "heavier?"

On some intuitive (perhaps wrong) idea of performance, I always get a copy of a mutable instance before I store it. So if a property expects an NSArray I take the mutable array I'm working with and store it as self.array = mutableArray.copy (though the property is specified as strong or retain).
This seems silly to me, suddenly, but is it? Do mutable instances -- doing the exact same task -- perform the same?
Note: The mutable instance falls out of scope and (thanks to ARC) gets released right after this, so there's no worry that it'll be mutated once it's assigned to the property.
NSArray and NSMutableArray are both (as far as I'm aware) implemented on top of CFArray, which simply has a flag specifying whether it's mutable. CFArray functions which require a mutable array have an assertion right at the beginning, checking that flag:
void CFArraySetValueAtIndex(CFMutableArrayRef array, CFIndex idx, const void *value) {
// snip...
CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__);
Mutable and immutable CFArrays are identical other than passing or failing this assertion, and so should NSArrays and NSMutableArrays be, performance- or other-wise.
Partly answered here: NSArray size and mutability
NSMutableArray is not noticeably slower or larger (memory-wise) than an NSArray. It's basically just an NSArray that reallocates itself when it gets full as as bigger array, and keeps doing that as you add items to it.
The reason for copying mutable arrays as immutable ones when assigning them to values in your class is so you can guarantee that their values don't change. If you store a mutable array in your class, other code can change its values outside of your class without calling any of your methods. That leaves you vulnerable to crashes due to internal inconstancy errors within your classes.
For example, supposing that when the array was set, you cached the length of the array as an int property in your class. That would be fine if the array was immutable, but if it was mutable, someone else could change the array, and your cached value would now be wrong, but you have no way of knowing that.
However, it's not necessary to do the copying manually. If you declare your array properties as:
#property (nonatomic, copy) NSArray *foo;
Then whenever you assign an array to object.foo, it will automatically be copied. You don't need to copy it again yourself. It's best practice to use a property type of copy instead of strong/retain for any type that has a mutable variant, like so:
#property (nonatomic, copy) NSArray *foo;
#property (nonatomic, copy) NSString *foo;
#property (nonatomic, copy) NSDictionary *foo;
#property (nonatomic, copy) NSData *foo;
etc...
However be careful not to use it for mutable properties, or it will make an immutable copy stored in a property that thinks it's mutable and cause a crash if you try to mutate it. The synthesised copy property isn't smart enough to use mutableCopy automatically.
#property (nonatomic, copy) NSMutableArray *foo; //don't do this
For clarity you're asking if, given an NSArray and an NSMutableArray both subjected to a battery of non-mutating test methods, does the NSArray perform noticeably faster? I specify non-mutataing, because it looks like you're copying a mutable array to an immutable array with the belief that the immutable array will perform its non-mutating methods faster than the mutable array. Anyways, the answer is no. (But don't take my word for it; profile).
Even if NSMutableArray overrode some non-mutating methods (which we can't know about, one way or another), you wouldn't need to worry about it. Adding a couple CPU cycles is trivial compared to the overall computational complexity of the operation. As long as NSMutableArray doesn't manage to turn a O(n) lookup-operation into a O(n2) operation, you'll be fine 99% of the time. (Those complexities are just fictitious examples).
While there are perfectly valid reasons why you might want to copy a mutable array into an immutable array (as pointed out by #NickLockwood), performance shouldn't be one of them. Premature optimization is very bad, after all.

XCode 4 c array of objective-c objects

With XCode 3 compiler, I could manage an array of objects like:
#interface myView:UIView
{
CALayer *layer[4];
}
#property (nonatomic,retain) CALayer **layer;
#end
#implementation myView
#dynamic layer;
- (CALayer **)layer { return layer; }
// I could then access elements like
- (void) example
{
self.layer[3] = NULL;
}
#end
With XCode 4 compiler the #property declaration generates an error "Property with retain must be an object type".
I guess best way to fix is to convert to NSArray, but I have 100's lines of code using the c-style array subscript (e.g., self.layer[i]). Is there some other way to fix?
Several problems with this code:
It should be MyView, not myView; classes start with capital letters.
CALayer ** is not an object type; it is a pointer to an object type, hence the compiler complaint. Simply making it assign will make it compile, but it'll still be wrong.
There is likely no reason to use a language array (MyClass foo[4]) to hold this data. Use an NSMutableArray (you can use [NSNull null] as a stand-in for "this slot is not populated".
If you really want to stick with the language array, drop the retain. Just remember that you have to explicitly manage the retain/releases of the objects within the array. The #property won't do that for you.
Also, while it may seem a pain to fix your code to be inline with typical standard patterns, it is only going to be more costly to do so as the code evolves and, someday, you'll likely be in a situation where you really need to do so....
Change it to an assign property so you don't try to retain a non-object?
You cannot use Objective-C memory management calls (i.e. retain) on a C array. You need to manage your array using standard C or C++ logic. You need to malloc and free memory on your own. If you do not need to retain the array then you can remove the retain property.

Comparing Objects in Objective-C

I am trying to compare objects in Objective-C and was just wandering how, for example, two objects (which are instances of UIView) are compared that hold two NSStrings like so:
#import <Foundation/Foundation.h>
#interface Notebook : UIView
{
NSString *nameOfBook;
NSString *colourOfBook;
}
#property (nonatomic, retain) NSString *nameOfBook;
#property (nonatomic, retain) NSString *colourOfBook;
Let's assume that I have two NSMutableArrays which hold several objects of the type Notebook. One is called reality and the other theory. Reality holds two notebooks with the nameOfBook #"Lectures" and #"Recipies", but colourOfBook are all empty. Theory holds three notebooks with the nameOfBook #"Lectures", #"Recipies", #"Zoo", and colourOfBook #"red", #"yellow", #"green".
I would like to compare the two arrays and adjust theory according to reality. In this case, it would mean to remove #"Zoo". I can't simply replace theory with reality as I would loose all the colours stored in theory.
This is the code I've come up with:
for (int i=0; i < [theory count]; i++) {
Notebook *testedNotebook = [Notebook alloc];
testedNotebook = [theory objectAtIndex:i];
if ([reality indexOfObject:testedNotebook] == NSNotFound)
{
NSLog(#"Book is not found in reality - remove it from theory...");
[theory removeObject:testedNotebook];
}
[testedNotebook release];
}
Now, my big question is how the objects will be compared. Ideally, I'd like to compare ONLY their NAMES regardless of the COLOUR. But I guess this is not how my code works right now. The entire objects are compared and the object in reality which holds #"Lectures" and #"" (no colour) cannot be the same as the object in theory which holds #"Lectures" and #"red".
How could I achieve to compare objects according to one of their attributes only (in this case the name)?
If you read the documentation for indexOfObject:, you'd find that NSArray calls isEqual: for each object in the array. So, override isEqual for Notebook and implement your own comparison routine.
Apropos of nothing, why are you allocating without initializing an instance of Notebook, overwriting it with an autoreleased instance, and subsequently releasing that? (Never mind you might be releasing it in your loop first!) You're destined for a crash. And why are you removing objects from an array while you're iterating through it?
You want to implement methods like -isEqual:. Please have a look here.