When I run this :
#interface Database : NSObject {
sqlite3 *database;
}
+(void)openDatabase;
#end
#implementation Database
+(void)openDatabase
{
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *databaseNameandPath = [NSString stringWithFormat:#"%#/DatabaseSnax.sqlite",docDir];
NSString *databasePath=[[NSString alloc]initWithFormat:databaseNameandPath];
if(sqlite3_open([databasePath UTF8String], &database) != SQLITE_OK)
{
NSLog(#"Error when open the database");
}
[databasePath release];
}
I have this error: instance variable database accessed in class method
How I can solve this issue and I need to keep my method (open database) as a static method so I can use it by class name, for example:
[Database openDatabase];
One cannot possibly access instance variables from class methods. You may declare a global variable, however:
static sqlite3 *database;
// ...
+ (void) openDatabase {
sqlite3_open(filename, &database);
// ...
}
You're attempting to access database from a class method (which are different from instance methods).
Change that declaration from:
+ (void) openDatabase;
to
- (void) openDatabase;
and instantiate your Database object via the traditional alloc + init and you'll be on your way.
I like H2CO3's answer too (and +1 to him), but my answer (which is what most people have to do with Objective C objects) may be more practical for what you are trying to do.
For reference, static means different things to different people/languages. Objective-C being mostly C plus a bunch of syntax enhancements, the keyword static in Objective-C has the same meaning as it does in C, which relates to the visibility of the symbol with respect to linking. This is subtly-but-importantly different from how Java and C# use the word static.
Objective-C doesn't have syntax for declaring "static" (in Java/C# parlance) or "class" variables. The runtime has "support" for them (witness the existence of: class_getClassVariable) but there's no syntax to declare them, so it's sort of a dead end. (If I had to guess, I'd bet that this feature exists in the runtime to support bridges to other languages/runtimes that use static/class variables.) As other people have suggested, the common way around this is to use global variables (or function statics (static in the C linkage sense.))
Related
Let's say this is my init method
- (id)initWithClient:(id <Client>)client
andDataStorage:(DataStorage *)storage
{
if (self = [super init])
{
self.client = client;
self.storage = storage;
}
return self;
}
Then I want to write a macro that somehow logs the parameters passed to a method, by wrapping the parameter with a defined macro. Is this possible in any way?
The problem is at runtime it's not possible to find out the type of a parameter passed to a method. So I'm trying to find a hack around it, and do it at compile time.
// somehow achieve this, and log the value inside the marco
#define INJECT(x) NSLog(#"%#", x)
- (id)initWithClient:(INJECT(id <Client>))client
andDataStorage:(INJECT(DataStorage *))storage
{
}
expected log in console:
id <Client>
DataStorage *
At the risk of running into what appear to be crossed wires in the comments: you can get the parameter types passed to a method at runtime.
E.g.
NSMethodSignature *signature =
[class methodSignatureForSelector:#selector(someSelector:)];
for(int argument = 2; argument < signature.numberOfArguments; argument++)
{
const char *argumentType = [signature getArgumentTypeAtIndex:argument];
// this is where it gets a bit messy...
if(!strcmp(argumentType, #encode(int))) NSLog(#"an integer");
if(!strcmp(argumentType, #encode(float))) NSLog(#"a float");
// ... etc, etc, etc ...
}
For any passed objects, use [object class] since all objects look the same at the runtime level — think of e.g. NSArray -addObject:; the runtime knows an object type will be passed in but it could be any object type.
See Apple's documentation on Type Encodings for information on what's going on there with those #encodes.
Whilst not an answer to the question as such. I would not recommend doing what you are asking about. I've seen far to much code where people have logged every single method call and argument (horribly over-complicated Java Enterprise stuff). The result has always been obscenely large logs that tell you next to nothing because of the amount of work it takes to find what you are after.
My recommendation would be that logging is important, but you should do targeted logging that clearing shows the state of relevant data at specific points which are important to understanding the flow.
Like others, I'm not sure what you are really after, or whether it is a good idea/design etc. But I wonder whether you are approaching the problem the wrong way. So let's take a look and maybe it will help you. From what I see you:
Want to find some way of obtaining the declared types of method parameters, in the form of strings, at runtime.
You are trying to tackle this by adding macros to the source. This tells me that you are not trying to do this for methods in a binary library that you are dynamically loading, but to methods in source you are compiling and are prepared to modify to achieve your goal.
Looked at that way, what is the problem? If you are prepared to add macros to your source why not simply add data declarations that contain the information you want - a mapping from a selector to an order list of parameter types as strings.
Is the issue that you want to extract the information in some automated way and were intending adding your macros by some automated process?
You can arrange for an Xcode project to run a source file through some other program by changing the file extension. Apple provide examples of using this to pre-process strings files - the files are fed through a Ruby script which produces a strings file which Xcode then handles as usual. Will that address your needs? Could you write a script/application (doesn't need to be in Ruby) which could add the information you need "on the fly" - take source in, produce modified source out which Xcode then compiles as usual? Note that the Clang compiler itself is designed to be called as a library so you can even use it to help you parse your source to extract the information you are after.
If none of those approaches suit consider that the debugger knows the correct types at runtime, and it gets those from the symbol information generated for it. There are library functions provided to help reader debugger information, so you should be able to write code which uses the same information the debugger does.
Hope those ideas help you, though I'm still not clear what you are trying or whether it makes sense!
due to objC being dynamically typed, all classes have the type id. The information about the declared types is erased. They are merely hints for the developer and to enable the compiler to do some type checking (again purely for the dev's benefit)
So while #encode works for 'primates' and structs and stuff, for classes all is equal... as there are not really object types for runtime
'Solution': Store the class names of method argumentsin a map manually and then COMBINE that info with #encode;s info to log the stuff.
working sample:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
NSDictionary *DDParamsMap(void);
NSDictionary *DDParamsMap() {
static NSDictionary *dict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//TODO
//add all methods that are have objc classes passed
//add the classes or NSNull
dict = #{#"Test_initWithArray:data:number:": #[NSArray.class, NSData.class, NSNull.null]};
});
return dict;
}
void DDLogParamsOf(Class class, SEL sel);
void DDLogParamsOf(Class class, SEL sel) {
//
//try internal lookup first (so we get class names
//
NSString *className = #(class_getName(class));
NSString *methodName = NSStringFromSelector(sel);
NSString *key = [NSString stringWithFormat:#"%#_%#", className, methodName];
NSArray *types = DDParamsMap()[key];
//
// loop
//
NSMethodSignature *signature = [class instanceMethodSignatureForSelector:sel];
if(!signature) {
signature = [class methodSignatureForSelector:sel];
}
//if the array doesnt have the right number of values, screw it!
if(types.count != signature.numberOfArguments - 2) {
types = nil;
}
for(int argument = 2; argument < signature.numberOfArguments; argument++) {
id type = types[argument - 2];
if(type && ![type isKindOfClass:[NSNull class]]) {
NSLog(#"class is %#", type);
}
else {
const char *argumentType = [signature getArgumentTypeAtIndex:argument];
// this is where it gets a bit messy...
if(!strcmp(argumentType, #encode(int))) NSLog(#"an integer");
if(!strcmp(argumentType, #encode(float))) NSLog(#"a float");
if(!strcmp(argumentType, #encode(id))) NSLog(#"it is a class");
// ... etc, etc, etc ...
}
}
}
#define LogParams() DDLogParamsOf(self.class, _cmd);
#interface Test : NSObject
+ (void)testMethofWithFloat:(float)f;
- (id)initWithArray:(NSArray*)a
data:(NSData*)d
number:(int)i;
#end
#implementation Test
+ (void)testMethofWithFloat:(float)f {
LogParams();
}
- (id)initWithArray:(NSArray*)a
data:(NSData*)d
number:(int)i
{
LogParams();
return nil;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
[Test testMethofWithFloat:3.0f];
Test *t = [[Test alloc] initWithArray:#[] data:[NSMutableData data] number:1];
t = nil;
}
}
I'm trying to determine if there is an elegant solution to this issue.
Say I have a global defined in some header:
Constants.h:
extern NSString *someGlobal;
And then I wish to use this global in some other class:
Foo.m
NSString *localVariable = someGlobal;
This all works just fine if I initialize the global like this:
Constants.m:
NSString *someGlobal = #"Some String Literal";
But lets say I need to initialize the global to something that isn't a compile-time constant. In such cases I typically do this:
Constants.m:
#implementation Constants
+ (void)initialize {
someGlobal = ... // some non-trivial initialization
}
#end
Now I have a potential problem in Foo.m. If no reference has been made to the Constants class when I try to use someGlobal, the result is nil. A workaround is to do:
Foo.m (or in some app startup code):
[Constants class];
That will trigger the initialize method of the Constants class and someGlobal will be properly initialized. As long as this is done before any runtime use of someGlobal, things work fine.
Is there a better way to initialize extern globals with non-compile time constants without the need to call code such as [Constants class] at app startup?
A more idiomatic way in Objective-C is using a singleton instead of multiple globals. Here is how:
#interface Globals
#property (readwrite,nonatomic) NSString *myString;
#property (readwrite,nonatomic) int myInt;
+(Globals*) instance;
#end
+(Globals*) instance {
static dispatch_once_t once;
static Globals *inst;
dispatch_once(&once, ^{
inst = [[Globals alloc] init];
inst.myString = #"Some String Literal";
inst.myInt = 42;
});
return inst;
}
Now you can use your globals like this:
NSLog(#"Global string: %#", [Globals instance].myString);
NSLog(#"Global string: %d", [Globals instance].myInt);
No, there is no better way. Logically, if some piece of code must execute before a variable is intialized, you have to take steps to make sure that happens.
You could arrange the flow of your program's code so as to guarantee that the Constants class get initialized before any other piece of code executes which needs it. For example, by tweaking the order in which things are initialized in your program and following the order of code execution from main() on down to prove to yourself that it works. But short of that (and the safest thing in any case), you would use your technique to force it to be made valid right before you use it.
Like dasblinkenlight's answer, this may not be exactly what you are looking for but it's another approach.
I would make class methods that returns the value you are looking for like this:
+(NSString *)someConstant {
static NSString *constant;
if(constant == nil)
constant = //your initialization here;
return constant;
}
Then where you need to use it just call [Constants someConstant];
Other random thoughts:
A constant that isn't some compile time value isn't really what extern variables are for and this method insures that the variable is initialized every time you use it. The class using the constant has to know about your class anyway or it wouldn't have imported its header file
How do I declare and use small helper functions inside my normal methods ?
In on of my objective-c methods I need a function to find an item within a string
-(void) Onlookjson:(id) sender{
NSString * res = [[sender gstring] copy];
persInfoBirth.text = getKeyValue(res, #"Birth");
}
I came up with a normal C type declaration for helper function getKeyvalue like this
NSString * getKeyvalue(NSString * s, NSString * key){
NSString *trm = [[s substringFromIndex:2] substringToIndex:[s length]-3];
NSArray *list = [trm componentsSeparatedByString:#";"];
//....
NSString res;
res = [list objectAtIndex:1];
//...
return res;
}
Example input string in s:
s=#"{ Birth = "1910"; Death = "1936"; }";
Anyway I get an exception "unrecognized selector sent to instance" for any of the two first lines in the helper function
How do I declare helper functions that are just to be used internally and how to call them safely ?
regards
Martin
Is this the real code? Do you get zero errors and warnings from the compiler? You must not ignore compiler warnings and you should turn on the Static Analyser in addition to the standard warnings.
There are many things wrong with the above code, most of which are nothing todo with declaring and calling methods. There is no way the above code could compile so maybe it pasted incorrectly or something..
Anyway.. declaring and using methods. Why are using a c function? Unless you have a good reason why not use Objective-c ? If you do have a good reason to use a C function the your definition should be:-
NSString *getKeyvalue( NSString *s, NSString *key ){
...
}
note the arguments. As NSString instances reside in the heap (not on the stack) you always want to pass pointers to them.
You then need to put the declaration in the header file:-
NSString *getKeyvalue( NSString *s, NSString *key )
EDIT:
In Objective-c there is no distinction between normal methods and helper methods, there is only one kind, and you have aleray written one
- (void)onLookJson:(id)sender { .. }
Taking it apart..
All methods begin with + or –, indicating Class method or Instance method. As you are familiar with C++ i guess you know what this means.
(void) is the return type. ie this method doesn't return a value. If it did it might look like (float) or (NSString *) or (id).
onLookJson: is the method name and the method takes 1 argument. Notice that the ':' is actually part of the name. This method is never is any circumstance just 'onLookJson'. An argument must always follow the :, so a method that doesn't take any arguments must not have one.
Ex
- (NSString *)fullName { .. }
This is an instance method, for example of a Person Class, you would call it like:-
NSString *theName = [aPerson fullName];
So
a method name that takes no
arguments is like 'speak'
a method
name that takes 1 argument is like
'speakTo:'
a method name that takes 2
arguments is like 'speakTo: language:'
a method name that takes 3
arguments is like 'speakTo: language: volume:'
etc.
All that is left is to put in the argument types and names.
Your function definition:
NSString *getKeyvalue( NSString *s, NSString *key ){
would become..
- (NSString *)getValue:(NSString *)s key:(NSString *)key { .. }
again, you need to declare it in the header or you will get a compiler warning.
- (NSString *)getValue:(NSString *)s key:(NSString *)key;
In Objective-C, I can write:
id pString = #"Hello, World.";
and the compiler will instantiate an NSString without me needing to explicitly call a factory method. However, NSString is really just a Foundation class and thus presumably not part of the actual Objective-C language definition.
So when I write #"String", how does the compiler know to build an NSString in particular, and not some other string-like object? In other words, where does the Objective-C language stop and the Foundation library start?
When you write Objective-C code outside of Cocoa or GNUStep environments, #"..." is not linked to NSString.
In this case, gcc provides an option for specifying a class associated to literal strings:
-fconstant-string-class=class-name
Use class-name as the name of the class to instantiate for each
literal string specified with the syntax "#"..."". The default
class name is "NXConstantString".
The #"" directive appears to be built-in to the objective-c compiler.
For instance, if you remove all #imports from your .m source file (& prefix header), the following line will be a syntax error:
NSString *string = #"ABCD"; // (Doesn't know anything about NSString class)
However, if you change the Foundation NSString type to the built-in void type, it will compile just fine:
void *string = #"ABCD";
So, even without Foundation's NSString definition, the compiler knows how to turn #"" into something that can become an NSString instance at runtime (it probably won't instantiate without Foundation, but the compiler doesn't seem to mind); Since it accepts the syntax without needing any external library definitions, the compiler sees #"" as part of the language.
Your code, however, won't be able to make use of any #"" instance without importing Foundation.h, so from the point of view of your program, #"" is part of the library.
Another interesting bit is that an Objective-C string literal (#"" notation) returns the same type as an explicitly instantiated NSString object:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
printf("string literal class: %s\n", object_getClassName(#"a string literal"););
NSString *str = [[NSString alloc] initWithUTF8String:"asdf"];
printf("explicit NSString class: %s", object_getClassName(str));
[pool drain];
return 0;
}
I vaguely remember that in other, older implementations of Objective-C, the string literal actually returned an object of a slightly different class, but that could be used interchangeably with NSString/NSCFString. Not totally sure on that part, though.
For my brand new ORM (chibi-ORM) I face a design decision.
I'm building the API for relationships, like:
#interface SamplePerson : DbObject {
NSString *name;
DbObjectRelation *cities;
}
Now, for DbObjectRelation I have this mockup:
#interface DbObjectRelation : NSObject {
NSMutableArray *childRows;
DbObject *parent;
BOOL isLoaded;
}
-(void) load;
-(void) addObject:(DbObject *) childRow;
-(void) removeObject:(DbObject *) childRow;
- (id)initWitParent:(DbObject *)parentObject;
So, I need a way inside load of know which database use for load the records.
I think in have in my DB connection something like:
static NSString *currentDbNameSingleton = nil;
+(NSString *)currentDbName {
#synchronize( self ) {
if ( currentDbNameSingleton == nil ) {
currentDbNameSingleton = [[NSString alloc]
}
}
return sharedInst;
}
+(void) setCurrentDbName(NSString *)name {
#synchronize( self ) {
currentDbNameSingleton = [name copy];
}
}
But wonder if is better build the DB class as a singleton. This is for iPhone projects...
Using a set of class accessors/mutators around a static variable like you're currently doing is pretty reasonable. Another pretty common solution if you're not going to change the database name would be to put it in a separate file, say Constants.h, along with other constant strings like dictionary and user defaults keys.
If you have additional code related to your database that you can refactor into a single place, then that's a pretty good point to built a singleton class for your database stuff. I wouldn't create a class just to hold a string though.
For strings that don't change you should create them as constants.
i.e. In your database connection file:
const NSString *CurrentDbName = #"name of database";
Note the capitlization to convey compile-time generation and constant-ness.
It's slightly more efficient than a singleton for a string. Of course, you can't do this with other types of Objective-C object because they can't be created by the compiler. For these, use a singleton.
If other parts of your program need access to the string, you should use an extern reference in your database's header file:
extern const NSString *CurrentDbName;