I have a lot of methods that repeat this simple boilerplate:
- (id)myObject {
if(!_myObject) {
self.myObject = [_myObject.class new];
}
return _myObject;
}
So I want to replace this with a simple macro:
#define default_init(instance) \
if(!instance) instance = [instance.class new]; \
return instance;
So that I would only have to call:
- (id)myObject {
default_init(_myObject);
}
The above code currently compiles, but the issue is that the macro directly sets the instance variable's value. Instead, I'd like to call self.instance = value;
So instead of
if(!instance) instance = [instance.class new];
I'd like something like;
if(!instance) self.instance = [instance.class new];
But obviously the current code does not allow for this. How might I accomplish something like this?
With this macro:
#define default_init(class, instance) \
if ( ! _##instance ) { \
self.instance = [class new] ; \
} \
return _##instance
I was able to create this instance method:
- (NSMutableArray*) myObject {
default_init(NSMutableArray, myObject) ;
}
I had to add a parameter defining the class, because _myObject is still nil, therefore _myObject.class is nil.
This StackOverflow question and this Cprogramming page recommend wrapping your multi-line macro in do {...} while(0):
#define default_init(class, instance) \
do { \
if ( ! _##instance ) { \
self.instance = [class new] ; \
} \
return _##instance ; \
} while(0)
If you really wanted to, you could make a macro that defines the entire method:
#define default_getter(class, instance) \
- (class*) instance { \
if ( ! _##instance ) { \
self.instance = [class new] ; \
} \
return _##instance ; \
}
And use it thusly:
default_getter(NSMutableArray, myObject)
Instead of a macro to build a getter method, I usually declare the property:
Instance.h
#interface Instance : NSObject
#property NSMutableArray* myObject ;
#end
and override - init to initialize the property:
Instance.m
- (id) init {
self = [super init] ;
if ( self ) {
self.myObject = [NSMutableArray new] ;
}
return self ;
}
Ok, this is my take on it.
Note: this was done just to see if it could be done. I don't think it would be a good idea to use this in shipping code.
First import #import "EXTRuntimeExtensions.h" from libextobjc. Then:
#define underize(name) _##name
#define property_type(property) \
property_attr(property)->objectClass
#define property_attr(propertyName) \
ext_copyPropertyAttributes(class_getProperty(self.class, # propertyName))
#define default_init(propertyName) \
- (id)propertyName { \
if(!underize(propertyName)) self.propertyName = [property_type(propertyName) new]; \
return underize(propertyName); \
} \
Then say you have a property:
#property (nonatomic) NSArray *myArray;
You can do:
default_init(myArray);
And that creates a default getter.
Related
I have Logger.h with following code:
#import <Foundation/Foundation.h>
#import <asl.h>
#define LogDebug(format, ...){ \
Logger(ASL_LEVEL_DEBUG, format,##__VA_ARGS__); }
#define Logger(LEVEL,format, ...) \
LogLocal(LEVEL,format,##__VA_ARGS__);
#define LogLocal(LEVEL, format, ...) \
va_list arg_list; \
va_start(arg_list, format); \
// ...\
va_end(arg_list); \
I call log from Obj-C as:
LogDebug(#"Name is called with flag: %#", collectName ? #"YES" : #"NO");
However I get an error:
'va_start' used in function with fixed args
How to get rid of this problem?
EDIT 1:
I tried to call also: AFLogLocal(LEVEL,format,...); - same error
EDIT 2:
if I'll remove asl_log and replace with NSLog - it will work:
#define LogLocal(LEVEL,format, ...) \
NSLog((#"XXX: %s " format), __PRETTY_FUNCTION__, ##__VA_ARGS__);
EDIT 3
credits to #Amin Negm-Awad, replaced LogLocal with function in .m file as:
void LogLocal(int level, NSString *format, ...){
va_list arg_list;
va_start(arg_list, format);
va_end(arg_list);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
asl_add_log_file(NULL, STDERR_FILENO);
asl_log(NULL, NULL, (level), "XXXX: %s", [formattedString UTF8String]);
}
works as expected.
You use macros that are expanded. But they do not create a scope or a call on expansion. It's simple text replacement.
// Somewhere in a function
void f(void)
{
…
LogDebug(#"Name is called with flag: %#", collectName ? #"YES" : #"NO");
…
}
This will expand at the end to something like this:
// Somewhere in a function
void f(void)
{
…
// LogDebug(#"Name is called with flag: %#", collectName ? #"YES" : #"NO");
va_list arg_list;
va_start(arg_list, #"Name is called with flag: %#");
// ...
va_end(arg_list);
…
}
Since f() takes no vargs, there are no vargs.
It might help to make LogLocal() a function, not a macro:
void LogLocal( int level, NSString *format, ... );
Implementation:
void LogLocal( int level, NSString *format, ... )
{
va_list arg_list;
va_start(arg_list, format);
// ...
va_end(arg_list);
}
To your Edit 2:
Yes, this works, because you do not touch the arg list, but pass the args to a function taking vargs. But you do not need that trick. Instead make LogLocal() a function and it will be called similar to NSLog() without any error.
I use following technique to manage my logs. I print logs to asl_log and before, regards to flag [DebugManager shared] isDebugging I want to send log line to other class (method addLogEvent)
#if !defined(TheLog)
#define TheLog(fmt, ...) { \
if ([[DebugManager shared] isDebugging]) \
addLogEvent(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__); \
}
#endif
#define __AF_MAKE_LOG_FUNCTION(LEVEL, NAME) \
inline void NAME(NSString *format, ...)\
{ \
TheLog(__PRETTY_FUNCTION__,format,##__VA_ARGS__);\
va_list arg_list; \
va_start(arg_list, format); \
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; \
asl_add_log_file(NULL, STDERR_FILENO); \
asl_log(NULL, NULL, (LEVEL), "PREFIX: %s", [formattedString UTF8String]); \
va_end(arg_list); \
}
// Something has failed.
__AF_MAKE_LOG_FUNCTION(ASL_LEVEL_ERR, AFLogError)
// Something is amiss and might fail if not corrected.
__AF_MAKE_LOG_FUNCTION(ASL_LEVEL_WARNING, AFLogWarning)
// The lowest priority for user log
__AF_MAKE_LOG_FUNCTION(ASL_LEVEL_INFO, AFLogDebug)
I map log level with __AF_MAKE_LOG_FUNCTION(LEVEL, NAME) and I need to call TheLog(__PRETTY_FUNCTION__,format,##__VA_ARGS__);\ from inline void NAME(NSString *format, ...)
I get an error:
Pasting formed ',__VA_ARGS__', an invalid preprocessing token
How can I fetch ,__VA_ARGS__ and __PRETTY_FUNCTION__?
This line:
TheLog(__PRETTY_FUNCTION__,format,##__VA_ARGS__);\
is part of the definition of this macro:
#define __AF_MAKE_LOG_FUNCTION(LEVEL, NAME) \
Note that that macro does not take a variable argument list. Therefore, there's no __VA_ARGS__ defined within its definition.
The fact that the function being defined by an instantiation of __AF_MAKE_LOG_FUNCTION — the inline void NAME() — takes a variable argument list isn't relevant. If that function wants to pass the variable argument list along to another function, it needs to do it using the stdarg functionality, as it does for -[NSString initWithFormat:arguments:], but that doesn't work for your TheLog macro, because it's not designed to accept a va_list.
You can't do what you're attempting. Your TheLog macro is incompatible with how you're trying to use it. You would need to design an alternative version, such as:
#define TheLogv(fmt, args) { \
if ([[DebugManager shared] isDebugging]) \
addLogEventv(__PRETTY_FUNCTION__,fmt,args); \
}
Note that this would, in turn, require the existence of a function addLogEventv() which accepts a va_list instead of an actual variable argument list. Within the body of the function being defined by __AF_MAKE_LOG_FUNCTION, you'd have to start and end the list twice, once around each time you pass it to another function, because each function will "consume" it:
#define __AF_MAKE_LOG_FUNCTION(LEVEL, NAME) \
inline void NAME(NSString *format, ...)\
{ \
va_list arg_list; \
va_start(arg_list, format); \
TheLogv(__PRETTY_FUNCTION__,format,arg_list);\
va_end(arg_list); \
va_start(arg_list, format); \
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; \
asl_add_log_file(NULL, STDERR_FILENO); \
asl_log(NULL, NULL, (LEVEL), "PREFIX: %s", [formattedString UTF8String]); \
va_end(arg_list); \
}
You could also change your TheLog() macro to take an NSString* and simply pass in the formattedString that's already being created.
Let's say I have an uninitialized variable:
UIViewController *vc;
From this variable, I want to reference UIViewController, such that I could call alloc or new on it to return an instantiated object.
Essentially, I want to do:
UIViewController *vc = [*typeof(vc) new];
... which does not compile because the compiler expects an expression rather than a type.
If #encode returned the actual type, I could do something like:
UIViewController *vc = [NSClassFromString(#(#encode(*typeof(vc)))) new];
...however, #encode returns '#', which just means "generic object".
I realize it's a little philosophical in nature, but I get tired of typing and would like to make a macro like NEW(x). I also realize a similar macro could be made if it involves the actual declaration, but I am not satisfied with that syntax/angle.
Here's what I have... it doesn't seem ideal, since it makes a performance hit. Still looking for better answers.
static Class classFromEncoding(const char *encoding) {
char *className = strndup(encoding + 1, strchr(encoding, '=') - encoding - 1);
Class retval = NSClassFromString(#(className));
free(className);
return retval;
}
#define NEW(variable) [classFromEncoding(#encode(typeof(*variable))) new]
Here's a macro-only version:
#define CLASS_FROM_VARIABLE(variable) \
^(const char *encoding) { \
char *className = strndup(encoding + 1, strchr(encoding, '=') - encoding - 1); \
Class retval = NSClassFromString(#(className)); \
free(className); \
return retval; \
}(#encode(typeof(*variable)))
#define NEW(variable) [CLASS_FROM_VARIABLE(variable) new]
#define ALLOC(variable) [CLASS_FROM_VARIABLE(variable) alloc]
Variations can be made using objc_getClass() or NSString initWithBytes, perhaps with performance gains. Still, it's not a no-op, which is what I'd prefer.
It's [obj class] or [obj className] depending on your needs.
I have a series of dictionaries stored in property list ("plist") files, and I was wondering if there was a way for me to make this code more efficient, especially for debugging purposes...
The header file contains a section like this:
//////////////////////////////////////////////////////////////////////////////////////
//
// dictionary objects retrieved from Property List ("plist") files...
//
- ( NSDictionary * ) dictionaryAdvertising;
- ( NSDictionary * ) dictionaryBuildings;
- ( NSDictionary * ) dictionaryCampus;
- ( NSDictionary * ) dictionaryLockerItems;
- ( NSDictionary * ) dictionaryRandomScreenObjects;
- ( NSDictionary * ) dictionarySchools;
- ( NSDictionary * ) dictionarySounds;
- ( NSDictionary * ) dictionarySupport;
In the main code section, I have the following:
//////////////////////////////////////////////////////////////////////////////////////////////
//
// use a single macro to create multiple instances of the same basic method with subtle
// variations to avoid code replication (if there's an issue, fix it once instead of
// multiple cut/paste instances)...
//
#define __CREATE_DICTIONARY_ACCESS_METHOD( _method_, _key_, _filename_ ) \
- ( NSDictionary * ) _method_ \
{ \
/* actual pointer to the "singleton" instance of this dictionary... */ \
static NSDictionary * dict = nil; \
/* once-only identifier for instantiating this "singleton" item... */ \
static dispatch_once_t onceToken = 0l; \
/* allocate and load this object, if it hasn't been done already... */ \
dispatch_once( &onceToken \
, ^{ \
dict = [ self loadDictionaryFromFile: _key_ ]; \
/* idiot check... */ \
NSAssert( ( dict ) \
, #"error allocating dictionary \"%#\"" \
, _key_ \
); \
/* if there is a field that lists an image file... */ \
if ( _filename_ ) \
{ \
/* load it into the texture cache... */ \
[ self cacheFromDictionary: dict \
name: _key_ \
key: _filename_ \
]; \
} /* end field name given for image file names */ \
} /* end run load process only once */ \
); \
return dict; \
}
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryAdvertising , CS_STRING_DICTIONARY_KEY_ADVERTISING , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryBuildings , CS_STRING_DICTIONARY_KEY_BUILDINGS , #"Filename" )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryCampus , CS_STRING_DICTIONARY_KEY_CAMPUS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryLockerItems , CS_STRING_DICTIONARY_KEY_LOCKER_ITEMS , #"Imagename" )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryRandomScreenObjects, CS_STRING_DICTIONARY_KEY_RANDOM_OBJECTS, nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySchools , CS_STRING_DICTIONARY_KEY_SCHOOLS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySounds , CS_STRING_DICTIONARY_KEY_SOUNDS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySupport , CS_STRING_DICTIONARY_KEY_SUPPORT , nil )
They each need to be created as separate methods so that each will have their own once-only initialization tokens. Since these objects are not actually stored as member variables, they will not be released during normal clean-up of parent objects, so they will persist across multiple instances of the parent class.
What I would like to do is find a more elegant solution to replace the macro defined in the main code module, especially one that would allow me to step through the code while debugging.
BTW, for those of you who want to use this, it's a workable process for creating/adding compile-time member access methods in Objective-C, mostly all of the C and C++ variant languages... The pseudo-singleton code ensures that the member access method is only created/allocated/initialized once.
If it helps, here is the code for loading the dictionary object from the plist file (this code allows for an override file in the user's "documents" directory, if you use this you may want to remove that capability, or at least keep it in mind):
// load the given dictionary from the associated Property List ("plist") file...
- ( NSDictionary * ) loadDictionaryFromFile: ( NSString * const ) className
{
// assume that there is a problem...
NSDictionary * dict = nil;
// build the file name for the "plist" file...
NSString * const fullFileName = [ NSString stringWithFormat: #"%#.plist"
, className
];
// idiot check...
NSAssert1( ( fullFileName )
, #"error allocating object for file \"%#\""
, className
);
// get the path to the "plist" files for this application in the "documents" folder...
NSString * const rootPath = [ NSSearchPathForDirectoriesInDomains( NSDocumentDirectory
, NSUserDomainMask
, YES
) objectAtIndex: 0
];
// idiot check...
NSAssert( ( rootPath )
, #"error allocating object"
);
// build the fully-qualified path to the requested file...
NSString * plistPath = [ rootPath stringByAppendingPathComponent: fullFileName ];
// idiot check...
NSAssert1( ( plistPath )
, #"error allocating object for file \"%#\""
, fullFileName
);
// if the file doesn't exist in the "documents" folder...
if ( ! [ [ NSFileManager defaultManager ] fileExistsAtPath: plistPath ] )
{
// then pull it from the resources bundled with the application...
plistPath = [ [ NSBundle mainBundle ] pathForResource: className
ofType: #"plist"
];
// idiot check...
NSAssert1( ( plistPath )
, #"error allocating object for file \"%#\""
, className
);
} // end file not in "documents" folder
// read the "plist" file into a dictionary object (statically allocate it so that it
// doesn't get automatically dropped when moving between scenes)...
dict = [ [ NSDictionary alloc ] initWithContentsOfFile: plistPath ];
return dict;
}
Any assistance or comments would be greatly appreciated. Also, I'm not sure if I've used the proper tags for this question....
=========================================================================
I modified the code based on the answer given, and it now looks like this:
// once-only method to access and load the contents of a Property List file into a dictionary...
- ( NSDictionary * ) accessDictionaryWithKeyAndFilename: ( NSString * const ) _key_
filename: ( NSString * const ) _filename_
token: ( dispatch_once_t * const ) onceToken
dict: ( NSDictionary * * const ) dict
{
// allocate and load this object, if it hasn't been done already...
dispatch_once( onceToken
, ^{
*dict = loadDictionaryFromFile( _key_ );
// idiot check...
NSAssert( ( *dict )
, #"error allocating dictionary \"%#\""
, _key_
);
// if there is a field that lists an image file...
if ( _filename_ )
{
// load it into the texture cache...
[ self cacheFromDictionary: *dict
name: _key_
key: _filename_
];
} /* end field name given for image file names */
} /* end run load process only once */
);
return *dict;
} // end accessDictionaryWithKeyAndFilename
//////////////////////////////////////////////////////////////////////////////////////////////
//
// use a single macro to create multiple instances of the same basic method with subtle
// variations to avoid code replication (if there's an issue, fix it once instead of
// multiple cut/paste instances)...
//
#define __CREATE_DICTIONARY_ACCESS_METHOD( _method_, _key_, _filename_ ) \
- ( NSDictionary * ) _method_ \
{ \
/* actual pointer to the "singleton" instance of this dictionary... */ \
static NSDictionary * dict = nil; \
/* once-only identifier for instantiating this "singleton" item... */ \
static dispatch_once_t onceToken = 0l; \
[ self accessDictionaryWithKeyAndFilename: _key_ \
filename: _filename_ \
token: &onceToken \
dict: &dict \
]; \
return dict; \
}
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryAdvertising , CS_STRING_DICTIONARY_KEY_ADVERTISING , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryBuildings , CS_STRING_DICTIONARY_KEY_BUILDINGS , #"Filename" )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryCampus , CS_STRING_DICTIONARY_KEY_CAMPUS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryLockerItems , CS_STRING_DICTIONARY_KEY_LOCKER_ITEMS , #"Imagename" )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionaryRandomScreenObjects, CS_STRING_DICTIONARY_KEY_RANDOM_OBJECTS, nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySchools , CS_STRING_DICTIONARY_KEY_SCHOOLS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySounds , CS_STRING_DICTIONARY_KEY_SOUNDS , nil )
__CREATE_DICTIONARY_ACCESS_METHOD( dictionarySupport , CS_STRING_DICTIONARY_KEY_SUPPORT , nil )
There is still the drawback of having the macro for creation of the methods, but the loading code is now available in debug mode. I need to maintain distinct static values for each dictionary/once_token so that we can have 0->n instances of the parent class but still have the loaded dictionary persist across all instances. Even when all instances of the parent class are dismissed and later more are created, the original single dictionary load is still available, streamlining processing.
Thanks!
For me, the easiest thing to do would be to just make the macro a function, and write 8 very small wrapper methods. So start with a free function like this:
NSDictionary* accessDictionaryWithKeyAndFilename (NSString* key, NSString* filename, dispatch_once_t* onceToken)
{
... everything from your macro here ...
}
Then for any given method you want to create, just call the above by doing the following:
-(NSDictionary*)dictionaryAdvertising
{
return accessDictionaryWithKeyAndFilename (CS_STRING_DICTIONARY_KEY_ADVERTISING, nil, &once_token);
}
and so on. (The once_token can either be a static in the method, or an ivar.) This is much more readable and much more debuggable. Writing the 1-line method to access the function is no harder than writing the 1-line macro to auto-create the method. It has the advantage of keeping the method with the rest of the class code, rather than being declared in some header or other source file. And of course, now you can step into each method and debug whatever's happening inside it.
Your -loadDictionaryFromFile: method doesn't appear to use any ivars from the class, so it could also become a free function. (Unless I missed something?)
I want to write a macro to generate 2 methods like below:
- (NSString*)propertyName
{
NSString *key = #"PropertyName";
//get value from NSUserDefaults
//...
}
- (void)setPropertyName:(NSString*)value
{
NSString *key = #"PropertyName";
//set value to NSUserDefaults
//...
}
The first letter of property name is lower case in the get method, upper case in the set method and key value.
The macro should receive 1 or 2 arguments which is the property name:
MY_PROPERTY(propertyName)
or
MY_PROPERTY(PropertyName)
or
MY_PROPERTY(propertyName, PropertyName)
The argument is also the value for key (string value).
How to write a macro for this? I prefer the first or second one. Thanks.
Let's get mad:
#define STRINGIFY(__TEXT__) [NSString stringWithFormat:#"%s", (#__TEXT__)]
#define GENERATE_GETTER_AND_SETTER(__UPPER_CASE__, __LOWER_CASE__, __TYPE__) \
- (void)set##__UPPER_CASE__:(__TYPE__)__LOWER_CASE__ { \
NSString *propertyName = STRINGIFY(__UPPER_CASE__); \
\
...
} \
\
- (__TYPE__)__LOWER_CASE__ { \
NSString *propertyName = STRINGIFY(__UPPER_CASE__); \
\
...
return ... \
} \
Usage:
GENERATE_GETTER_AND_SETTER(MyProperty, myProperty, NSArray*)
Note that you have to specify both lower case and upper case names and you have to know the type of the property.
It might be easier to declare the properties as #dynamic and then implement the methods dynamically, see Objective-C forwardInvocation: for more details.