For line Objective-C - objective-c

I'm making an iOS code interpreter. I have all the checking done but as of now you can only enter one command. I want the user to be able to enter multiple commands into a UITextView. What I plan do with the text view is pass each line the my IF statement line.
Does anyone know of a way to say, pass each line one by one into a if statement line?
- (IBAction)runCommad:(id)sender {
//Alert
NSString *alertCheck = #"alert(";
NSRange alertCheckRange = [code.text rangeOfString : alertCheck];
//Logger
NSString *logCheck = #"log(";
NSRange logCheckRange = [code.text rangeOfString : logCheck];
if (alertCheckRange.location != NSNotFound) {
//If the compiler sees alert()...
NSString *commandEdit;
commandEdit = code.text;
commandEdit = [commandEdit stringByReplacingOccurrencesOfString:#"alert(" withString:#""];
commandEdit = [commandEdit stringByReplacingOccurrencesOfString:#")" withString:#""];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Syn0" message:commandEdit delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}else if (logCheckRange.location != NSNotFound) {
//If the compiler sees log()...
NSString *commandEdit;
commandEdit = code.text;
commandEdit = [commandEdit stringByReplacingOccurrencesOfString:#"log(" withString:#""];
commandEdit = [commandEdit stringByReplacingOccurrencesOfString:#")" withString:#""];
logFile = [NSString stringWithFormat:#"%#\n%#", logFile,commandEdit];
logTextView.text = logFile;
}
}

First, get your string components to evaluate:
NSString *text = [textView text];
NSArray *components = [text componentsSeperatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
You can't use a switch with a string, so you'll need to check for each case with an if:
for (NSString *string in components)
{
if ([string isEqualToString:#"The first string you're matching"])
{
//Do something because you found first string
}
if([string isEqualToString:#"The second string you're matching"])
{
//Do something else
}
}
That's the idea.

Two suggestions, first if you are comfortable with blocks you can use NSString's:
(void)enumerateLinesUsingBlock:(void (^)(NSString *line, BOOL *stop))block
This method will call the block passing it each line in the original string in order. If you want to stop before processing each line you set stop to YES.
Alternatively you can use NSString's:
(NSArray *)componentsSeparatedByString:(NSString *)separator
This will break a string up into components based on separator. Use this in a for enumeration:
for(NSString *nextLine in [originalString componentsSeparatedByString:#"\n"])
{
// process nextLine, break from loop to stop before processing each line
}

Related

run applescript from cocoa app stopped working

This code had been working fine until just recently. I hadn't' changed anything nor upgraded my system and I'm completely flummoxed.
I've been using it for 6 years and now it dies on me.
Is there an easier or better way of running an applescript from within a cocoa application? At this point I'm happy to pay to fix this problem!
utils.h
#import <Foundation/Foundation.h>
#interface Utils : NSObject
// Runs an applescript with a given map of variables (name/value)
+ (NSArray *)runApplescript:(NSString *)source withVariables:(NSDictionary *)variables;
// Runs an applescript from a file pathwith a given map of variables
// (name/value)
+ (NSArray *)runApplescriptFromFile:(NSString *)scriptName withVariables:(NSDictionary *)variables;
+ (NSArray *)arrayFromDescriptor:(NSAppleEventDescriptor *)descriptor;
// String is empty or only has white characters (space, tab...)
+ (BOOL)stringIsEmptyOrWhite:(NSString *)string;
#end
Utils.M
#import "Utils.h"
#implementation Utils
+ (NSArray *)arrayFromDescriptor:(NSAppleEventDescriptor *)descriptor {
// Enumerate the apple descriptors (lists) returned by the applescript and
// make them into arrays
NSMutableArray *returnArray = [NSMutableArray array];
NSInteger counter, count = [descriptor numberOfItems];
for (counter = 1; counter <= count; counter++) {
NSAppleEventDescriptor *desc = [descriptor descriptorAtIndex:counter];
if (nil != [desc descriptorAtIndex:1]) {
[returnArray addObject:[Utils arrayFromDescriptor:desc]];
} else {
NSString *stringValue = [[descriptor descriptorAtIndex:counter] stringValue];
if (nil != stringValue) {
[returnArray addObject:stringValue];
} else {
[returnArray addObject:#""];
}
}
}
return returnArray;
}
+ (NSString *)escapeCharacters:(NSString *)string {
return [string stringByReplacingOccurrencesOfString:#"\"" withString:#"\\\""];
}
+ (NSArray *)runApplescript:(NSString *)source withVariables:(NSDictionary *)variables {
NSString *input = #"";
NSArray *variableNames = [variables allKeys];
// Transform the dictionary of names/values to set sentences of applescript
for (NSString *variableName in variableNames) {
NSObject *variableValue = [variables objectForKey:variableName];
if ([variableValue isKindOfClass:[NSString class]]) {
input =
[input stringByAppendingString:[NSString stringWithFormat:#"set %# to (\"%#\" as text)\n", variableName,
[Utils escapeCharacters:variableValue], nil]];
} else if ([variableValue isKindOfClass:[NSNumber class]]) {
input = [input stringByAppendingString:[NSString stringWithFormat:#"set %# to (%# as integer)\n",
variableName, variableValue, nil]];
} else if ([variableValue isKindOfClass:[NSArray class]]) {
// Initialize a list
NSString *entry;
NSArray *values = (NSArray *)variableValue;
input = [input stringByAppendingString:[NSString stringWithFormat:#"set %# to {", variableName]];
BOOL first = TRUE;
for (entry in values) {
if (!first) {
input = [input stringByAppendingString:#", "];
}
input = [input
stringByAppendingString:[NSString stringWithFormat:#"\"%#\"", [Utils escapeCharacters:entry], nil]];
first = FALSE;
}
input = [input stringByAppendingString:#"}\n"];
}
}
NSString *finalScript = [input stringByAppendingString:[NSString stringWithFormat:#"\n\n%#", source]];
NSLog(#"Final script: %#", finalScript);
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:finalScript];
NSDictionary *error;
NSAppleEventDescriptor *descriptor = [script executeAndReturnError:&error];
NSLog(#"applescript error: %#", [error description]);
// Transform the return value of applescript to nested nsarrays
return [Utils arrayFromDescriptor:descriptor];
}
+ (NSArray *)runApplescriptFromFile:(NSString *)scriptName withVariables:(NSDictionary *)variables {
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:scriptName ofType:#"applescript"];
NSString *scriptSource =
[[NSString alloc] initWithContentsOfFile:scriptPath encoding:NSASCIIStringEncoding error:nil];
return [Utils runApplescript:scriptSource withVariables:variables];
}
+ (BOOL)stringIsEmptyOrWhite:(NSString *)string {
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
return [string isEqualToString:#""];
}
#end
Easier, yes, although whether that’s your actual problem is another question.
http://appscript.sourceforge.net/asoc.html
I assume you’ve already got other details, including sandboxing and hardening settings and plist entries, taken care of. (Recent Xcode upgrades also had a habit of breaking it when auto-upgrading your project files, by turning on hardening for you so Apple events can’t get out.)

Why would subclassing NSFormatter prevent me from editing NSTextField's input?

I'm trying to format two NSTextFields for SMPTE time codes, which have the format:
HH:MM:SS:FF. However, when the user switches the SMPTE code to drop-frame, the delimiter between SS and FF needs to switch to a ; (HH:MM:SS;FF). To do this, I've subclassed NSFormatter, and have it mostly working except for one very stubborn problem.
The text field accepts input just fine, but if I highlight-replace, backspace, delete, or insert any new characters into the text field, I get an NSBeep and I can't switch focus away from the text field. I can input new text if I delete the whole text field first, but not if I try to edit the existing input. Here are my implemented methods/overrides:
- (NSString*)stringForObjectValue:(id)obj
{
if ( ! [obj isKindOfClass:[NSNumber class]])
{
return nil;
}
NSMutableString *string = [NSMutableString stringWithString:#"00:00:00:00"];
int length = (int)[[obj stringValue] length];
int insertLocation = 9;
if (length == 1)
{
[string replaceCharactersInRange:NSMakeRange(10, 1) withString:[obj stringValue]];
}
else
{
while (length > 1)
{
NSString *temp = [[obj stringValue] substringFromIndex:length-2];
[string replaceCharactersInRange:NSMakeRange(insertLocation, 2) withString:temp];
obj = [NSNumber numberWithInt:[obj intValue]/100];
length -= 2;
insertLocation -= 3;
}
if (length == 1)
{
[string replaceCharactersInRange:NSMakeRange(insertLocation+1, 1) withString:[obj stringValue]];
}
}
return string;
}
- (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error
{
int valueResult;
NSScanner *scanner;
BOOL returnValue = NO;
scanner = [NSScanner scannerWithString: string];
[scanner scanString:#":" intoString:NULL];
[scanner scanString:#";" intoString:NULL];
if ([scanner scanInt:&valueResult] && ([scanner isAtEnd])) {
returnValue = YES;
if (obj)
{
*obj = [NSNumber numberWithInt:valueResult];
}
}
return returnValue;
}
At least at this point, I don't need to validate the input during editing, only when editing is finished. I tried implementing isPartialStringValid and just returning YES, but that didn't seem to help either. Any help would be greatly appreciated!
Ok, I just solved it by doing some more testing. It appears as though why it was failing is because getObjectValue was receiving the string with the delimiters in them and was not correctly removing them. I simply replaced the method with this:
- (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error
{
NSString *newString = [string stringByReplacingOccurrencesOfString:#":" withString:#""];
if (obj)
{
*obj = [NSNumber numberWithInt:[newString intValue]];
return YES;
}
return NO;
}
Now it works perfectly.

Accessing a specific string using rangeOfString in Objective C

I'm using NSURLConnection to pull data from a webpage. I'm looking to find a specific line of text to display in a basic app. I've converted my NSData to an NSString. The program successfully locates the string I'm looking for:
#"Most recent instantaneous value:
However, I need to actually pull and store the string that follows "instantaneous value: myString "
I'm noob, so I'm stuck. Here's my code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString:#"http://waterdata.usgs.gov/ga/nwis/uv?cb_72036=on&cb_00062=on&format=gif_default&period=1&site_no=02334400"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if (connection) {
// Connect
label.text = #"Connecting...";
myData = [[NSMutableData alloc] init];
} else {
// Error
}
}
- (void)connection:(NSURLConnection *) connection didReceiveData:(NSData *)data
{
[myData appendData:data];
}
-(void)connectionDidFinishLoading: (NSURLConnection *)connection {
response = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
label.text = response;
NSString *string1 = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
if ([string1 rangeOfString:#"Most recent instantaneous value: "].location == NSNotFound){
NSLog(#"Not found");
} else
{
NSLog(#"Found");
}
connection = nil;
}
You already have half the solution:
NSRange range = [string1 rangeOfString:#"Most recent instantaneous value: "];
if (range.location == NSNotFound) {
NSLog(#"Not found");
} else {
NSLog(#"Found");
// Method 1
NSString *string2 = [string1 stringByReplacingCharactersInRange:range withString:#""];
NSLog(#"%#", string2);
// Method 2
NSString *string3 = [string1 substringFromIndex:range.location+range.length];
NSLog(#"%#", string3);
}
Both methods produce the result you are looking for.
This depends no the format of what follows. You can easily use a regular expression to extract this value if it has a delimiter. For example, let's say it looks like this:
...Most recent instantaneous value: 74219MoredataBlahBLahBlah
This would be extracted using the regular expression "Most recent instantaneous value: ([0-9]+)"
However, if it is something like this
...Most recent instantaneous value: MyValueMoredataBlahBLahBlah
Then you are pretty much out of luck unless it is the same size each time
If it is like this
......Most recent instantaneous value: MyValue MoredataBlahBLahBlah
Then you can get it like this "...Most recent instantaneous value: ([A-Z,a-z]+ )" (note the space at the end inside the parenthesis).
Could you perhaps tell a bit more about the format of your value?
EDIT:
Since you know the length of your string, just do as shown above and when you get here:
NSString *restOfString = [string1 substringFromIndex:theRange.location+theRange.length];
Continue with this: NSString *cutString = [restOfString substringToIndex:8];
A good general tip for this sort of problem is to look at the documented methods in the NSString Class Reference. For example, the section under "Dividing Strings" has a few options for you, including
– substringFromIndex:
Your code might go something like this
NSString *string1 = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
NSRange theRange = [string1 rangeOfString:#"Most recent instantaneous value: "];
if (theRange.location == NSNotFound){
NSLog(#"Not found");
} else
{
NSString *restOfString = [string1 substringFromIndex:theRange.location+theRange.length];
}
ComponentSeparatedBy ":" should work.

Objective-C Bad exception dropbox

I've got a question. Where is the reason for EXC_BAD_ACCESS in the following code ?
-(void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
if(metadata.isDirectory) {
db_Path = metadata.path;
int i = 0;
NSString *fileName = [[NSString alloc] init];
for(DBMetadata *file in metadata.contents) {
fileName = [NSString stringWithFormat:#"%#", file.filename];
[db_MetaFileNames addObject:file.filename];
i++;
}
[self createMetaListArray];
[fileName release];
}
}
-(void)createMetaListArray {
fileNamesAtDirectory = db_MetaFileNames;
for (int i=0; i < [fileNamesAtDirectory count]; i++) {
NSString *filePathWithName = db_directory;
[filePathWithName stringByAppendingFormat:
[fileNamesAtDirectory objectAtIndex:i]];
[filePathsAtDirectory addObject:filePathWithName];
[filePathWithName release];
}
}
Can Anyone here help me ?
Here:
NSString *fileName = [[NSString alloc] init];
for(DBMetadata *file in metadata.contents) {
fileName = [NSString stringWithFormat:#"%#", file.filename];
The NSString on the first line gets overwritten with the new values on the third line. The original value leaks.
This means that:
}
[self createMetaListArray];
[fileName release];
The release on the last line releases not the fileName that you alloc/init above, but the assignment inside the loop. You don't alloc/copy/retain that, so you're not "in charge" of releasing it.
You have a similar misunderstanding in the second function.
[filePathWithName stringByAppendingFormat:[fileNamesAtDirectory objectAtIndex:i]];
This does not amend filePathWithName. It returns a new string.
I suggest you read up on Cocoa's memory management rules -- you're missing some fundamentals. Understanding those will make your life a lot easier.

Issue with NSString memory management

I have developed in C but am quite new to Objective-C and iPhone app development. I am working on an app that needs to strip the punctuation off a string. The function works but when I analyse the code it flags up some issues around one of the NSstrings I am using.
I don't understand why and therefore don't know how to fix it.
The code for the main function along with the analyser warning is:
- (IBAction)doIt {
NSString *start_punct = [[NSString alloc] init];
NSString *end_punct = [[NSString alloc] init];
NSString *actual_word = [[NSString alloc] init];
outputTextTextView.text = translatedText; //potential leak of an object alloctated on line xx and stored into 'actual word'
[translatedText release]; translatedText = nil;
[start_punct release]; start_punct = nil; //incorrect decrement of reference count of an object that is not owned at this point by the caller
[end_punct release]; end_punct = nil;
[actual_word release]; actual_word = nil; //this causes a crash
start_punct = [MainViewController getStartPunct:word start:&start_range_start len:&start_range_len];
end_punct = [MainViewController getEndPunct:word start:&end_range_start len:&end_range_len];
actual_word = [word substringWithRange: NSMakeRange(start_range_start,(end_range_start-start_range_start)+1)];
}
The code for the getStartPunct and getEndPunct functions is below
+(NSString*) getStartPunct:(NSString*) inputString
start:(NSInteger*)rangeStart
len:(NSInteger*)length {
NSString* start_str = nil;
NSRange firstAlphanumCharFromStart = [inputString rangeOfCharacterFromSet:[NSCharacterSet alphanumericCharacterSet]];
if (firstAlphanumCharFromStart.location != NSNotFound) {
start_str = [inputString substringWithRange: NSMakeRange(0, firstAlphanumCharFromStart.location)];
*length = firstAlphanumCharFromStart.length;
*rangeStart = firstAlphanumCharFromStart.location;
} //if
if (start_str == nil) {
*length=0;
*rangeStart=0;
}
return start_str;
} //getStartPunct
+(NSString*) getEndPunct:(NSString*) inputString
start:(NSInteger*)rangeStart
len:(NSInteger*)length {
NSString* end_str = nil;
NSInteger rnge = inputString.length;
NSCharacterSet* CS = [NSCharacterSet alphanumericCharacterSet];
NSRange firstNonAlphanumCharFromEnd = [inputString rangeOfCharacterFromSet:CS options:NSBackwardsSearch];
if (firstNonAlphanumCharFromEnd.location != NSNotFound) {
end_str = [inputString substringWithRange: NSMakeRange(firstNonAlphanumCharFromEnd.location+1, rnge - firstNonAlphanumCharFromEnd.location-1)];
*length = firstNonAlphanumCharFromEnd.length;
*rangeStart = firstNonAlphanumCharFromEnd.location;
} //if
if (end_str == nil) {
*length=0;
*rangeStart=0;
}
return end_str;
} //getEndPunct
Can someone see what the issue is? I'm sure it is something very basic..
Many Thanks in advance!
Thanks for all the responses so far.
adpalumbo you are right, I had paste the elements in the wrong order. The correct order is below and I have changed the initialization as suggested by Alex Nichol.
This has fixed 1 of the warning but the others (as shown below) still remain and I don't understand why 'start_punct' and 'end_punct' are behaving differently
- (IBAction)doIt {
NSString *start_punct = nil;
NSString *end_punct = nil;
NSString *actual_word = nil;
start_punct = [MainViewController getStartPunct:word start:&start_range_start len:&start_range_len]; // method returns objective with +0 retain count
end_punct = [MainViewController getEndPunct:word start:&end_range_start len:&end_range_len];
actual_word = [word substringWithRange: NSMakeRange(start_range_start,(end_range_start-start_range_start)+1)];
[translatedText release]; translatedText = nil;
[start_punct release]; start_punct = nil; //incorrect decrement of reference count
[end_punct release]; end_punct = nil;
//[actual_word release]; actual_word = nil; //possible abend
}