\[NSString stringWithUTF8String: "hello"\] returns nil. Huh? - ios

I'm trying to solve a problem in an iOS program that was recently ported to 64 bits.
I discovered that stringWithUTF8String always returns nil, and I was able to narrow the problem to a very simple 12 liners. Here is my main.m file
#import <UIKit/UIKit.h>
#include <string>
int main(int argc, char* argv[])
{
std::string a("hello");
NSString * s1 = [NSString stringWithUTF8String: a.c_str()];
NSString * s2 = [[NSString alloc] initWithUTF8String: a.c_str()];
NSString * s3 = [NSString stringWithUTF8String: "hello"];
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, #"AppDelegate");
}
}
s1, s2, and s3 are nil when I debug the program on an iPad Air 2 !
I am inclined to think there might be either an obvious problem in the code, or there is a configuration error in the project settings since it was ported to 64 bits (this code used to work perfectly).
Does anyone have a clue?
Thanks in advance
EDIT :
I just tested the code in a new project under OSX (File|New Project|Command Line Tool), and it works.
So, it seems that my problem is due to the project settings. I do not know where to look although.

Related

iOS 7.1 CommonCrypto library complains: Implicit conversion loses integer precision: 'NSUInteger' (unsigned long) to CC_LONG (unsigned int)

I get the above error (in title) whilst doing a MD5 from file.. I can usually cope with these type of 32->64bit conversion issues..but in this case, I do not know what I should do as CC_MD5 is part of CommonCrypto->CommonDigest, a library that ships with iOS7.1. I am assuming [inputData length] is returning NSUInteger and therein lies the issue, however can I simply cast down from UL to UI? I will possibly lose precision if the file is large. Why would a library that Apple ships with require int in a 64 bit capable language such as iOS? Did someone overlook something or am I being really stupid and mis-diagnosing the problem?
- (NSString*) getMD5FromFile:(NSString *)pathToFile {
unsigned char outputData[CC_MD5_DIGEST_LENGTH];
NSData *inputData = [[NSData alloc] initWithContentsOfFile:pathToFile];
CC_MD5([inputData bytes], [inputData length], outputData);
[inputData release];
NSMutableString *hash = [[NSMutableString alloc] init];
for (NSUInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
[hash appendFormat:#"%02x", outputData[i]];
}
return [hash autorelease];
}
From CommonCrypto->CommonDigest.h:
extern unsigned char *CC_MD5(const void *data, CC_LONG len, unsigned char *md)
I filed bug report with apple (#17256918) but isn't there a way to do this without the error?
You simply need to cast [inputData length] to either int or uint32_t as you suspect.
If you want to assert that number is not too large for type you can use the UINT32_MAXto check. Here is an example on an NSAssert you could use.
NSAssert([inputData length] < UINT32_MAX, #"too big!");
I'm not a pro but I hope I didn't make a huge mistake.
For code to be a bit safer than just casting without assertion you can use this assert:
NSAssert([inputData length] <= (CC_LONG)-1, #"Input length is too big for unsigned CC_LONG type! Or CC_LONG became signed, which is unlikely");
This variant is better because it doesn't rely on UINT32_MAX, because CC_LONG can be changed in future.
P.S. I was going crazy when I first saw this trick (CC_LONG)-1 some years ago before I realized that it's just type casting and not a sophisticated C feature related to sizeof. So this is a huge relieve for your brain if you struggled with it)

Memory not decreasing after a series of insertions in the database

I have simple code that inserts values into my database:
-(void)inserirBd:(NSString *)valores{
sqlite3_stmt *statement;
// Get the documents directory
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = dirPaths[0];
// Build the path to the database file
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:#"fazerbem.sqlite"]];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &myDatabase) == SQLITE_OK) {
NSString *insertSQL = valores;
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(myDatabase, insert_stmt,
-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE) {
} else {
NSLog(#"Error -> %s", sqlite3_errmsg(myDatabase));
}
sqlite3_finalize(statement);
sqlite3_close(myDatabase);
}
}
and I am calling this function in this way:
cInsert = [NSString stringWithFormat:#"INSERT INTO fazerbem_products (value) Values ('%#')", p1_1.text,];
InsertBD *insert = [[InsertBD alloc] init];
[insert inserirBd:cInsert];
To perform some tests of memory, I put the above command in a loop that will insert a repeat this 85,000 times, in debug memory, before inserting the values ​​into the database the memory was 1.1 MB, after performing the loop the memory rose to around 500 MB.
But after that, why not the memory lowered to 1.1 MB again? since the loop was already processed and in this case the memory should be released ? (I'm using ARC, and my app only have a button to do this)
The Xcode Debug Memory report is not what either you want nor what you think it is.
The Debug Navigator shows the same things as the "Activity Monitor" instrument. It is not showing current live memory by your app, it's showing current memory allocated to your app by the OS. The OS will reclaim unused but allocated memory as needs arise.
The concept is to avoid allocation memory blocks from the system, deallocating only to need to re-allocate later. There is a difference between the system allocation memory (large blocks) to an app and the app allocation memory to an object (portions of the large blocks). You care about the second, the system cares about the first.
See this SO Answer Here by #Putz1103 for complete details.
Use Instruments and look at Live Bytes to see the actual memory usage.

NSLog disable from certain classes and DEBUG

Hello guys I have found this code that is used to create a different NSLog (without data and timestamps) that displays the class where the log was made and the line number.
I have read that is possible to disable the logging only for certain classes with NO_LOG but there was not explained how to use it exactly, I am quite new to obj-c and I appreciate an explanation on how to disable logging for certain classes and how to activate and deactivate the debugging. thanks
#define MAKESTRING(__VA_ARGS__) #__VA_ARGS__
#define TOSTRING(...) MAKESTRING(__VA_ARGS__)
static inline void PxReportv(BOOL doLog, char const *file, int line, NSString *prefix, NSString *fmt, va_list argList) {
if (doLog) {
NSString *fileNameWithExtension = [[NSString stringWithFormat:#"%s", file] lastPathComponent];
#ifdef NO_LOG
NSString *fileName = [fileNameWithExtension stringByDeletingPathExtension];
char *f = TOSTRING(NO_LOG);
NSArray *comps = [[[NSString alloc] initWithFormat:#"%s", f] componentsSeparatedByString:#","];
for (NSString *except in comps) {
if ([except isEqualToString:fileName]) {
return;
}
}
#endif
vprintf([[[NSString alloc] initWithFormat:[[NSString alloc] initWithFormat:#"%# <%# [%d]> %#\n", prefix, fileNameWithExtension, line, fmt] arguments:argList] cStringUsingEncoding:NSUTF8StringEncoding], NULL);
}
}
static inline void PxReport(BOOL doLog, char const *file, int line, NSString *prefix, NSString *fmt, ...) {
va_list ap;
va_start(ap, fmt);
PxReportv(doLog, file, line, prefix, fmt, ap);
va_end(ap);
}
#define PxError(...) PxReport(YES, __FILE__, __LINE__, #"[ERROR]", __VA_ARGS__)
#ifdef DEBUG
#define PxDebug(...) PxReport(YES, __FILE__, __LINE__, #"[DEBUG]", __VA_ARGS__)
#define NSLog(...) PxReport(YES, __FILE__, __LINE__, #"", __VA_ARGS__)
#else
#define PxDebug(...)
#define NSLog(...)
#endif
Add this:
#define NO_LOG 1
before #importing the file you've shown above.
BTW a better implementation would define PxDebug() and NSLog() to nothing if NO_LOG was defined...
that is quite a verbose solution, i made one that is a lot neater than that
#ifndef DebugLog_h
#define DebugLog_h
#if DEBUG
#define DLog(...) do{\
printf("[%s:%d]", __FUNCTION__, __LINE__);\
NSString *_S_ = [NSString stringWithFormat:__VA_ARGS__];\
printf(" %s\n",[_S_ cStringUsingEncoding:NSUTF8StringEncoding]);\
}while(0);
#else
#define DLog(...)
#endif
#endif
that will print the line number, class and function it came from
eg:
Dlog(#"hello %d", 123);
[-[SomeViewController viewWillAppear:]:91] hello 123
edit: if you add the file to your projectname-Prefix.pch file, then you can use it without having to include it everywhere
and it will automatically be taken out of release builds, because DEBUG is defined as a project definition automatically when its in debug mode

Is SQLite able to update or insert

I am facing a problem in Xcode : I tried many weeks to add data into sqlite database and I surf Google for many results but none of them can give a complete and clear tutorial, most of them are using .db but not sqlite, I suspect that sqlite is not able to insert or update, just can read data, or is there any other way to do it?
Please attach the link with tutorial in the answer if you guys have any. Thank you
I suspect that sqlite is not able to insert or update, just can read
data
Of course you can insert data in SQLite. This is a database just as other ones, just a little lighter and embedded in the same process as your application instead of a different one as is usually the case (this both means SQLite can't answer to more than one client, and that you don't have to install anything on the computer apart your application).
Here's the INSERT documentation : http://www.sqlite.org/lang_insert.html
And here's a tutorial among many other ones : http://www.techotopia.com/index.php/An_Example_SQLite_based_iPhone_Application
(in fact I just googled for "sqlite xcode insert", I didn't knew this tutorial before)
Sunny it's been two days that im working to find out how to insert data to sqlite on iOS. First thing you should do is to tell SQLite to where save the data. By default if you run ur app on ur phone it will put the sqlite file in a readonly root. to solve it you should do this :
-(NSString *)GetDocumentDirectory{
fileMgr = [NSFileManager defaultManager];
homeDir = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
return homeDir;
}
which fileMgr is NSFileManager and homeDir is NSString next when you wanna insert yout data, first u should give ur SQLite root to it, which is the first line, and it checks if sqlite3_open and will start to insert your data:
-(void)InsertRecords:(NSMutableString *)myData{
NSString *dbPath = [self.GetDocumentDirectory stringByAppendingPathComponent:#"YOUR SQLITE FILE NAME.sqlite"];
const char *dbpath = [dbPath UTF8String];
sqlite3 *contactDB;
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO tableName (tableColumn) VALUES (\"%#\")", myData];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_bind_text(statement, 1, [myData UTF8String], -1, SQLITE_TRANSIENT);
}
else {
NSLog(#"error");
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}
hope it helps :)

Sqlite FTS doesn't work when compiling with LLVM on iOS

I've been developing an enterprise iPad app for a while now. Since the app development started almost 2 years ago, I needed to compile my own version SQLite from source, since the default SQLite library (the sqlite3.dylib) didn't have FTS enabled by default.
Ok, everything was working fine since then. I've been always using GCC as the project compiler.
The thing is that now I'm trying to convert my whole project to use ARC. To do so, I need to use Apple's LLVM compiler.
That's it. When I change the compiler (from GCC 4.2 to LLVM 3.1 or 4.0, without converting to ARC, and without changing anything else), my app builds fine, everything runs ok, except by my FTS queries, which don't work at all Even the simplest ones. They run and return always with no result (with a SQLITE_OK code, though).
I'm stuck here. I have already talked to an Apple engineer at WWDC'12 but we couldn't find any solution.
I guarantee that it is unlikely to be a malformed query or something like that since the app is working fine with GCC. Also, I'm able to run the queries on the Terminal version of SQLite (or using other apps, like Base)
I was also using an old version of SQLite, but I've updated to the most recent version to date (3.7.13). Everything stays the same. I've also noticed that, now (I don't know since when) the sqlite that comes with the mac supports FTS (!!!) and I was able to remove my own version and use Apple's one. The thing is, I'm having the exact same behavior.
I've been looking for a solution, but couldn't find one. I've found some bugs related to armv6 and compiler optimisations (which can be fixed by using the -mno-thumb flag), but it's not my case. I also noticed that when I analyse my custom sqlite files using Clang it points out many, many "potencial errors".
I have this non-skeptical view and I (still) don't believe that it's a LLVM or SQLite bug. I prefer to check everything that is possible before addressing them a bug. Maybe I'm forgetting to configure something or need to add some flag to the compiler that I'm not doing.
I appreciate any help. Again, the bug only occurs on projects compiled with LLVM (even with the default sqlite). If I run the same queries on the Terminal version of sqlite3, everything goes fine.
UPDATE:
This code works. It creates a new database, a new virtual table using fts, insert a couple of items and then execute the select. I'll try more complex queries later, but, for now, it seems that the issue with my app might be, as expected, a bug in my code.
NSArray *dirPaths = dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
sqlite3 *database;
// Build the path to the database file
NSString *databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"test.db"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
NSError *error = nil;
[filemgr removeItemAtPath:databasePath error:&error];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
char *errMsg;
const char *sql_stmt = "CREATE VIRTUAL TABLE IF NOT EXISTS pages USING fts3(title, body);";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table");
} else {
sql_stmt = "INSERT INTO pages(docid, title, body) VALUES(53, 'Home Page', 'SQLite is a software...');";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to insert");
}
sql_stmt = "INSERT INTO pages(title, body) VALUES('Download', 'All SQLite source code...');";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to insert");
}
}
sqlite3_stmt *statement;
const char *query_stmt = "select * from pages where body match 'soft*';";
if (sqlite3_prepare_v2(database, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSLog(#"%# - %#", [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)],
[[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)]);
} else {
NSLog(#"no results");
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
} else {
NSLog(#"Failed to open/create database");
}
After all, I've found the bug. It was in my code. In summary, that what I've found out:
If I have something like that (I know it's weird/wrong):
int a = 0;
a = a++;
NSLog(#"%d", a);
the logged value will be 1 if this code is compiled with gcc and 0 if compiled with llvm.
I don't know why, but that's another question :)

Resources