Calling two times a function of a static variable? - objective-c

I have this class containing a static variable "database" which represent a database realized with sqlite and a function getAllShop which task is recalling all the data that are stored in the db and filling them into a mutable array
#define kFilename #"negozi.sqlite"
#implementation ShopDatabase
static ShopDatabase *database;
+(ShopDatabase *)database{
if (database==nil) {
database = [[ShopDatabase alloc] init];
return database;
}
}
- (id)init
{
self = [super init];
if (self) {
// Create the path to the database in the Documents directory for the bundle itself is not writable
NSArray *pathsToDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [pathsToDocuments objectAtIndex:0];
databasePath = [documentsDirectory stringByAppendingPathComponent:kFilename];
if (![[NSFileManager defaultManager] isReadableFileAtPath:databasePath]) {
if ([[NSFileManager defaultManager] copyItemAtPath:yourOriginalDatabasePath toPath:databasePath error:NULL] != YES)
NSAssert2(0, #"Fail to copy database from %# to %#", yourOriginalDatabasePath, databasePath);
}
// --- open db
if(sqlite3_open([databasePath UTF8String], &database) != SQLITE_OK){
NSLog(#"Failed to open database");
}else {
NSLog(#"Database opened");
}
}
return self;
}
- (NSMutableArray *) getAllShops{
// ------ read all the db
NSMutableArray *returnArray=[[NSMutableArray alloc] init];
NSString *query= #"SELECT * FROM negozio";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, NULL) == SQLITE_OK){
NSLog(#"Prepared database");
while (sqlite3_step(statement)==SQLITE_ROW) {
int uniqueId = sqlite3_column_int(statement, 0);
NSMutableString *nome = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
ShopInfo *info= [[ShopInfo alloc] initWithUniqueId:uniqueId nome:nome];
[returnArray addObject:info];
}
sqlite3_finalize(statement);
}
return returnArray;
}
#end
When i have to take data from the database from another class i do this, calling the getAllShop and all goes well. In this way i have all the data of the db into my array shopinfo:
NSMutableArray *shopInfo=[[ShopDatabase database] getAllShops];
Now, my database contains data that i need to use to fill TWO table view, so i need to execute this TWO times: one time in the class representing the first table view and one in the second. When i do this in the first view all goes well, but when i do the same the second time, Xcode give me a exc bad access error. I tried executing the code two times in the same class and this is what i get
2012-05-11 13:06:54.897 Shopping Mall[11333:707] -[NegozioPartenza getAllShops]: unrecognized selector sent to instance 0x14b8c0
2012-05-11 13:06:54.899 Shopping Mall[11333:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NegozioPartenza getAllShops]: unrecognized selector sent to instance 0x14b8c0'
*** First throw call stack:
(0x33ad188f 0x325c3259 0x33ad4a9b 0x33ad3915 0x33a2e650 0xa4141 0x35727e33 0x3574c629 0x35710d7d 0x357d34dd 0x3571955d 0x3571940b 0x357d34a3 0x35788873 0x357881b7 0x357d1d89 0x357d04eb 0x3582b82b 0x33a2b3fd 0x35709faf 0x35709f6b 0x35709f49 0x35709cb9 0x3570a5f1 0x35708ad3 0x357084c1 0x356ee83d 0x356ee0e3 0x32fa622b 0x33aa5523 0x33aa54c5 0x33aa4313 0x33a274a5 0x33a2736d 0x32fa5439 0x3571ce7d 0xa2515 0xa24c0)
terminate called throwing an exception(lldb)
I am a newbie to objective C so i can't manage to understand what's the point. How can i call two times a function on a static variable? Thank you.
Edit: maybe calling the [ShopDatabase database] activate a second time the constructor of initializazione making mess? When i say that a variable is static it mean there's only one instance of it for every object of that class, right? So how i can access that unique instance after creating it the first time? I think i'm messing up what happen when you use a static variable...

You don't have a valid return on the second call.
+(ShopDatabase *)database{
if (database==nil) {
database = [[ShopDatabase alloc] init];
return database;
}
}
On the second call database is not nil and you don't return anything. You should be getting a warning that not all control paths return a value.
Here is the proper way.
+(ShopDatabase *)database{
if (database==nil) {
database = [[ShopDatabase alloc] init];
}
return database;
}

Somewhere after your first call, database is being released. Subsequently, an object of type NegozioPartenza is being created at the same location. The second call to [ShopDatabase database] returns this object, and you then send -getAllShops to that object, which obviously doesn't implement that method.

Related

How to parse and take only this string value

I wanted to get only array string value app. As example(SLGoogleAuth ,HalfTunes,TheBackgrounder,Calculiator) . But don't know how to do?
It's a code.
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector=NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSLog(#"apps: %#", [workspace performSelector:selectorALL]);
}
It's output:
Thanks in advance
You do not want to parse that. NSLog prints out a description of an object. You want to access that value directly.
[LSApplicationWorkspace allApplications];
returns NSArray of LSApplicationProxy. LSApplicationProxy class has a ivar _bundleURL that contains information that you need. You need runtime functions to access it. Working example below:
// #import <objc/runtime.h>
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector=NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSArray* appProxies = [workspace performSelector:selectorALL];
Ivar bundleUrlIvar = class_getInstanceVariable([appProxies.firstObject class], "_bundleURL");
NSMutableString* result = [NSMutableString string];
for (id appProxy in appProxies)
{
NSURL* url = object_getIvar(appProxy, bundleUrlIvar);
// at this point you have the information and you can do whatever you want with it
// I will make it a list as you asked
if (url)
{
[result appendFormat:#",%#", [url lastPathComponent]];
}
}
if (result.length > 0)
{
// remove comma from beginning of the list
[result deleteCharactersInRange:NSMakeRange(0, 1)];
}
NSLog(#"apps: %#", result);
Note that this will be rejected by AppStore as you are using private apis. So use at your own discretion.

Return strings in sequence from NSMutableArray [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I have a NSMutableArray with comma separated strings for an objective-c iphone application (iOS SDK 6.0). I need a simple function that, when triggered, returns a string, one at a time, from string 0 onwards. To give you some context, a user would click a button, and for every click a new string is returned, in order, from the Array. It's a list of previously saved "favourite quotes". The string is displayed in a UITextView.
Ideally, I would also have a function for reversing, i.e. going backwards in the array from the current position.
This might be pretty basic, but I seem to only be able to find more advanced implementations that I'm unable to translate into this looping backwards and forwards in an Array of strings.
EDIT: Current code for this function below. I need to add the part where one string at a time is returned from the array (allRows) and displayed in a textview
- (IBAction)nextQoute:(id)sender {
const char *dbpath = [_databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_qoutesDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:
#"SELECT qoutesSaved FROM qoutes"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(_qoutesDB,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
NSMutableArray *allRows = [[[NSMutableArray alloc] init] autorelease];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *qouteField = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(
statement, 0)];
NSString *str = [NSString stringWithFormat:#"%#", qouteField];
[allRows addObject:str];
[qouteField release];
}
Very thankful for help!
You mention that you are new to Objective C, and based on how I read what you're describing, I wonder if you are making this a lot more complicated than it needs to be. Items in an array (that is, NSArray, or NSMutableArray) aren't "seperated" by anything - different items in an array are accessed by their order in the array, called an index. So in your code, when you build allRows, each str that you put into it gets put into its own index, and to get it back out of the array, you just use that index.
For example, let's pretend your quotes you are pulling from your database are:
"Here's looking at you kid"
"I'll be back"
"It's a trap"
If they are put into the array in that order, and you want to put "It's a trap" into a textfield (called myTextField), you just write
myTextField.text = [allRows objectAtIndex:2];
In the end, that means what you probably want to do for your app is keep a counter you pass to objectAtIndex. When the user clicks the forward button, increase the count. When they click the back button, decrease the count. Then, call the code I put above, except instead of sending the value 2, send your counter variable. As mentioned in the comments to H2CO3s answer, make sure you put some validation in there to prevent your count from going beyond the limits of your array, or you'll get a nasty crash. If this is indeed what you are trying to do and you are still confused, I can add some more code.
I'm not too familiar with accessing a sqlite database the way you do here (I use CoreData, so the calls are very different), so perhaps I am completely mistaken about what you are trying to do here - but this seems like a very simple task that is being very overcomplicated.
You can just keep track of the string index in an instance variable (or if you don't have an object to work with because you write a class method or a function and not an instance method, then you can use a static local variable too).
#interface Foo: NSObject {
NSInteger index;
NSArray *strings;
}
// ...
- (NSString *)nextString
{
return index < strings.count ? strings[index++] : nil;
}
- (NSString *)previousString
{
return index > 0 ? strings[--index] : nil;
}
You can fetch the whole data in database.And store it one array
declare these two objects.
#interface YourClassName: NSObject {
NSInteger stringIndex;
NSArray *quoteFieldDataArr;
}
NSMutableArr *quoteFieldDataArr=[self fetchDataFromDataBase];
-(NSMutableDictionary *)fetchDataFromDataBase
{
const char *dbpath = [_databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_qoutesDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:
#"SELECT qoutesSaved FROM qoutes"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(_qoutesDB,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
NSMutableDictionary *allRows = [[[NSMutableDictionary alloc] init] autorelease];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *qouteField = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(
statement, 0)];
NSString *str = [NSString stringWithFormat:#"%#", qouteField];
[allRows addObject:str];
[qouteField release];
return nil;
}
// Load Next String
- (NSString *)loadNextString
{
return stringIndex < quoteFieldDataArr.count ? quoteFieldDataArr[stringIndex++] : nil;
}
- (NSString *)loadpreviousString
{
return stringIndex > 0 ? quoteFieldDataArr[--stringIndex] : nil;
}

NSlog showing odd output

I have a NSlog that is giving odd output in the debugger. How do I get it to show the proper value?
NSError *error = nil;
NSArray *data;
[self setStatus:#"Syncing data..."];
self.userInfo = [self.cloud Authenticate:[self serialNumber]];
if ( self.deviceInfo )
{
data = [self.device GetData:&error];
if ( !data )
{
[self displayErrorMessage:error];
data = [NSMutableArray array];
}
//data received from device: Log point
NSLog(#"data received from device: %#",data);
Debuger output
"<DataPoint: 0x1001f81b0>",
"<DataPoint: 0x10012f5f0>",
"<DataPoint: 0x1001f7780>",
"<DataPoint: 0x1001f8780>",
This is the default string returned by NSObject's description method, which just prints the pointer value. If you want to see the proper data printed, override description in your DataPoint class:
- (NSString*) description
{
// Example:
return [NSString stringWithFormat: #"ivar1=%# , ivar2=%#",ivar1, ivar2];
}
As you stored an object of DataPoint in the array, correct output is shown.
If you want to see full value for each of them, then you need to use
for(DataPoint *dp in data){
NSLog(#"%#",dp.property);//property should be your property name of DataPoint class.
}
+1 for #Ramy's good suggestion to override description, I suggest extending, rather than replacing, like this:
- (NSString *)description {
return [NSString stringWithFormat: #"%#: ivar1=%#, ivar2=%#",
[super description], self.ivar1, self.ivar2];
}
The inherited behavior that answers the class and the %p pointer is very useful, too.

Objective C: Why is this code leaking?

I'm trying to implement a method similar to what mytunescontroller uses to check if it has been added to the user's login items. This code compiles without warnings but if I run the leaks performance tool I get the following leaks:
Leaked Object # Address Size Responsible Library Responsible Frame
NSURL 7 < multiple > 448 LaunchServices LSSharedFileListItemGetFSRef
NSCFString 6 < multiple > 432 LaunchServices LSSharedFileListItemGetFSRef
Here is the culprit:
- (BOOL)isAppStartingOnLogin
{
LSSharedFileListRef loginListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if (loginListRef) {
NSArray *loginItemsArray = (NSArray *)LSSharedFileListCopySnapshot(loginListRef, NULL);
NSURL *itemURL;
for (id itemRef in loginItemsArray) {
if (LSSharedFileListItemResolve((LSSharedFileListItemRef)itemRef, 0, (CFURLRef *) &itemURL, NULL) == noErr) {
if ([[itemURL path] hasPrefix:[[NSBundle mainBundle] bundlePath]]) {
[loginItemsArray release];
[itemURL release];
CFRelease(loginListRef);
return YES;
}
}
}
[itemURL release];
[loginItemsArray release];
CFRelease(loginListRef);
}
return NO;
}
LSSharedFileListItemResolve() returns an owned object in the third parameter. This can be verified by reading the header. As a result, you need to release itemURL.

Performance when building the Objective C application in the device

I have a performance problem when I build the application in the Device. It is actually in my database. I have a table of wine details in which there are 2114 wine names. To get the all those wine names, I wrote this code in the appDelegate:
-(NSMutableArray*)getWineDetails
{
[wineDetailsList removeAllObjects];
sqlite3_stmt* statement;
const char *sql = "select *from wineDetails order by name";
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
}
while (sqlite3_step(statement) == SQLITE_ROW)
{
primaryKey = sqlite3_column_int(statement, 0);
//printf("\n primaryKey1 Value:%d",primaryKey);
wineDetails *wineDets = [[wineDetails alloc] initWithPrimaryKey:primaryKey database:database];
[wineDetailsList addObject:wineDets];
//printf("\n ==========================%d",[wineDetailsList count]);
[wineDets release];
}
sqlite3_finalize(statement);
printf("\n Inside AppDelegate .....wineDetailsList count:%d",[wineDetailsList count]);
return wineDetailsList;
}
I am calling this method in the viewWillAppear of another controller where I have to display the wine names in the table view.
The viewWillAppear code:
-(void)viewWillAppear:(BOOL)animated
{
CorkItAppDelegate* appDelegate = (CorkItAppDelegate*)[[UIApplication sharedApplication] delegate];
winesList = [appDelegate getWineDetails];
[tableView reloadData];
}
Here the problem is that when I build it in the device, it takes too much time to navigate into the controller due to the amount of date in the database. What should I do to get rid of this performance issue?
Thanks,
Monish Kumar.
Just as a quick suggestion, you could add an index on the name column, that might speed up the fetch. Also, make sure you're not fetching any more things than you need.