[NSCFString stringValue]: unrecognized selector sent to instance - iphone

I'm using this code to query core data and return the value of key, I store the value like this :
NSString *newName= #"test";
[newShot setValue:newName forKey:#"shotNumber"];
and I query like this :
NSManagedObject *mo = [items objectAtIndex:0]; // assuming that array is not empty
NSString *value = [[mo valueForKey:#"shotNumber"] stringValue];
NSLog(#"Value : %#",value);
I'm crashing with this message though :
[NSCFString stringValue]: unrecognized selector sent to instance,
does anyone know where that would be coming from ?

newName (#"test") is already an NSString. There is no need to call -stringValue to convert it to a string.
NSString *value = [mo valueForKey:#"shotNumber"];

I often times add a category for NSString to handle this:
#interface NSString(JB)
-(NSString *) stringValue;
#end
#implementation NSString(JB)
-(NSString *) stringValue {
return self;
}
#end
You can add a similar category to other classes that you want to respond this way.

[mo valueForKey: #"shotNumber"] is returning a string and NSString (of which NSCFString is an implementation detail) do not implement a stringValue method.
Given that NSNumber does implement stringValue, I'd bet you put an NSString into mo when you thought you were putting in an NSNumber.

The value for the key #"shotNumber" is probably of type NSString which is just a wrapper for NSCFString. What you need to do, is, instead of stringValue, use the description method.

Note that you could also get this problem if you are trying to access a string property on an object that you think is something else, but is actually a string.
In my case I thought my Hydration object was in fact a hydration, but checking its class via isKindOfClass I found that it was an NSString and realized that I had incorrectly cast it as a Hydration object and that my problem lied further up the chain.

Related

Overriding description method for NSObject

I'm not sure if this is a simulator issue, but I don't remeber having this problem before when I was using the iPad 5.0 simulator and below (now I'm running iPad 5.1 simulator). I overrode the description method for my Condition object to be:
- (NSString *)description {
NSString *str = [[NSString alloc] initWithFormat:#"Condition: %#", _conditionName];
return [str autorelease];
}
I have an array of these objects. My values are all valid. When I do:
for (Condition *p in self.reportsArray) {
NSLog(#"%#", [p description]);
}
It logs all my values, and then it crashes at the end. When I look at Instruments with Zombies, the last 4 calls are
-[NSPlaceHolderString initWithBytes:length:encoding:]
+[NSString stringWithUTF8String:]
-[NSAutoreleasePool release]
-[NSPlaceholderString initWithFormat:locale:arguments:]
Am I overriding description correctly?
Edit:
In Instruments, I get: message was sent to a deallocated object (zombie) at address:0x8ccf190. On the app itself, I get EXC_BAD_ACCESS.
It seems like the string returned from your description method is being released too soon.
Try rewriting your method using the stringWithFormat: class method.
- (NSString *)description {
return [NSString stringWithFormat:#"Condition: %#", _conditionName];
}
Make sure _conditionName is not a primitive.
Because the format string "%#" expects an object.

CoreData expects a string when returning an array and NSData when returning a string

I have a simple NSValueTransformer that should convert a comma-separated string first, second, third into an array. So in my CoreData schema I have the attribute options which I set to Transformed and specify my Transformer.
This is my Transformer:
-(id)transformedValue:(id)value{
// convert it to an array
if(!value){
return nil;
} else{
NSString *languages = value;
NSArray *result = [languages componentsSeparatedByString: #", "];
// return result;
return #"test Result";
}
}
Now when I return the variable result I get the following error (length looks like it's expecting an NSString):
... 'NSInvalidArgumentException', reason: '-[__NSArrayI length]: unrecognized selector sent to instance 0x6cc59a0'
If I return the test string above instead I get this error (bytes looks like it's expecting an NSData object):
... 'NSInvalidArgumentException', reason: '-[__NSCFConstantString bytes]: unrecognized selector sent to instance 0xf0780'
It looks like there is a conceptual problem which I don't seem to grasp. Also curiously +(Class)transformedValueClass and +(BOOL)allowsReverseTransformation are never "touched" when using the Debugger.
There are no errors in code you provide. The problem lays in your CoreData approach.
When you use transformed property in CoreData, the value is storing in database as NSData object. And length in your errors is related to NSData length method.
Therefore you should provide NSValueTransformer with transformation from NSData to NSArray and reverse to put concatenated arrays to CoreData as NSData.
If you want to keep the possibility to store property as NSString, you can implement special accessor to NSString property of your managed object, which will transform the NSString to NSArray value or use your NSValueTransformer outside your CoreData model like this:
NSValueTransformer *transformer = [NSValueTransformer valueTransformerForName:#"StringToArrayValueTransformer"];
NSArray *array = [transformer transformedValue:yourEntityItem.yourStringProperty];
The value needs to be cast to a NSString try this
-(id)transformedValue:(id)value{
// convert it to an array
if(!value){
return nil;
} else{
NSString *languages = (NSString*)value;
NSArray *result = [languages componentsSeparatedByString: #", "];
return result;
}
}

Should I release #property (nonatomic, copy)?

I'm using UISearchBar, and one of its properties, text, is declared as follows:
Abstract: The current or starting search text.
Declaration: #property(nonatomic, copy) NSString *text;
I know that the rule is to release what ever you use +alloc, +new or -copy.
I did:
NSString *searchText = searchBar.text;
And:
[searchText release];
And I got a nice EXC_BAD_ACCESS message. When I removed the release line, the EXC_BAD_ACCESS message stopped to appear, so I assumed that it is the eror source.
The question: Shouldn't I release searchText, since it comes from a property that uses copy?
No, you should not use release here. The "copy" in this case refers to how the setter is implemented, not the getter. The call you made (-text) does not include the word "copy" so you should not release the result.
The copy attribute of the property means that the object is copied before assigning to the instance variable. When you access this property you then get a reference to the copy that was made.
When you set the the text on the searchbar:
NSString* myTextString = [[NSString alloc] initWithString:#"My Text String"];
mySearchBar.text = myTextString;
[myTextString release];
To elaborate on Rob Napier's correct answer:
NSString *searchText = searchBar.text;
This code assigns a reference to the text property of searchBar to searchText. This is not a copy of the searchText, just another reference to the same NSString object in the searchBar object. Releasing searchText is the same as releasing searchBar.text, which cause your EXC_BAD_ACCESS message.
In this declaration of the text property, the getter method is merely:
- (NSString *)text {
return text;
}
The more interesting method is the setter method. For this declaration, the setter is similar to:
- (Void)setText:(NSString *)newString {
if (text != newString) {
[text release];
text = [newString copy];
}
}

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.

How to transfer values between classes on iPhone?

I want to send a string from one class to the other:
1) In class1, I add a property to hold the string:
#property (nonatomic, retain) NSString *str;
2) and a method to send back a string:
-(NSString *)sendBackStr:(NSString *)strURL
{
NSString *str = [[NSString alloc] stringWithString:strURL];
return str;
}
3) In class2, I add a property to hold the received string:
#property (nonatomic, retain) NSString *returnStr;
4) and the following code:
Class1 *c1 = [[Class1 alloc] init];
returnStr = [c1 sendBackStr:#"URL"];
But the program stops at returnStr = [c1 sendBackStr:#"URL"]; Any ideas about what's wrong with it?
stringWithString is a class method returning an autoreleased string. You should be calling it like this:
myProperty = [NSString stringWithString:strURL];
Here I assume your property does a copy, to increment the retain count on the autoreleased string that's returned from the stringWithString method. (Objects returned from alloc calls have a retain count of one and are not autoreleased.) It's more usual to give strings the copy property rather than the retain one - you usually just want your own copy of a string, not a shared reference to a string owned by someone else.
What I also can't understand is why you've written the code like this, unless it's just an example. In class 2, all you need to do is write
returnStr = [NSString stringWithString:#"URL"];
stringWithString: is a message that needs to be sent to the NSString class, not an instance of your class (returned via alloc).
The correct code should be:
-(NSString *)sendBackStr:(NSString *)strURL
{
return [NSString stringWithString:strURL];
}
You might want to familarize yourself more about the idioms around allocation, retention, and autoreleasing of pointers. If you wanted to alloc this string for some reason and return it from the sendBackStr: message, then you would probably want this code:
-(NSString *)sendBackStr:(NSString *)strURL
{
return [[[NSString alloc] initWithString:strURL] autorelease];
}