I have an SQL schema that I need to feed to SQLite in my Objective-C command-line app. I’d like the schema to be a part of the binary, so that I can distribute just one file. Unlike a regular Mac or iOS app, the binary has no resource bundle, so the traditional way of storing resources inside the app bundle is out. Is there an elegant way to include the schema in the source? I know I can simply store it as a multiline string in a header, but that sucks.
Schema.h
extern NSString * someSchema;
Schema.m
NSString * someSchema = #"CREATE TABLE IF EXISTS blah...."
#"More SQL here"
#"more SQL here";
What about creating an array of sql statements, and then you use an enum to access the statements.
Schema.h file:
static const NSString *sqlStatements[] = {
#"CREATE TABLE...",
#"SELECT * FROM ...",
... // Lots of other statements
#"DELETE ..."
};
typedef enum {
SQLCREATECommand=0,
SQLSelectCommand,
... // Matching enums
SQLDeleteCommand
} SQLCommands;
Used in some other file:
NSString *stmt = sqlStatements[SQLCREATECommand];
The benefit of doing it this way is that the code becomes more maintainable.
One interesting solution I have come up with is extended attributes. It’s possible to add a Run Script phase to the Xcode build process that does something like:
xattr -w com.company.Schema "`cat SQL/schema.sql`" \
${BUILT_PRODUCTS_DIR}/${EXECUTABLE_NAME}
And then in runtime:
const size_t maxSchemaSize = 1000;
char schema[maxSchemaSize] = {0};
getxattr(argv[0], "com.company.Schema", schema, maxSchemaSize, 0, 0);
NSLog(#"%s", schema);
This way I can keep the schema in a separate file without mangling it into a header file. The obvious downside is that the extended attribute might not survive some file operations.
Related
I have a handful of SQL queries in my objective-c project. Is there any way I can store these in separate files query1.sql, query2.sql, in my tree and sub them in at compile time?
Motivating this is the fact that my queries are either copy-pastable but unreadable (no whitespace):
NSString* query = #"SELECT A.a, B.* from myTable A INNER JOIN otherTable B ON ...
Or readable but littered with line splices:
NSString* query = #"SELECT A.a, B.* \
FROM myTable A
INNER JOIN \
...
I am aware of .strings files for objective c, but thought they were more for localization. Is that right?
EDIT: I could have been more clear: I want to store my individual sql statements in separate text files that 1.) are valid SQL independently, and 2.) can be imported at compile time.
you can try this
NSString* query =
#"SELECT A.a, B.* "
"FROM myTable A "
"INNER JOIN "
"..."
or move the sql string to file query1.sql
"SELECT A.a, B.* "
"FROM myTable A "
"INNER JOIN "
"..."
and
NSString *query= #(
#include "query1.sql"
);
I'm going to say "no", but we'll see if anyone disagrees!
If you are prepared to forgo the compile time inclusion just have the sql files copied into your bundle and read them in. This is of course a runtime cost on each run, but that probably isn't significant, but YMMV!
Another, more involve approach - but you only need to write it once - is to develop a script, say in Ruby, which transforms your SQL files into Objective-C source and include it as build phase. Apple has an example somewhere of doing this (using Ruby, hence the suggestion of Ruby - start with Apple's sample and edit it) to run the preprocessor over strings files. The approach does work very well, but you need to put the effort in first and then can reuse it in all your projects.
HTH
The other recommendations for macros or multi-line strings are good, but if you want to put them in .strings files, that's fine. Just use NSLocalizedStringFromTable so that you can specify a different strings table than your localized strings. And of course, don't localize the SQL.
But I'd probably build something custom that read the file and cached its contents. Building exactly what you want would probably take about fifteen minutes, and there's no harm in it if it makes development simpler.
If you want to store all your select queries in their own files (like you say), just use NSBundle to load find your resource file by some token. Something like:
NSString * SQL(NSString *key) {
NSString *path = [[NSBundle mainBundle] pathForResource:key ofType:#"sql"];
return [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
}
NSString * const SELECT_EM = #"SELECT_EM";
NSString *select_query = SQL(SELECT_EM);
Then you'd have a file called SELECT_EM.sql in your resources directory.
Again, lots of choices; they're all fine. If you're calling SQL a lot, you should probably add an NSDictionary cache for them so you don't have to load the string every time.
I've got a class for storing constants.
So, there are two files that call Constant.h and Constant.m
This is what I have in .h file:
#import <Foundation/Foundation.h>
enum kParams {
kFirstName = 0,
kLastName = 1
};
extern NSString * const kNotificationUpdateMainMenu;
This is what I have in .m file:
#import "Constants.h"
NSString * const kNotificationUpdateMainMenu = #"kNotificationUpdateMainMenu";
For first time it works good, but when I try to add some other const (kNotificationFbLoginSuccsess for example) other classes don't see it.
This is a message that shows me which problem I have. But I don't understand how my other constants work without this issue (just new constant that I add get this error).
/Users/developer/Documents/Projects/Test/Test/Test/AppDelegate.m:121:64: Use of undeclared identifier 'kNotificationFbLoginSuccsess'
I found some way how to fix it:
Open organizer
Clear derived data
Delete project.xcworkspace file and xcuserdata
Close Project
Relaunch Xcode
but as I think is too much operations that I can add one constant. How come?
Your "global" constant is not actually external (separately compiled and later linked together). Take the easy way out and place NSString * const kNotificationUpdateMainMenu = #"kNotificationUpdateMainMenu"; into the header file. The method file needs nothing.
I would use #define kNotificationUpdateMainMenu #"kNotificationUpdateMainMenu" to perform the spell checking. The compiler will create one shared instance of the constant string for the entire compilation.
I have used the statfs(2) system call to get many characteristics of a Mac OS X filesystem, but it doesn't tell me if the filesystem is case-sensitive or not.
I need this information as the application I am developing will be moving many files around and I want to detect potential loss of data due to files being moved from a case-sensitive filesystem to a case-insensitive filesystem.
Can anyone suggest a way of detecting this?
If you're already using stat(2), then you can easily use pathconf(2) with the _PC_CASE_SENSITIVE selector (result 0 = case-insensitve, 1 = case-sensitive. Note that the man page is out of date, but the _PC_CASE_SENSITIVE and _PC_CASE_PRESERVING are supported. By convention, if a file system doesn't support _PC_CASE_SENSITIVE selector then it is case-sensitive.
I’ve looked around and haven’t found an API for that. There are two possibilities I can think of:
Creating a temporary file and trying to open it with a different case pattern, e.g. creating "a9999" and trying to open "A9999". Considering that neither "a9999" nor "A9999" were available on that particular directory, the filesystem is case-sensitive if and only if opening "A9999" fails.
Running diskutil(8) against the filesystem. It reports case-sensitive, -insensitive file systems differently: Name: Mac OS Extended (Case-sensitive) vs. Name: Mac OS Extended (not journaled).
Since diskutil(8) is able to identify that, it could be the case that this information is available via some API or system call.
Edit: It turns out that NSURL has a set of methods that work on file system properties. In particular, -getResourceValue:forKey:error with the key being NSURLVolumeSupportsCaseSensitiveNamesKey will tell you whether a given filesystem (represented as an NSURL instance) supports case sensitive names.
See the following code for an example of its use.
#include <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString *path = [NSString stringWithCString:argv[1] encoding:NSASCIIStringEncoding];
NSURL *filesystem = [NSURL fileURLWithPath:path isDirectory:YES];
NSNumber *caseSensitiveFS;
BOOL hasCaseSensitiveResource;
hasCaseSensitiveResource = [filesystem getResourceValue:&caseSensitiveFS
forKey:NSURLVolumeSupportsCaseSensitiveNamesKey error:NULL];
if (hasCaseSensitiveResource)
{
if ([caseSensitiveFS intValue] == 1)
{
NSLog(#"%s is a case sensitive filesystem", argv[1]);
}
else
{
NSLog(#"%s is a case insensitive filesystem", argv[1]);
}
}
else
{
NSLog(#"can't query %s for case sensitiveness", argv[1]);
}
[pool drain];
return 0;
}
Output example:
./testcase /
/ is a case insensitive filesystem
./testcase /Volumes/Disk\ Image/
/Volumes/Disk Image/ is a case sensitive filesystem
./testcase nonono
can't query nonono for case sensitiveness
Create a temporary file with uppercase letters and check if the file exists using lowercase letters, if the test fails the file system is case-sensitive.
Look here for some code to find the HFS subtype of a device:
http://www.opensource.apple.com/source/libfs/libfs-3/FSFormatName.c
The routine is_hfs will return the hfs subtype. If the subtype is kHFSXSubType or kHFSXJSubType, then it's an HFSX (case sensitive) device.
I am a little confused as to when it's best to use:
static NSString *AppQuitGracefullyKey = #"AppQuitGracefully";
instead of
#define AppQuitGracefullyKey #"AppQuitGracefully"
I've seen questions like this for C or C++, and I think what's different here is that this is specifically for Objective C, utilizing an object, and on a device like the iPhone, there may be stack, code space or memory issues that I don't yet grasp.
One usage would be:
appQuitGracefully = [[NSUserDefaults standardUserDefaults] integerForKey: AppQuitGracefullyKey];
Or it is just a matter of style?
Thanks.
If you use a static, the compiler will embed exactly one copy of the string in your binary and just pass pointers to that string around, resulting in more compact binaries. If you use a #define, there will be a separate copy of the string stored in the source on each use. Constant string coalescing will handle many of the dups but you're making the linker work harder for no reason.
See "static const" vs "#define" vs "enum". The main advantage of static is type safety.
Other than that, the #define approach introduces a flexibility of inline string concatenation which cannot be done with static variables, e.g.
#define ROOT_PATH #"/System/Library/Frameworks"
[[NSBundle bundleWithPath:ROOT_PATH#"/UIKit.framework"] load];
but this is probably not a good style :).
I actually would recommend neither, you should use extern instead. Objective-c already defines FOUNDATION_EXPORT which is more portable than extern, so a global NSString instance would look something like this:
.h
FOUNDATION_EXPORT NSString * const AppQuitGracefullyKey;
.m
NSString * const AppQuitGracefullyKey = #"AppQuitGracefully";
I usually put these in declaration files (such as MyProjectDecl.h) and import whenever I need.
There are a few differences to these approaches:
#define has several downsides, such as not being type safe. It is true that there are workarounds for that (such as #define ((int)1)) but what's the point? And besides, there are debugging disadvantages to that approach. Compilers prefer constants. See this discussion.
static globals are visible in the file they are declared.
extern makes the variable visible to all files. That contrasts with static.
Static and extern differ in visibility. It's also notable that neither of these approaches duplicates the string (not even #define) as the compiler uses String Interning to prevent that. In this NSHipster post they show proof:
NSString *a = #"Hello";
NSString *b = #"Hello";
BOOL wtf = (a == b); // YES
The operator == returns YES only if the two variables point at the same instance. And as you can see, it does.
The conclusion is: use FOUNDATION_EXPORT for global constants. It's debug friendly and will be visible allover your project.
After doing some search (this question/answer among other things) I think it is important to say that anytime when you are using string literal #"AppQuitGracefully" constant string is created, and no matter how many times you use it it will point to the same object.
So I think (and I apologize me if I'm wrong) that this sentence in above answer is wrong: If you use a #define, there will be a separate copy of the string stored in the source on each use.
I use static when I need to export NSString symbols from a library or a framework. I use #define when I need a string in many places that I can change easily. Anyway, the compiler and the linker will take care of optimizations.
USING #define :
you can't debug the value of identifier
work with #define and other macros is a job of Pre-Processor,
When you hit Build/Run first it will preprocess the source code, it will work with all the macros(starting with symbol #),
Suppose, you have created,
#define LanguageTypeEnglish #"en"
and used this at 2 places in your code.
NSString *language = LanguageTypeEnglish;
NSString *languageCode = LanguageTypeEnglish;
it will replace "LanguageTypeEnglish" with #"en", at all places.
So 2 copies of #"en" will be generated.
i.e
NSString *language = #"en";
NSString *languageCode = #"en";
Remember, till this process, compiler is not in picture.
After preprocessing all the macros, complier comes in picture, and it will get input code like this,
NSString *language = #"en";
NSString *languageCode = #"en";
and compile it.
USING static :
it respects scope and is type-safe.
you can debug the value of identifier
During compilation process if compiler found,
static NSString *LanguageTypeRussian = #"ru";
then it will check if the variable with the same name stored previously,
if yes, it will only pass the pointer of that variable,
if no, it will create that variable and pass it's pointer, next time onwards it will only pass the pointer of the same.
So using static, only one copy of variable is generated within the scope.
I've reading in several post and in Apple's code guidelines that in Objective-C String constants should be defined as extern NSString *const MY_CONSTANT; and that the #define directive should be avoided. Why is that? I know that #define is run at precompile time but all string will share the same memory address. The only advantage I read was that if the constant must be updated or changed you don't have to recompile your entire project. So that i s the reason why #define should be avoided?
Thanks
UPDATE: In this case is good to use a #define or there is a better approach?
/* Constants Definition */
#define SERVER_URL #"http://subdomain.domain.edu.ar/Folder/"
NSString *const ServerURL = SERVER_URL;
NSString *const LoginURL = SERVER_URL#"welcome.asp";
NSString *const CommandURL = SERVER_URL#"com.asp";
A practical reason to use the constant as opposed to the definition is that you can do direct comparisons (using ==) instead of using isEqual:. Consider:
NSString * const kSomeStringConstant = #"LongStringConstantIsLong";
...
[someArray addObject:kSomeStringConstant];
if ([someArray lastObject] == kSomeStringConstant)
{
...
}
This would work, since the == comparison would be comparing identical const pointers to a single NSString object. Using #define, however:
#define STRING_CONSTANT #"MacrosCanBeEvil";
...
[SomeArray addObject:STRING_CONSTANT]; // a new const `NSString` is created
if ([someArray lastObject] == STRING_CONSTANT) // and another one, here.
{
...
}
This would not work out, since the two strings would have unique pointers. To compare them effectively, you would have to do a character-by-character comparison using isEqual:
if ([[someArray lastObject] isEqual:STRING_CONSTANT])
{
...
}
This can be far more costly in terms of execution time than the simple == comparison.
Another motivation could be the size of the executable itself. The #defined constant would actually appear, in place, wherever it was used in the code. This could mean that the string appears many times in your executable. In contrast, the constant should (with modern compilers) be defined only once, and all further usages would make reference to the pointer to that one definition.
Now, before anyone yells at me for premature optimization, consider that the two approaches are almost identical in terms of implementation, but the const pointer method is far superior in terms of code size and execution time.
It's not necessarily guaranteed that there will only be one NXConstantString object for a given string literal in an entire application. It seems pretty likely that different compilation units might have different objects for the same constant string. For example, if somebody writes a plugin, one constant string will be generated for occurrences of that NSString literal in the plugin and one will be generated for occurrences in the host application, and these will not be pointer-equal.
The best argument I have heard is that const strings show up in the debugger, whereas macros do not.
static NSString * const SERVER_URL = #"http://subdomain.domain.edu.ar/Folder/";
As far as I know, #define only lets you define C-style string constants. To create a constant NSString object, you have to declare it in the header, and then give it a value in one of your .m files.
Header file:
extern NSString *MyConstantString;
Main file:
NSString *MyConstantString = #"String value";