iOS initializing any variable crashes the app - objective-c

When I try to initialize any variable, my app crashes
NSString *str = #"Some String";
It crashes with the error message - "EXC_BAD_ACCESS"
This stmt is inside a function. Everywhere else in the program the initialization is working but when i call this function, my app crashes giving the error message
Here is my function
+ (NSString *) recvToFile:(NSString *)_fileName {
#try {
int _sz = [self recvNumber:4];
uint8_t t[_sz];
NSMutableData *data = nil;//[[NSMutableData alloc] init];
NSMutableData *fileData = [[NSMutableData alloc] init];
NSString *str = #"Some String";
long _pos = 0;
NSString *_fullPath = _fileName;
while (_sz > _pos) {
long _c = [m_sin read:t maxLength:_sz];
_pos += _c;
data = [NSData dataWithBytes:t length:_c];
if([Misc checkTempFileExists:_fileName]==nil)
[[NSFileManager defaultManager] createFileAtPath:_fullPath contents:nil attributes:nil];
[fileData appendData:data];
}
[fileData writeToFile:_fullPath atomically:YES];
NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:_fullPath error:nil];
long long length = [[attr valueForKey:#"NSFileSize"] intValue];
if (length >= _sz)
return (_fullPath);
}
#catch (NSException * e) {
}
return (nil);
}
Everywhere else initializing is working but not for this function

In your code you are using class method and in that method you are initializing nonstatic string, i think tats the problem.Instead of that Can you please check it with static NSString=#"Some String"; after the import files in .m file.The same way you can create fullpath string also.Then u can assign like fullpath=_fileName;#aswinikumar..sorry for my english

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.)

Inputstring is null after being set in a static method, why?

I am uncertain of how memory is managed in my particular case...
I have two methods:
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath
{
NSString * destPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
destPath = [destPath stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.plist", name]];
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
{
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"plist"] toPath:destPath error:nil];
}
plistPath = [NSMutableString stringWithString:[destPath copy]];
NSData * plistXML =
[[NSFileManager defaultManager] contentsAtPath:plistPath];
NSLog(#"AFTER plistPath: \n%#",plistPath);
return
(NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:format
errorDescription:&errorDesc];
}
+(bool)writeToCache:(NSString*) data andField: (NSString*) field
{
NSString * errorDesc = nil;
NSPropertyListFormat format;
NSMutableString * plistPath;
NSMutableDictionary * temp = [BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:plistPath];
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
return false;
}
NSMutableArray * arr = [temp objectForKey:field];
[arr addObject:data];
NSLog(#"path: %#",plistPath);
// Write to plist
bool res = [temp writeToFile:plistPath atomically:YES];
NSLog(#"RES: %d", res);
return true;
}
The problem is that the bottom method that sends in "plistPath" to the above method retrives a null plistPath after the above method has set it. Why and how can I fix this?
NSLog(#"path: %#",plistPath);
in the bottom method shows null, why?
I use ARC. Also "destPath" is set and shows the correct path.
I believe you could be a bit confused here.
You are creating plistPath in the bottom method. And then you pass plistPath into
[BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:plistPath];
but plistPath is NULL
NSMutableString * plistPath; // Is NULL
But once it has been passed in the local plistPath takes over.
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath // Notice the local plistPath variable. This is the one you are playing with in this method.
At this point you are setting plistPath but remember it is still just a local variable and not an instance variable. So the button method will never know about it being set, as far as the button method is concerned it is still NULL
plistPath = [NSMutableString stringWithString:[destPath copy]];
So whatever you set in plistPath in the top method will not get passed back to the bottom method, think of the top plistPath as being deallocated when the method does the return.
So the plistPath in the bottom method will remain NULL
So try this instead SOLUTION
static NSMutableString *yourNewStringforPlistPath; //This will be NULL
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath
{
NSString * destPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
destPath = [destPath stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.plist", name]];
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
{
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"plist"] toPath:destPath error:nil];
}
yourNewStringforPlistPath = [NSMutableString stringWithString:[destPath copy]];
NSData * plistXML =
[[NSFileManager defaultManager] contentsAtPath:yourNewStringforPlistPath];
NSLog(#"AFTER plistPath: \n%#",yourNewStringforPlistPath);
return
(NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:format
errorDescription:&errorDesc];
}
+(bool)writeToCache:(NSString*) data andField: (NSString*) field
{
NSString * errorDesc = nil;
NSPropertyListFormat format;
NSMutableDictionary * temp = [BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:[NSNull null]]; // As this is already NULL you don't really need to pass yourNewStringforPlistPath in unless in the future this value can be set before this.
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
return false;
}
NSMutableArray * arr = [temp objectForKey:field];
[arr addObject:data];
NSLog(#"path: %#",yourNewStringforPlistPath);
// Write to plist
bool res = [temp writeToFile:yourNewStringforPlistPath atomically:YES];
NSLog(#"RES: %d", res);
return true;
}

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
}

Mysterious Memory Leak - NSString Autorelease Issue

Even though I'm using NSAutoreleasePool in a tight loop, the following line in the method below is causing me to get memory warnings and ultimately crashing my app (by commenting out that line, the problem goes away). Anyone have an idea why this is the case?
filepath = [docpath stringByAppendingPathComponent:file];
-(void)fileCleanup
{
NSString *documentspath = [AppSession documentsDirectory];
NSString *docpath = [documentspath stringByAppendingPathComponent:#"docs"];
NSFileManager *filemanager = [NSFileManager defaultManager];
NSArray *files = [filemanager subpathsOfDirectoryAtPath:docpath error:NULL];
NSLog(#"fileCleanup");
if(files != nil && [files count] > 0)
{
BOOL deletefile;
NSString *filepath;
NSAutoreleasePool *readPool;
NSString *testfile;
NSString *file;
for(file in files)
{
deletefile = YES;
for (testfile in allFiles) {
readPool = [[NSAutoreleasePool alloc] init];
//line below is causing memory leak!
filepath = [docpath stringByAppendingPathComponent:file];
//if([filepath isEqualToString:testfile])
//{
// deletefile = NO;
// break;
//}
[readPool drain];
}
if(deletefile)
{
[self logText:[#"\nD: " stringByAppendingString:[#"docs/" stringByAppendingPathComponent:file]]];
[filemanager removeItemAtPath:[docpath stringByAppendingPathComponent:file] error:NULL];
}
}
}
I replaced the inner "for" loop with the Objective C equivalent of the php in_array() function and the memory issues disappeared!
NSUInteger matchint = [allFiles indexOfObject:[docpath stringByAppendingPathComponent:file]];
if(matchint != NSNotFound)
deletefile = NO;
The break statement was dropping you out of your inner for loop without calling -drain on the NSAutoreleasePool.

release of previously deallocated object issue

I have a function which use for read one single line from a csv file.
But I got a release of previously deallocated object error, or sometimes the it is "double free" error.
I try to track down which object causes this error base on the error memory address, but I failed to do this.
Here's the code:
#interface CSVParser : NSObject {
NSString *fileName;
NSString *filePath;
NSString *tempFileName;
NSString *tempFilePath;
//ReadLine control
BOOL isFirstTimeLoadFile;
NSString *remainContent;
}
#property(nonatomic,retain) NSString *fileName;
#property(nonatomic,retain) NSString *filePath;
#property(nonatomic,retain) NSString *tempFileName;
#property(nonatomic,retain) NSString *tempFilePath;
#property(nonatomic,retain) NSString *remainContent;
-(id)initWithFileName:(NSString*)filename;
-(BOOL)checkAndCopyFile:(NSString *)filename;
-(BOOL)checkAndDeleteTempFile;
-(NSString*)readLine;
-(NSArray*)breakLine:(NSString*)line;
#end
#implementation CSVParser
#synthesize fileName;
#synthesize filePath;
#synthesize tempFileName;
#synthesize tempFilePath;
#synthesize remainContent;
-(id)initWithFileName:(NSString *)filename{
//ReadLine control
isFirstTimeLoadFile = TRUE;
self.fileName = filename;
self.tempFileName = [[NSString alloc] initWithFormat:#"temp_%#",fileName];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [documentPaths objectAtIndex:0];
self.filePath = [documentDir stringByAppendingPathComponent:fileName];
self.tempFilePath = [documentDir stringByAppendingPathComponent:tempFileName];
if ([self checkAndCopyFile:fileName]) {
return self;
}else {
return #"Init Failure";
}
}
-(BOOL)checkAndCopyFile:(NSString *)filename{
BOOL isFileExist;
NSError *error = nil;
NSFileManager *fileManger = [NSFileManager defaultManager];
isFileExist = [fileManger fileExistsAtPath:filePath];
if (isFileExist) {
//Create a temp file for reading the line.
[fileManger copyItemAtPath:filePath toPath:tempFilePath error:&error];
return TRUE;
}else {
return FALSE;
}
}
-(NSString*)readLine{
NSError *error = nil;
//Read the csv file and save it as a string
NSString *tempFirstLine = [[[NSString alloc] init] autorelease];
NSString *stringFromFileAtPath = [[NSString alloc] init];
if (isFirstTimeLoadFile) {
NSLog(#"Into First Time");
stringFromFileAtPath = [NSString stringWithContentsOfFile:tempFilePath
encoding:NSUTF8StringEncoding
error:&error];
isFirstTimeLoadFile = FALSE;
}else {
NSLog(#"Not First Time");
NSLog(#"Not First Time count:%d",[remainContent retainCount]);
stringFromFileAtPath = remainContent;
remainContent = nil;
}
if ([stringFromFileAtPath isEqualToString:#""]) {
[stringFromFileAtPath release];
return #"EOF";
}
//Get the first line's range
NSRange firstLineRange = [stringFromFileAtPath rangeOfString:#"\n"];
//Create a new range for deletion. This range's lenght is bigger than the first line by 1.(Including the \n)
NSRange firstLineChangeLineIncludedRange;
if (stringFromFileAtPath.length > 0 && firstLineRange.length == 0) {
//This is the final line.
firstLineRange.length = stringFromFileAtPath.length;
firstLineRange.location = 0;
firstLineChangeLineIncludedRange = firstLineRange;
}else {
firstLineRange.length = firstLineRange.location;
firstLineRange.location = 0;
firstLineChangeLineIncludedRange.location = firstLineRange.location;
firstLineChangeLineIncludedRange.length = firstLineRange.length + 1;
}
//Get the first line's content
tempFirstLine = [stringFromFileAtPath substringWithRange:firstLineRange];
remainContent = [stringFromFileAtPath stringByReplacingCharactersInRange:firstLineChangeLineIncludedRange withString:#""];
[stringFromFileAtPath release];
error = nil;
return tempFirstLine;
}
And the following code shows how I use the class above:
CSVParser *csvParser = [[CSVParser alloc] initWithFileName:#"test.csv"];
BOOL isFinalLine = FALSE;
while (!isFinalLine) {
NSString *line = [[NSString alloc] init];
line = [csvParser readLine];
if ([line isEqualToString:#"EOF"]) {
isFinalLine = TRUE;
}
NSLog(#"%#",line);
[line release];
}
[csvParser release];
If I run the code, and finish the csv parsing, the App's main function will give me the double free error when it try to free the autorelease pool."* __NSAutoreleaseFreedObject(): release of previously deallocated object (0x6a26050) ignored"
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
Could someone help me solve this issue?
Thank you!
[pool release];
Do not use -retainCount.
The absolute retain count of an object is meaningless.
You should call release exactly same number of times that you caused the object to be retained. No less (unless you like leaks) and, certainly, no more (unless you like crashes).
See the Memory Management Guidelines for full details.
There are a few problems in your code:
you aren't following the correct init pattern. You should have a self = [super init...]; if (self) {...} in there somewhere.
tempFileName is a retain property and you assign it the result of alloc/init. It will be leaked.
An immutable empty string ([[NSString alloc] init]) is pretty much never useful. And, in fact, stringFromFileAtPath is being leaked (technically -- implementation detail wise there is an empty immutable singleton string and thus, no real leak, but.... still...)
Finally, the crash: your readLine method correctly returns an autoreleased object. Yet, your while() loop consuming the return value of readLine is also releaseing that return value, leading to a double-release and an attempt to free that which was already freed.
You should "build and analyze" your code. I bet the llvm static analyzer would identify most, if not all, of the problems I mentioned above (and probably some more I missed).
When building with the analyzer, do you have either "all messages" or "analyzer issues only" selected in the Build window? Because, looking at the code, I'm surprised the analyzer didn't catch the obvious problem with stringFromFileAtPath.
Excerpting the code, you have the following lines that manipulate stringFromFileAtPath:
NSString *stringFromFileAtPath = [[NSString alloc] init];
....
stringFromFileAtPath = [NSString stringWithContentsOfFile:tempFilePath
encoding:NSUTF8StringEncoding
error:&error];
....
stringFromFileAtPath = remainContent;
....
[stringFromFileAtPath release];
And remainContent is set by:
remainContent = [stringFromFileAtPath stringByReplacingCharactersInRange:firstLineChangeLineIncludedRange
withString:#""];
You are releasing an autoreleased object. By memory keeps going up, how are you measuring it? Don't use Activity Monitor as it is nearly as useless to developers as retainCount is misleading. Use Instruments.
Your tempFirstLine NSString object is declared with autorelease, and is returned as your NSString line, which is then released.
Try using this:
while (!isFinalLine) {
NSString *line = [csvParser readLine];
if ([line isEqualToString:#"EOF"]) {
isFinalLine = TRUE;
}
NSLog(#"%#",line);
}
Replac this:
NSString *stringFromFileAtPath = [[NSString alloc] init];
with this:
NSString *stringFromFileAtPath = nil;
and get rid of the [stringFromFileAtPath release] statements.
The first line creates a pointer to a new string object that you never use, because you immediately overwrite the pointer with a pointer to string objects from elsewhere, which you don't need to release because you don't own them/didn't create them. Since you are releasing them, you're getting a crash.
You make the same mistake with tempFirstLine.