I'm trying to follow a Dave DeLong blog post here.
We construct a category on NSNumber to compute the factorial. It seems to work fine, but when I wrap it up into an NSExpression and try to evaluate the expression, I get
[NSCFNumber factorial:]: unrecognized selector sent to instance 0x100108d40'
But the object at that address is the NSNumber, which does recognize that selector.
I'm stumped.
#import <Foundation/Foundation.h>
#interface NSNumber (FactorialExpression)
- (NSNumber *) factorial;
#end
#implementation NSNumber (FactorialExpression)
- (NSNumber *) factorial {
double baseValue = [self doubleValue];
double result = tgamma(baseValue+1);
return [NSNumber numberWithDouble:result];
}
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSNumber *n = [NSNumber numberWithDouble:4.2];
NSLog(#"%# %#", n, [n factorial]);
NSLog(#"%p %d", n, [n respondsToSelector:#selector(factorial)]);
NSExpression *f = [NSExpression expressionForConstantValue:n];
NSExpression *e = [NSExpression expressionForFunction:f
selectorName:#"factorial:"
arguments:nil];
NSLog(#"operand %# %#", [e operand], [[e operand] class]);
NSLog(#"operand %#", [e function]);
id result = [e expressionValueWithObject:nil context:nil];
//NSLog(#"%# %#", [result description], [result class]);
[pool drain];
return 0;
}
2011-03-13 10:09:02.312 test[94896:903] 4.2 32.57809605033135
2011-03-13 10:09:02.314 test[94896:903] 0x100108d40 1
2011-03-13 10:09:02.315 test[94896:903] operand 4.2 NSConstantValueExpression
2011-03-13 10:09:02.316 test[94896:903] operand factorial:
2011-03-13 10:09:02.316 test[94896:903] -[NSCFNumber factorial:]: unrecognized selector sent to instance 0x100108d40
What am I not understanding about this? Thanks.
It's embarassing. A stupid typo. Sorry guys.
NSExpression *e = [NSExpression expressionForFunction:f selectorName:#"factorial:" arguments:nil];
The selector name should not have a colon at the end.
I had a problem with categories, but I was testing a static library. So I had to add -ObjC linker flag to the project.
Related
I'm currently pulling info from an sql DB where the 'cachedDist' column is set as a double. However when I pull it into my app and create my array I turn it into an String and the sort will obviously be off, 18.15 will come before 2.15. How do I fix that in my code so it will sort distance as a Double and not a String?
In Bar object.
NSString *cachedDist
#property(nonatomic,copy) NSString *cachedDist;
#synthesize cachedDist;
My while loop in the View Controller.
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
Bar * bar = [[Bar alloc] init];
bar.barName = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,1)];
bar.barAddress = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,2)];
bar.barCity = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 3)];
bar.barState = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 4)];
bar.barZip = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 5)];
bar.barLat = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 8)];
bar.barLong = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 9)];
if (currentLoc == nil) {
NSLog(#"current location is nil %#", currentLoc);
}else{
CLLocation *barLocation = [[CLLocation alloc] initWithLatitude:[bar.barLat doubleValue] longitude:[bar.barLong doubleValue]];
bar.cachedDist = [NSNumber numberWithDouble:[currentLoc distanceFromLocation: barLocation]/1000];
[thebars addObject:bar];
}
My sorting
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"cachedDist" ascending:YES];
sortedArray = [thebars sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
return sortedArray;
NSString has a method doubleValue to make this quite simple:
double cachedDistance = [cachedDistanceString doubleValue];
which you can use in a custom comparator for your sorting, or else make the property an NSNumber or double to make sorting that much easier. (I'm not sure how you are sorting...)
edit:
I re-evaluated your code, and now it looks like we are going from a double to a string to a double... we can cut out the middle-man, so to speak.
In your #prototype section, change the #property:
// #property(nonatomic,copy) NSString *cachedDist; // old way
#property(nonatomic) double cachedDist;
then assign it like this:
bar.cachedDistance = [currentLoc distanceFromLocation: barLocation]/1000;
and remove the lines which create a string from the distance (which is actually just a double).
Alternatively, if you want to be more object oriented, you can (should?) use NSNumber objects:
#property(nonatomic,copy) NSNumber *cachedDist;
...
bar.cachedDistance = [NSNumber numberWithDouble:[currentLoc distanceFromLocation: barLocation]/1000];
What is the appropriate way to call a method with an action, and what should the method itself look like for passing a CGPoint parameter? I've tried to look up examples online without much luck, so I've been pretty much guessing.
What I have tried is this for calling it:
CGPoint spriteCoord = saveStation.sprite.position;
id a1=[CCMoveTo actionWithDuration:.4 position:ccp(saveStation.sprite.position.x,saveStation.sprite.position.y)];
id actionSaveStationReaction = [CCCallFuncND actionWithTarget:self selector:#selector(saveStationReaction : data:) data:&spriteCoord];
[hero.heroSprite runAction:[CCSequence actions:a1, actionSaveStationReaction, nil]];
And the method itself:
-(void) saveStationReaction:(id)sender data:(void *)data {
CGPoint spriteCoord = (void *)data; //error: Invalid initializer
NSLog(#"spriteCoord x = %f", spriteCoord.x);
NSLog(#"spriteCoord y = %f", spriteCoord.y);
}
The proper way to send a CGPoint (or any non-id type like C structs) to a method that takes an id as parameter (any method that uses performSelector) is by wrapping it in an NSValue object:
NSValue* value = [NSValue valueWithBytes:&spriteCoord objCType:#encode(CGPoint)];
In the method that is being called you can retrieve the point from the NSValue object by casting the data pointer to NSValue* and calling getValue:
-(void) saveStationReaction:(id)sender data:(void *)data {
CGPoint spriteCoord;
[((NSValue*)data) getValue:&spriteCoord];
NSLog(#"spriteCoord x = %f", spriteCoord.x);
NSLog(#"spriteCoord y = %f", spriteCoord.y);
}
GamingHorror's suggestion on wrapping the CGPoint in an NSValue is spot-on.
But there's a simpler way than using valueWithByte:objCType: method: valueWithCGPoint:, assuming you are coding for iOS and not MacOS.
NSValue *spriteCoordValue = [NSValue valueWithCGPoint:spriteCoord];
[CCCallFuncND actionWithTarget:self selector:#selector(saveStationReaction:data:) data:spriteCoordValue];
// and ..
-(void) saveStationReaction:(id)sender data:(void *)data {
CGPoint spriteCoord = [(NSValue *)data CGPointValue];
NSLog(#"spriteCoord x = %f", spriteCoord.x);
NSLog(#"spriteCoord y = %f", spriteCoord.y);
}
NSValue can also deal with CGSize and CGRect using similar way.
You can't typecast to a CGPoint with that syntax. Try....
CGPoint *spriteCoord = data;
CGFloat ptX = spriteCoord->x;
CGFloat ptY = spriteCoord->y;
I tried...
CGPoint* spriteCoord = (CGPoint)data;
which didn't work and I guess expectedly so. Try my first suggestion and see if that works for you. It did compile for me but I'm not sure how it will execute and that may depend on your particular situation.
CGPoint is a struct, not an object, so you can't pass it directly to any of the CCCallFunc's. There are several ways of dealing with this, but the quickest converts the CGPoint to NSString using NSStringFromCGPoint, passing the string, then converting it back to a CGPoint using CGPointFromString.
I am getting random malloc crashes in stringByReplacingOccurrencesOfString. I've noticed that it crashes on longer strings, but I can't seem to find why. What could be the problem?
The error:
CashTrader(53448,0xb0103000) malloc: *** error for object 0x5c5eca0: incorrect checksum for freed object - object was probably modified after being freed.
Input encryptedparams
raStwjnw9uiOEHzF00UazOUp879zUuLwJ6J300BH2DMH29Pww/4mOR3oHXv4F/CL
Sample Code:
-(NSURL *)createReqUrl:(NSString *)hostString secure:(BOOL)usessl urlRoot:(NSString*)urlRoot encryptedParam:(NSString *)encryptedparams{
NSString *encryptString;
encryptString = nil;
encryptString = [encryptedparams stringByReplacingOccurrencesOfString:#"+" withString:#"%%2b"];
encryptString = [encryptString stringByReplacingOccurrencesOfString:#"/" withString:#"%%2f"];
encryptString = [encryptString stringByReplacingOccurrencesOfString:#"=" withString:#"%%3d"];
encryptString = [encryptString stringByReplacingOccurrencesOfString:#"#" withString:#"%%40"];
NSString *answer = [[NSString alloc] initWithString:urlRoot];
answer = [[answer stringByAppendingString:encryptString] stringByReplacingOccurrencesOfString:#"%%" withString:#"%"];
NSString *scheme = nil;
if (usessl)
scheme = #"https://";
else
scheme = #"http://";
return [[NSURL alloc] initWithString:[[scheme stringByAppendingString:hostString] stringByAppendingString:answer]];;
}
I do believe your encryptString should be NSMutableString.
(have searched, but not been able to find a simple solution to this one either here, or in Cocoa docs)
Q. How can I trim all leading whitespace only from an NSString? (i.e. leaving any other whitespace intact.)
Unfortunately, for my purposes, NSString's stringByTrimmingCharactersInSet method works on both leading and trailing.
Mac OS X 10.4 compatibility needed, manual GC.
This creates an NSString category to do what you need. With this, you can call NSString *newString = [mystring stringByTrimmingLeadingWhitespace]; to get a copy minus leading whitespace. (Code is untested, may require some minor debugging.)
#interface NSString (trimLeadingWhitespace)
-(NSString*)stringByTrimmingLeadingWhitespace;
#end
#implementation NSString (trimLeadingWhitespace)
-(NSString*)stringByTrimmingLeadingWhitespace {
NSInteger i = 0;
while ((i < [self length])
&& [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[self characterAtIndex:i]]) {
i++;
}
return [self substringFromIndex:i];
}
#end
This is another solution using Regular Expressions (requires iOS 3.2):
NSRange range = [string rangeOfString:#"^\\s*" options:NSRegularExpressionSearch];
NSString *result = [string stringByReplacingCharactersInRange:range withString:#""];
And if you want to trim the trailing whitespaces only you can use #"\\s*$" instead.
This code is taking blanks.
NSString *trimmedText = [strResult stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#",trimmedText);
Here is a very efficient (uses CoreFoundation) way of doing it (Taken from kissxml):
- (NSString *)trimWhitespace {
NSMutableString *mStr = [self mutableCopy];
CFStringTrimWhitespace((CFMutableStringRef)mStr);
NSString *result = [mStr copy];
[mStr release];
return [result autorelease];
}
NSString *myText = #" foo ";
NSString *trimmedText = [myText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSLog(#"old = [%#], trimmed = [%#]", myText, trimmedText);
Here's what I would do, and it doesn't involve categories!
NSString* outputString = inputString;
NSRange range = [inputString rangeOfCharacterFromSet: [NSCharacterSet whitespaceCharacterSet]
options:0];
if (range.location == 0)
outputString = [inputString substringFromIndex: range.location + range.length];
This is much less code.
I didn't really have much time to test this, and I'm not sure if 10.4 contains the UTF8String method for NSString, but here's how I'd do it:
NSString+Trimming.h
#import <Foundation/Foundation.h>
#interface NSString (Trimming)
-(NSString *) stringByTrimmingWhitespaceFromFront;
#end
NSString+Trimming.m
#import "NSString+Trimming.h"
#implementation NSString (Trimming)
-(NSString *) stringByTrimmingWhitespaceFromFront
{
const char *cStringValue = [self UTF8String];
int i;
for (i = 0; cStringValue[i] != '\0' && isspace(cStringValue[i]); i++);
return [self substringFromIndex:i];
}
#end
It may not be the most efficient way of doing this but it should work.
str = [str stringByReplacingOccurrencesOfString:#" " withString:#""];
When i try and compile I come up with a warning that reads initialization makes pointer from integer without a cast. No clue why. I am just trying to get the size of a website.
#import "Lockerz_RedemptionViewController.h"
#implementation Lockerz_RedemptionViewController
-(IBAction)startLoop:(id) sender {
NSData *dataNew = [NSData dataWithData:[NSData dataWithContentsOfURL:[NSURL
URLWithString:#"http://www.google.com/"]]];
NSUInteger *len = [dataNew length]; //error is here
NSLog(#"%#", len);
}
NSUInteger is just a wrapper for an unsigned int, alter your code to this (i.e. remove the * as it's not a pointer to an object)
NSUInteger len = [dataNew length];
Also I think you're going a bit overboard with your initialisation, why not just do
NSData *dataNew = [NSData dataWithContentsOfURL:[NSURL
URLWithString:#"http://www.google.com/"]];
That should return you an autoreleased object containing the data you need