Hey. I am reading in a string from a file and attempting to use the resulting string to set a BOOL property on an object using the KVC method -setValue:forKeyPath:. However, this breaks with an exception: -[NSCFString charValue]: unrecognized selector sent to instance 0x7fff711023b0. I'm guessing this is because BOOL is typedef'd from char. Is there a way around this? Thanks!
When setting a BOOL property using KVC, you need to pass an NSNumber object. What you could do in your case is pass [NSNumber numberWithBool:[myString boolValue]]. That should fix your crash.
I am catching the exception, checking it's name, and then retrying with a wrapped value when needed. Here is the code:
#try
{
[(NSObject*)retObj setValue:[[obj keyValuePairs] objectForKey:key]
forKeyPath:key];
}
#catch (NSException * e)
{
if ([[e name] isEqualToString:NSInvalidArgumentException])
{
NSNumber* boolVal = [NSNumber numberWithBool:[[[obj keyValuePairs] objectForKey:key] boolValue]];
[(NSObject*)retObj setValue:boolVal
forKeyPath:key];
}
}
Thanks anyway!
Add a simple category to your project:
#implementation NSString (CharValue)
- (BOOL)charValue {
return [self isEqualToString:#"0"] ? NO : YES;
}
#end
Related
My method returns a NSNumber* and I want to unit test this method. Since my actual return value is NSNumber*, I create a new expected value of NSNumber*, but it fails. Here is the code:
NSNumber *cRating = [movie getRating:ratingDictionary ratingType:criticRating];
XCTAssertEqualObjects(cRating, [[NSNumber alloc]initWithInt:70], #"");
The error is:
[SFModelTest testGetCriticRatingMethod] failed: ((cRating) equal to ([[NSNumber alloc]initWithInt:70])) failed: ("70") is not equal to ("70")
Since it is saying "70" is not equal to "70", I am guessing it has to do with alloc init. Some pointer stuff that is not equal. Can somebody please help? Thank you.
Edit for comment: adding getRating method
- (NSNumber *)getRating:(NSDictionary *)movieDic ratingType:(enum RatingsEnum) rating{
NSNumber *result = 0;
NSNumber *ratingNum = 0;
switch (rating) {
case userRating:
{
ratingNum = [movieDic objectForKey:#"audience_score"];
break;
}
case criticRating:
{
ratingNum = [movieDic objectForKey:#"critics_score"];
break;
}
default:
break;
}
if(ratingNum && ratingNum > 0)
{
result = ratingNum;
}
return result;
}
The method returns NSNumber. And my test is:
NSNumber *cRating = [movie getRating:ratingDictionary ratingType:criticRating];
XCTAssertEqualObjects(cRating, [[NSNumber alloc]initWithInt:70], #"");
When I do a class NSLOG, it returns __NSCFConstantString. I am confused now.
I think that you're doing something wrong because XCTAssertEqualObjects(#(1), #(1), #"Not equal."); or XCTAssertEqualObjects([NSNumber numberWithInt:1], [NSNumber numberWithInt:1], #"Not equal."); if you are not familiar with literals, will pass.
You should checkout the getRating:ratingType: method to see which type of object it returns.
Try adding a breakpoint just before that XCTAssertEqualObjects and inspect the cRating instance.
I am unable to save numeric values to coredata via a bound tableview and arrayController. I have changed the code to return hardcoded values, it doesn't crash when i try edit the numeric column but doesn't persist the hardcoded value:
#import "StringToNumberTransformer.h"
#implementation StringToNumberTransformer
+ (Class)transformedValueClass { return [NSNumber class]; }
+ (BOOL)allowsReverseTransformation { return YES; }
- (id)transformedValue:(id)value {
// return (value == nil) ? nil : [NSString stringWithFormat:#"%i", [value shortValue ]];
//return (value == nil) ? nil : NSStringFromClass([value class]);
return #"1";
}
- (id)reverseTransformedValue:(id)value {
// return [value numberFromString:[value stringValue]];
return [NSNumber numberWithInt:2];
}
#end
Here is the link to the xib setup ! http://tinypic.com/r/2nty41s/5 !
Can anyone shed some light on this?
Many thanks
As stated in the docs for Custom Value Transformers (Value Transformer Programming Guide)
A value transformer subclass must implement the transformedValueClass
class method. This method returns the class of the object that the
transformedValue: method returns.
Thus, your transformedValueClass returns the class for NSNumber, but your transformedValue: method returns an instance of type NSString
Since you're converting from an NSString to an NSNumber, I imagine you'll want to swap the implementations of your transformedValue: and reverseTransformedValue: methods.
Why when I use method respondsToSelector: or instancesRespondToSelector: at line 43 I cannot bypass STAssertTrue?
//My test case code
- (void)testApiClass {
//Check object
NSString* classKey = #"Api";
id obj = NSClassFromString(classKey);
STAssertNotNil(obj, [NSString stringWithFormat:#"Model '%#' not found.", classKey]);
//Check properties
NSArray* properties =
#[
#"performSyncRequestWithUri::",
#"performAsyncRequestWithUri:::",
];
for (NSString* property in properties) {
SEL propertySel = NSSelectorFromString(property);
BOOL isRespondsToSel = [obj respondsToSelector:propertySel];
STAssertTrue(isRespondsToSel, [NSString stringWithFormat:#"Property '%#' not found on object of class name '%#'", property, [obj class]]);
}
}
#interface Api : NSObject
- (NSDictionary*)performSyncRequestWithUri:(NSString *)requestUri params:(NSDictionary *)params;
- (void)performAsyncRequestWithUri:(NSString *)requestUri params:(NSDictionary *)params completionHandler:(void (^)(NSDictionary *, NSError *))completionBlock;
#end
The string constants in your properties array don't match the selectors in your Api interface.
Also, neither of those selectors refers to a property. A property has two selectors: a getter, like stringValue, which has no colons, and a setter, like setStringValue:, which has one colon and (usually) starts with set.
Instead of embedding your selectors in strings, make an array of selectors:
SEL selectors[] = {
#selector(performSyncRequestWithUri:params:),
#selector(performAsyncRequestWithUri:params:completionHandler:),
NULL
};
for (size_t i = 0; selectors[i]; ++i) {
SEL selector = selectors[i];
BOOL respondsToSelector = [obj respondsToSelector:selector];
STAssertTrue(respondsToSelector, [NSString stringWithFormat:
#"Object %# doesn't respond to selector %s",
obj, sel_getName(selector)]);
}
The advantages here are that Xcode will autocomplete the selectors for you, and you can command-click the selectors to jump to their definitions.
The methods are called performAsyncRequestWithUri:params:completionHandler: and performSyncRequestWithUri:params:
I have set up my simple Xcode project with a table that is binded to an array controller. It works fine if the array controller is full of entities with a string attribute. However I want to change the attribute to a BOOL and have the table show the string "true" or "false" based on the BOOL.
I have overrided the following two methods from NSFormatter:
-(NSString*) stringForObjectValue:(id)object {
//what is the object?
NSLog(#"object is: %#", object);
if(![object isKindOfClass: [ NSString class ] ] ) {
return nil;
}
//i'm tired....just output hello in the table!!
NSString *returnStr = [[NSString alloc] initWithFormat:#"hello"];
return returnStr;
}
-(BOOL)getObjectValue: (id*)object forString:string errorDescription:(NSString**)error {
if( object ) {
return YES;
}
return NO;
}
So the table gets populated with "hello" if the attribute is a string however if I switch it to a boolean, then the table gets populated with lots of blank spaces.
I don't know if this helps but on the line where I'm outputting the object, it outputs __NSCFString if the attribute is a string and "Text Cell" if I switch the attribute to a boolean. This is something else I don't understand.
Ok, it's not 100% clear what you're trying to do from the code, but first things first - BOOL is not an object, it's basically 0 or 1, so to place BOOL values into an array, you're probably best off using NSNumber:
NSNumber *boolValue = [NSNumber numberWithBool:YES];
and placing these into your array. Now you want to change your method:
-(NSString*) stringForObjectValue:(id)object {
NSNumber *number = (NSNumber *)object;
if ([number boolValue] == YES)
return #"true";
else
return #"false";
}
There's a few things here - for example, you want to avoid passing around id references if you can (if you know all your objects in the NSArray are NSNumber, you shouldn't need to).
I am following the Stanford CS193P class and I am trying to do assignment 2. I have now for hours stared at the same error message:
-[NSCFString stringValue]: unrecognized selector sent to instance 0x4b373e0,
while trying endless revisions of the code
The method running is this one:
+ (double)evaluateExpression:(id)anExpression
usingVariableValues:(NSDictionary *)variables;
{
CalculatorBrain *evalBrain =[[CalculatorBrain alloc]init];
for (id element in anExpression) {
if([element isKindOfClass:[NSString class]]) {
NSString *elementFirst=[element substringToIndex:1];
if ([elementFirst isEqual:#"v"])
{
NSString *varLookUp = [element substringFromIndex:0];
[evalBrain setOperand:[[variables objectForKey:varLookUp]doubleValue]];
}
else
{
[evalBrain performOperation:element];
}
}
if ([element isKindOfClass:[NSNumber class]]) {
[evalBrain setOperand: [element doubleValue]];
}
}
return [evalBrain operand];
}
it is at the statement
if ([elementFirst isEqual:#"v"])
the program crashes.
Grateful any hints!
Try
if ([elementFirst isEqualToString:#"v"])
instead.
Here is result of NSLog on elementfirst:
2011-02-06 16:00:11.554 Calculator[14160:207] elementfirst: +
it correctly shows that I hit the '+' key which is not equal to 'v' and the program should go to the else block. Unfortunately it crashes in stead...