iphone [removeObjectIdenticalTo:] doesnt work - iphone

I have the following code that populates an array (this is within a loop):
NSString *code = [NSString stringWithFormat:#"%# - (%#) %#",[tempDic objectForKey:#"state"],[tempDic objectForKey:#"city"],[tempDic objectForKey:#"name"]];
[tempArrayOfAirports removeObjectIdenticalTo:code]; // checks for a previous object, then removes if found
[tempArrayOfAirports addObject:code]; //adds the object
Previously, code had simply been:
NSString *code = [tempDic objectForKey:#"city"];
[tempArrayOfAirports removeObjectIdenticalTo:code];
[tempArrayOfAirports addObject:code];
Which worked fine, but for some reason, changing "code" is keeping it from finding other identical strings. My result is a huge array with many, many repeated objects.

Since you're creating a new string in your new code, you probably want to use removeObject: instead of removeObjectIdenticalTo:. The removeObjectIdenticalTo: method uses object addresses to test for "identicalness," whereas removeObject: tests for equality using isEqual:. If you only care about the contents of the strings, use removeObject:.
In your old code, you probably inserted the same object into both tempDic and tempArrayOfAirports so the address check worked. This is not the case in your new code, in which you create a new string (at a new address) with stringWithFormat:.

Related

Accessing value from array of objects

I am having two arrays, Namely
NMutableArray* first;
NMutableArray* second;
Now I am copying first object to the second array like
for (int i=0;i<first.count; i++)
{
[second addObject:[first objectAtIndex:i];
}
This is ok. I don't know how to access the value of the First Array. I tried like this ,
[second addObject:[[first objectAtIndex:i]name]];
I want to get the name value which is in the first object of first array. I tried using the above line, it is showing some warning. Please help me
Assuming you started with an array like this:
NSArray *array1 = #[#{#name : #"Fred"},
#{#name : #"Bill"}];
You could create a second array that contains the value of a given property of each element of the first array as follows:
NSArray *array2 = [array1 valueForKey:#"name"];
If you then logged the second array...
NSLog(#"%#", array2);
...the resulting output would be
2012-04-18 16:26:11.226 ExampleRunner[23320:707] (
Fred,
Bill
)
EDIT
Note that this will work regardless of whether the objects in the first array are instances of NSDictionary as shown in the example above, or instances of a class or classes that have a name property or instance variable (or an _name instance variable, for that matter). For more information on how and why this works, see the documentation for the NSKeyValueCoding informal protocol:
http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Protocols/NSKeyValueCoding_Protocol/Reference/Reference.html
The brackets are currently in the wrong place:
[second addObject:[[first objectAtIndex:i] name]];
Updated Answer:
Again, I think you should split stuff out into easy to parse lines of code:
for (id theObject in first)
{
// without an actual type, I still think the compiler might
// throw a warning on this next line of code;
// but maybe RJR III is correct and it won't warn.
// I didn't check.
NSString * nameOfObject = [theObject name];
if(nameOfObject)
{
[second addObject:nameOfObject];
}
}
Notice that I do some error checking in here as well (i.e. making sure the name is not nil).
Original Answer:
You're getting a warning because the compiler doesn't know what kind of custom object is being fetched from your call to "[first objectAtIndex: i]". In other words, it doesn't know what kind of object you're trying to get the "name" of.
Cast it to the right type and you'll get rid of the warning.
Or even better, split that one line of multiple things happening at once into two or three lines of code and make your code more readable in the process.

Change the content of a string within an array of strings

This is my code:
NSString *newString = #"new value";
[breakdownCollection objectAtIndex:i] = newString;
breakdownCollection is an NSArray of multiple strings. I need to access a given string contained in the array via index number, and change the string's content to that of the new string. Note that I cannot simply replace the string with the new one, I am only trying to replace its contents.
When I try to do this, however, I get an "lvalue required as left operand of assignment" error.
Any help with this issue would be very much appreciated!
The error you get is because you wrote the assignement instruction incorrectly. That is, you cannot assign newString to [breakdownCollection objectAtIndex:i].
Also, you won't be able to do it this way. Instead, in order to modify string object content, use NSMutableString, which provides methods to do so (NSString are immutable objects).
So, for example you should try :
[[breakdownCollection objectAtIndex:i] setString:newString];
assuming you put NSMutableString into breakdownCollection.
PS : in order to change the object at the index i, you have to use NSMutableArray instead of NSArray, and then call :
[breakdownCollection replaceObjectAtIndex:i withObject:newString];
Good luck !
NSMutableString class reference
NSMutableArray class reference
Use an NSMutableArray instead and then you can use the method -replaceObjectAtIndex: withObject:

Objective-c pointers and NSString

If I have this code, why doesn't the textview's text update? As far as I knew a * meant a pointer, and I haven't done a copy.
NSString *searchText = myTextView.text;
searchText = [searchText stringByReplacingOccurrencesOfString:#" " withString:#";"];
So why isn't myTextView's text changed as if I did:
myTextView.text = [searchText stringByReplacingOccurrencesOfString:#" " withString:#";"];
And how would I write the code, so that the first code example works as I intend?
The method stringByReplacing... Doesn't change the string, it returns a new string object (autoreleased, according to the naming conventions). So after the 2nd line of code, searchText points to a totally differen NSString object.
Besides, NSString objects cannot be changed, for that there's NSMutableString
If you expect to modify myTextView.text, you have to write it like your second example, and assign a new value to the property you're trying to modify. Assigning a new value to some other variable or property won't do the job - "spooky action at a distance" may work when we eventually have quantum computing, but we're not there yet. :-)
To expand a bit: Yes, searchText is a pointer. But so is myTextView.text, and when you do "searchText = myTextView.text", you're not creating any sort of lasting relationship between the two - all you're doing is making searchText point to the same target as myTextView.text. Changing either one of them after that point will have no effect on the other. So, when you assign the result of stringByReplacing... to searchText, you're making it and only it point to a different target.
Your second example invokes the setter of the "text" property.
Your first example takes the pointer of the string, and then changes the pointer within the same scope. Hence, "text" is not changed.
BTW: Depending on how your property is defined, the setter you use will either copy, retain or assign the value you give the setter. So if you use the following:
#property(copy) NSString* text;
Then yes, the setter will copy the value you give it when you invoke:
myTextArea.text = //some string

iPhone, objective c reassign and return pointer of method

Hey guys, lately I have been asking quite a few questions about memory management on the iPhone. Fortunately things are getting clearer. But I still struggle when it gets more complex: So is there something wrong with this in terms of memory mangement? My question and suggestions are in the comments...
//I get a text from a textfield
NSString *text = [[NSString alloc]initWithString:txtField.text];
NSMutableString *newText = [self replaceDynamicRegex:text];
[text release];
...
//The method replaces regex it finds in the text. The regex part is just pseudo code
//and I just interested in memory management
-(NSMutableString*)replaceDynamicRegex:(NSString*)txt{
NSString *currentTag = [NSString stringWithString:#"dynamiclyCreatedTag"];
//As long as we find a particuar regex (just pseuo code here) we replace it
while (currentTag != NULL) {
if([html stringByMatching:openingTag] == NULL){
break;
}
//regular expression
currentTag = [NSString stringWithString:[html stringByMatching:theRegex]];
//Get rid of the useless part of the currentTag pseudo code
NSString *uselessTagPart = #"uselessRegex";
//Reassignment of the pointer currentTag --> ok to do this? cause I did not alloc]init]?
//and instead used stringWithString wich then gets autoreleased
currentTag = [currentTag stringByReplacingOccurrencesOfRegex:uselessTagPart withString:#""];
//Reassignment of the pointer html --> Ok to do this? cause it is just a pointer and the
//object is being released after the method call (further up)
html = (NSMutableString*)[html stringByReplacingOccurrencesOfRegex:currentTag withString:replacementTag];
}
//Do I need to autorelease this?
return html;
}
Your code looks correct memory-management-wise. Just remember, if you don't have call a method with alloc, new, retain, or copy in the method name, you don't have to worry about releasing.
One small point--your first 3 lines of code are redundant and inefficient. You shouldn't usually use initWithString--copy is usually a better choice when dealing with immutable objects, since behind the scenes a copy method can be replaced by a (less expensive) retain method. In your case, you don't even need to use copy--[self replaceDynamicRegex: txtField.text] will have the same result. Likewise, instead of [NSString stringWithString:[html stringByMatching:theRegex]], you can use simply use [html stringByMatching:theRegex] (since that method returns a new string).
Another note--html = (NSMutableString*)[html stringByReplacingOccurrencesOfRegex:currentTag withString:replacementTag] is incorrect. stringByReplacingOccurrencesOfRegex: returns an NSString, which can't be cast to an NSMutableString (you'll likely get a crash later on when you send a mutating method to the string). Instead, use [[html stringByReplacingOccurrencesOfRegex:currentTag withString:replacementTag] mutableCopy]
Generally, when you see a method named xWithY, you can assume the string will be autorelease-d.
Therefore, you probably do not need to autorelease the value returned from -stringByReplacingOccurrencesOfRegex:withString:.
The rest of your code looks okay, to me.

Detect if one position in Array is already initiated

I need to check specific positions in an NSArray to see if they have already been initialized, but I am having trouble. I tried to do the following, but it causes my application to crash!
if ((NSMutableArray *)[arrAllBlocks objectAtIndex:iLine] == nil)
{
[arrAllBlocks insertObject:[[NSMutableArray alloc] init] atIndex:iLine];
}
NSMutableArray *columArray = (NSMutableArray *)[arrAllBlocks
objectAtIndex:iLine];
[columArray insertObject:newBlock atIndex:iColumn];
What is the best to do this? I already tried some methods like isValid, and things like that!
You have a few options here:
Option 1: Pre-fill the array with instances of NSNull, and then use the code given by Dave DeLong in his answer.
Option 2: (Similar to #1) pre-fill the array with instances of NSMutableArray, and then have no extra code at all. (If you're going to pre-fill, you may as well do this).
Option 3: Do not pre-fill the array, but insert items dynamically as required. This will be almost identical to a pre-fill if the first iLine is near the maximum:
while([arrAllBlocks count] <= iLine)
{
[arrAllBlocks addObject:[NSMutableArray arrayWithCapacity:0]];
}
NSMutableArray *columArray = (NSMutableArray *)[arrAllBlocks
objectAtIndex:iLine];
[columArray insertObject:newBlock atIndex:iColumn];
Option 4: Use a dictionary to maintain the list of NSMutableArrays:
NSString *key = [NSString stringWithFormat:#"%d", iLine];
NSMutableArray *columnArray = [dictAllBlocks objectForKey:key];
if (columnArray == nil)
{
columnArray = [NSMutableArray arrayWithCapacity:0];
[dictAllBlocks setObject:columnArray forKey:key];
}
[columArray insertObject:newBlock atIndex:iColumn];
How to choose:
If the maximum value for iLine is not enormous, I would go with option #2. A handful of NSMutableArrays initialized to zero capacity will take up very little memory.
If the maximum value for iLine is enormous, but you expect it to be accessed sparsely (i.e., only a few values of iLine will ever be accessed), then you should go with Option #4. This will save you from having to fill an NSMutableArray with objects that never get used. The overhead of converting the string-value key for the dictionary will be less than the overhead for creating all of those blanks.
If you're not sure, try out each option and profile them: measure your memory usage and the time required to execute. If neither of these options work, you may have to explore more complex solutions, but only do that if it turns out to be necessary.
A note of caution:
The original code that you posted has a memory leak in the following line:
[arrAllBlocks insertObject:[[NSMutableArray alloc] init] atIndex:iLine];
The NSMutableArray objects that you initialize here are never released. When you call [[NSMutableArray init] alloc], a brand new object is created (with a reference count of one). The insertObject method then adds that new object to arrAllBlocks, and retains it (increasing its retain count to 2). Later, when you release arrAllBlocks, the new array will be sent a release message, but that will only reduce its retain count to one again. At that point, it will stick around in RAM until your program exits.
The best thing to do here is to use [NSMutableArray arrayWithCapacity:0] instead (as I have done in my examples). This returns a new NSMutableArray, just the same as your code did, but this instance has already been autoreleased. That way, arrAllBlocks can take ownership of the new object and you can be sure that it will be released when appropriate.
You can't. NSArray (and its subclass NSMutableArray) do not allow you to insert nil into the array. That's clearly outlined in the documentation.
If, for some reason, you need to have "empty" values in an array, then you should insert [NSNull null] instead and test for that. From the docs: "The NSNull class defines a singleton object used to represent null values in collection objects (which don’t allow nil values)."
UPDATE:
This means you could change your code very simply to this:
if ([[arrAllBlocks objectAtIndex:iLine] isEqual:[NSNull null]]) {
[(NSMutableArray *)arrAllBlocks insertObject:[NSMutableArray array] atIndex:iLine];
}
NSMutableArray *columnArray = (NSMutableArray *)[arrAllBlocks objectAtIndex:iLine];
[columnArray insertObject:newBlock atIndex:iColumn];
To check for NSNull you can simply compare against the pointer, since it's a Singleton:
if ([NSNull null] == [arrAllBlocks objectAtIndex:iLine]) {
[arrAllBlocks insertObject:[NSMutableArray array] atIndex:iLine];
}
NSMutableArray *columnArray = [arrAllBlocks objectAtIndex:iLine];
[columnArray insertObject:newBlock atIndex:iColumn];
I also removed the unsightly casts. Casting is rarely necessary in Objective-C. It usually just adds noise, and can hide real bugs. Since you're experiencing crashes, it's worth removing the casts from this code and listen to what the compiler has to tell you about it.
Telling the compiler to ignore warnings for a piece of code does not make the underlying problem with it go away!