I have used the following code.
MainView.h:
NSString *sCopySource;
NSString *sFileSource;
// retain and copy used both for test proposes
#property (nonatomic, retain) NSString *sCopySource;
#property (nonatomic, copy) NSString *sFileSource;
MainView.m:
// Inside the first method:
sCopySource = [NSString stringWithFormat:#"%#%#", path1, filename];
sFileSource = [NSString stringWithFormat:#"%#%#", path2, filename];
// Inside the second method:
[[NSFileManager defaultManager] copyItemAtPath:sCopySource toPath:sFileSource error:&err];
And take error in the last line of the code by zombie-enabled objects sCopySource and sFileSource:
message sent to deallocated instance
Why? The properties marked as retain and copy. How to fix this?
Thanks a lot for the help!
P.S. Please don't answer to use ratain and release methods. They're extremely inconvenient.
You have defined the property, but you are writing directly to the instance variable.
If you want to use the retain/release logic in the property, you need to use:
self.sCopySource = [NSString stringWithFormat:#"%#%#", path1, filename];
self.sFileSource = [NSString stringWithFormat:#"%#%#", path2, filename];
That way, the methods that do the copy and retain are used.
Related
I'm an Objective-C newbie and I'm studying iPhone programming.
In my appDelegate, in the -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method, I've a class member (#syntethized) called databasePath.
I set its value this way:
databasePath = [self copyDatabaseToDocuments];
I copied the entire copyDatabaseToDocuments method from a wonderful book by Alasdair Allan and made very little changes (the name of the db is the only thing I changed):
-(NSString *)copyDatabaseToDocuments{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath=[paths objectAtIndex:0];
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"myDb.sqlite"];
//
if(![fileManager fileExistsAtPath:filePath]){
NSString *bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"myDb.sqlite"];
[fileManager copyItemAtPath:bundlePath toPath:filePath error:nil];
}
return filePath;
}
I NSLog the databasePath and I regularly get its value (it is a string path and it is not null) after the assignment.
Then, I have a method -(NSMutableArray*)readDatabase:(char*)querySQL I call from a ViewController through a delegate reference.
Anything works fine if -inside this last method- I assign again the value of databasePath.
But, if I don't assign it again AND I want to use its value (that I suppose it was set in the -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method) the app crashes.
Why?
Make sure that your #property for databasePath looks like this:
#property (nonatomic, retain) NSString *databasePath;
And then set it in this way:
self.databasePath = [self copyDatabaseToDocuments];
It is probably crashing because copyDatabaseToDocuments returns an autoreleased string, and unless you use the self. notation to set databasePath, that autoreleased string can go away at any time.
At a guess, since you don't show the relevant code, you are not retaining the value of databasePath. You assign it directly to the ivar in your code sample above, yet the method you show returns an autoreleased string.
I'll guess your property is defined as retain or copy. You should therefore set the value as
self.databasePath = [self copyDatabaseToDocuments];
This will then retain or copy the value for you. Synthesizing property accessors does you no good if you don't use them!
I am battling with this piece of code for days now, I really would appreciate your insight. So here it is...
fileName which is declared in my .h file as
NSString *fileName;
and later as
#property (nonatomic, copy) NSString *fileName;
is not accessible inside viewWillDisappear. It just shows up as nil object, or some weird value when I debug. When I debug, my console show this msg:
Printing description of fileName:
FileNumber1
When I reach to viewWillDisappear my console shows this:
Printing description of fileName:
Yeah, just nothing for fileName.. the value simply disappears.. I just don't know why!!!
MORE DETAILS:
Inside my init method I do the following and it works fine.
fileName = [[NSString alloc] initWithString:(NSString *)CFURLGetString(pdfURL)];
fileName = [[fileName lastPathComponent] stringByDeletingPathExtension];
fileName = [fileName stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
Throughout the program I can access fileName just fine, but inside viewWillDisappear I am trying to save to my standardUserDefaults by attempting the following:
NSUserDefaults *ud = [[NSUserDefaults standardUserDefaults] autorelease];
NSArray *tmp = [NSArray arrayWithArray:someMutableArray];
[ud setObject:tmp forKey:fileName]; // THE PROBLEM IS HERE WITH fileName AS AKEY
[ud synchronize];
[tmp release];
At this point someMutableArray is accessible, but fileName is not. Any insight, thoughts, ideas, will be appreciated.
in your init method change it to read self.fileName =
You need to use your property methods so that a copy of the string will be made, currently because you are using the variable rather than the property you are assigning an autoreleased string. By using self.fileName the string will be copied and will not be autoreleased.
I have a database with 10 tables. As I need to access this database in different view controllers, I have to declare the two methods shown below in each of them. Is there a way I can avoid this by declaring these methods in the application delegate. If yes, how can I go about using these methods in different classes.
- (NSString *) getWritableDBPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:DATABASE_NAME];
}
-(void)createEditableCopyOfDatabaseIfNeeded
{
// Testing for existence
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:DATABASE_NAME];
NSLog(#"%#",writableDBPath);
success = [fileManager fileExistsAtPath:writableDBPath];
if (success)
return;
// The writable database does not exist, so copy the default to
// the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:DATABASE_NAME];
success = [fileManager copyItemAtPath:defaultDBPath
toPath:writableDBPath
error:&error];
if(!success)
{
NSAssert1(0,#"Failed to create writable database file with Message : '%#'.",
[error localizedDescription]);
}
}
in your view controller first of all create a delegate variable
YourAppDelegate *appDelegate=(YourAppDelegate *)[[UIApplication sharedApplication]delegate];
then u can call any methods that you have define in your delegate
like [appDelegate methodName];
This just screams to be implemented as a separate controller with class level methods. I would highly recommend creating a Database controller with a definition like so:
#interface DatabaseController: NSObject
+ (NSString *) getWritableDBPath ;
+ (void) createEditableCopyOfDatabaseIfNeeded ;
#end
Then in your code using it as so:
#import "DatabaseController.h"
NSString * somePath = [DatabaseController getWritableDBPath];
[DatabaseController createEditableCopyOfDatabaseIfNeeded];
Set them as public, so you can call them with [ ]
You just need to change the minus for +
+(void)createEditableCopyOfDatabaseIfNeeded;
You will need to define a protocol for this class and add a variable of that protocol to the member variable of this class as follows:
The classes where the object is created can either call this method using the object. The Best option is to use the app delegate class to implement these methods.
You can then assign the objects's delegate as the app delegate and call the methods.
#protocol mySqlDelegate ;
#interface mySqlClass {
id <mySqlDelegate> delegate;
}
#property (nonatomic, assign) id <mySqlDelegate> delegate;
#end
#protocol mySqlDelegate
- (void) delegateMethodsForThisClass;
#end
first create a common instance for appdelegate.
otherwise in constant.h file create a instance like
mAppDelegate=(YourAppDelegate*)[[UIApplication sharedApplication] ];
then just import constant.h and you may use mAppdelegate anywhere so using this you easily call
Can anyone please tell me how to write NSMutableArray of custom class objects to file?
below is the code which i am using to write my array "medicationsArray" to file.
medicationsArray contains the objects of below medicationData class
#interface medicationData: NSObject {
NSString *drName;
NSString *date;
NSString *description;
}
#end
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentDir = [path objectAtIndex:0];
NSString *pathWithFileName;
NSString *pathWithFileName = [NSString stringWithFormat:#"%#/medicationsArray",documentDir];
[medicationsArray writeToFile:pathWithFileName atomically:NO];
by using this code i am not able to create a file.
can anyone help me in this, thanks in advance
Instead of constructing the pathWithFileName "manually" you should use the stringByAppendingPathComponent: method instead:
NSString *pathWithFileName = [documentDir stringByAppendingPathComponent:#"medicationsArray"];
This will take care of any extra slashes, etc. in the path. This might be the reason your path may be wrong. I presume that the extra declaration of pathWithFileName in your snippet is just a typo.
Thank you for the reply Claus,
Now i am able to store the data, i am using NSArchiver and NSUnArchiver.
below is the code please find.
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentDir = [path objectAtIndex:0];
NSString *pathWithFileName;
pathWithFileName = [NSString stringWithFormat:#"%#/medicationsArray",documentDir];
NSData *savedData = [NSKeyedArchiver archivedDataWithRootObject:appDelegate.medicationsArray];
[savedData writeToFile:pathWithFileName atomically:YES];
to extract the data
if([[NSFileManager defaultManager] fileExistsAtPath:pathWithFileName])
{
NSData *savedData = [[NSData dataWithContentsOfFile:pathWithFileName] retain];
appDelegate.medicationsArray = [[NSKeyedUnarchiver unarchiveObjectWithData:savedData] retain];
[objMedicationDetailsTVController.tableView reloadData];
}
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [path objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"DB"];
NSString *fileName = [newWordbookName stringByAppendingString:#".csv"];
NSString *fullPath = [databasePath stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
[databasePath release];
//[fileName release]; Error!
//[fullPath release]; Error!
//NSLog(#"#1 :databasePath: %d",[databasePath retainCount]);
//NSLog(#"#1 :fileName: %d",[fileName retainCount]);
//NSLog(#"#1 :fullPath: %d",[fullPath retainCount]);
I'm using this code and want to release NSString* ..
so, I declare fileName, fullPath, and databasePath of NSString.
database is released but fileName, fullpath doesn't release. I don't know why it happens.
I know that NSArray is Autoreleased. But is documentsDirectory autoreleased?
(newWordbookName is nsstring type)
I hope that I look through a document about iPhone memory management.
By convention the only two cases when a method returns a retained object are constructors i.e. alloc, new etc. and object-copying methods (containing copy in their name).
In all other cases the object is expected to be autoreleased, unless explicitly stated otherwise in the documentation.
This is the complete memory management documentation:
Cocoa Memory Management
You should not be calling release on any of the objects in the above code.
The reason the NSArray is autorelease'd is the same reason all the other objects are autorelease'd: the methods that assigned them their values called autorelease on them before they returned. In general, you can assume methods return autorelease'd objects if they do not have the word "create" or "new" in them. That is the general Cocoa convention. (Although 3rd party code may be goofy and do things differently, so caveat programmer).
You only really need to worry about objects you alloc or copy yourself; in other words, pair every alloc or copy with a release or autorelease.