I want to define a constant file path that includes whoever's username (e.g. /Users/username/Desktop...in my specific case it's a directory I create at /var/spool/FolderICreate/username).
What's the best way of declaring this constant so my other classes can recognize it? I currently have a globals.h file to include for the classes that need to see the globals, but I'm not sure how to set the value. #define obviously needs a hardcoded string literal, and I'm not sure if I can or how I would set the string using extern const NSString*. I feel like this shouldn't be hard, but I'm at a loss.
-- EDIT --
As people have rightly pointed out, my code was unnecessary because I can get the username with NSUserName, etc, so I've removed it. But I think people are missing the point of my question. I can see there are multiple ways to get the pathname I want--how do I declare that as a constant?
To your revised question, as Kaelin notes, you don't want a constant. A constant in C is something defined at compile time, and you don't know what the value is at compile time.
You don't want a variable for this problem. You want a static function. Foundation provides many that do things similar to what you want. If you want something else, make your own static function MYDirectoryForStuff().
Do not create non-constant global variables. That way lies madness. You then have to verify that they get initialized before they are used, which leads to all kinds of subtle bugs. If you use a static function, then it can easily be self-initializing.
Have a look at NSPathUtilities before you re-implement a bunch of functionality that's already provided in Foundation framework. You'll find a bunch of methods there specifically for what you're trying to do.
To answer the question you actually asked, there's no straightforward way to declare a variable as a constant and then change its value at runtime... That sort of defeats the purpose of telling the compiler it's constant. Simply declare the variable in a header file as an extern NSString * and only set it once. :-)
You should never make any assumptions about where the user's home folder might be.
The correct way to retrieve the path to the current user's home folder is
NSHomeDirectory()
If you wish to access one of the standard subfolders of the user's home folder, don't make any assumption about their location either, but use
NSSearchPathForDirectoriesInDomain()
to retrieve them. For example, to get the documents folder:
NSArray *searchPaths = NSSearchPathForDirectoriesInDomain(
NSDocumentDirectory,
NSUserDomainMask,
YES
);
NSLog(#"The documents folder is at %#", [searchPaths objectAtIndex:0]);
The Foundation framework has a function for this: NSUserName().
Related
I was reading this question/answers, which basically showed an interesting behaviour in Java and strings, and two questions came up in my mind:
Are Objective-C/Swift String s behave the same? I mean if I have for example two variables which stores the same literal "someString", internally, will they refer to one "someString" object? I didn't find anything about it in the documentation.
If the answer to my previous question is yes, then is it possible to change same string literals the way like in Java?
Not all NSString literals (#"string literal") share the same storage due to compilation units.
NSString literals can not be changed in the program code, they are compiled into readonly segments.
NSString variables, that is that are created at runtime, only are shared by assignment.
NSString instances are immutable and can not be changed after creation.
NSMutableString instances can be modified and all variables pointing to such an instance point to the same change.
Swift is slightly different, as #Grimxn points out, Swift String is not a class and immutability is determined by the declaration syntax: let or var.
What I want to do:
I want to implement ability for user to use CIFilters on image. So I need somehow to fix it's sequence. For example if user opens image, then applies CIGaussianBlur, and then CIColorControls and then CISepia, I need to get result like that:
On user opened image apply CIGaussianBlur -> on CIGaussianBlur output image apply CIColorControls - > on CIColorControls output image apply CISepia.
Thats OK. But what if then user turns off CIGaussianBlur? I need then to repeat this effect's sequence just without blur. It would look like this:
On user opened image apply CIColorControls -> on CIColorControls output image apply CISepia.
The question
Is it possible to do something like this:
After applying any effect, add some string in NSMutableArray or NSMutableDictionary. Then when applying another effect, check NSMutableArray or NSMutableDictionary contents like that:
if object at index 0 is equal to "blur", apply blur on source image, then take blur's output image like current effect's input image
And so on? So that effects would be re-applied every time in their sequence made by user.
If it is possible maybe someone could suggest me any solution?
I think that this is a great instance for the factory idea to be used.
You should store your array of filters to process the image as an array - that maintains sort order, and is fairly straightforward to deal with (other than something like a NSCountedSet).
The next logical question to ask, then, is how do we apply the factory pattern here? The most important thing to consider is what type should the context object be? Here are a few thoughts:
Using NSString as a constant identifier.
Probably the simplest to start, its , and easy to understand - the downside is that it's slower than other options, and can get to be quite the complex if-else block, as you cannot use a switch statement on a NSString.
NSNumber, wrapping an enum value.
This is probably one of the better options. You can convert right down to an int variable, which compares quite fast on almost any processor I can imagine, and, if you use ObjC 2.5's fancy literals, you could do something like this:
[filters addObject:#(filterType_gaussianBlur)];
Where filterType_gaussianBlur is an enum constant or something.
Another advantage to using an enum is the support for switch statements out of the box. It cleans up your code if done properly, it's faster than a large if-else block, the only thing to look out for is ensuring that you break properly!
Storing Class objects
This one may require some explaining. In objective-c, you can actually store a reference to the type of an object, CIGaussianBlur, NSString, NSData, etc.
This class "object" allows you to dynamically create an object based just on it's type, for example:
Class cls = nil;
if (stringMutable)
cls = [NSMutableString class];
else
cls = [NSString class];
NSString *mutableOrNot = [[cls alloc] initWithString:#"Hello World!"];
However, the disadvantage to this approach would be the inability to configure the objects after they are initialized, and you must use the same selector (method) to initialize each one. If that is not an issue (I do not use CoreImage), then using the Class approach should be fine.
All in all, use whatever makes sense in the situation. If these filters need no additional configuration after they have been initialized, then approach 3 makes a lot of sense. I personally wouldn't recommend approach 1, unless it really is necessary to use a string. Whenever you can, always try to control the values that an object can have. It makes your life much easier, trust me.
If I had two variables in Objective C like this where one holds the name of the other as a string
NSInteger result = 4;
NSString * theName = #"result";
How would I best access the first variable using the string instead of a reference to the variable? For instance if I had a lot of variables and would generate the name of the one I need by code I'd need a way to get to the variable using that string.
Though not directly answering your question, it's possible to access properties (or ivars) of an object by
[object setValue:#"value" forKey:theName]
Similarly, the getter is [object valueForKey:theName] (thanks kevboh!)
That's not possible in objective-c. Variable names cannot be synthesised by name. The variable name itself doesn't mean anything when running your code, the compiler converts it into a memory address. The name is just a way for the programmer to make writing and reading code easier.
Depends on your exact situation but you probably should be using an NSArray or NSDictionary.
Newbie question here. I'd like to be able to specify through data (i.e. an XML file), the appropriate Objective-C message to send. Any advice on if this is possible or how I can do this?
The next best thing, if I can't do this, would be some way to create a map object that would correlate a key (an int) with a function (I guess also a selector). Is that possible if the above isn't?
If someone could point me to some tutorial or example code as reference, that'd be great. Right now I'm doing things with a big switch statement, and I don't like it. (I'm switching on the id and in each case, explicitly calling the method relevant to the particular id.)
I love that you asked this question; too often, I see Satan's Swollen Switch Statement. It's nice to see someone wanting to using a function-table instead.
If you're OK with using a property list file (which is usually encoded in XML), this is really easy.
Just make a property list where the root element is a dictionary, which maps from some keys to some selectors.
Key Type Value
----------------------------------------------
Root Dictionary
firstKey String someSelector
secondKey String anotherSelector
Load the contents of your property list into an NSDictionary:
id path = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"plist"];
id dict = [NSDictionary dictionaryWithContentsOfFile:path];
SEL selector = NSSelectorFromString([dict objectForKey:#"firstKey"]);
if ([someObject respondsToSelector:selector]) {
[someObject performSelector:selector];
}
Of course, you'll want to refactor this logic into an appropriate method, and probably cache the property list as an instance variable.
Note: I personally think it's better to just put this function table inline; property lists are cool, but I'm not sure that it is very helpful in this case. Also, if you are cool with using Objective-C++, std::map will allow you to get away with not wrapping and unwrapping the selectors in NSString objects, etc.
According to Cocoa Programming for Mac OS X, 3rd Edition, on page 202 (chapter 13):
You will be registering, reading, and
setting defaults in several classes in
your application. To make sure that
you always use the same name, you
should declare those strings in a
single file and then simply #import
that file into any file in which you
use the names. There are several ways
to do this. For example, you could use
the C preprocessor’s #define command,
but most Cocoa programmers use global
variables for this purpose.
Is this really the correct best practice? Global variables? That seems insane to me – counter to everything I’ve ever been taught.
Would a better design be a simple Singleton class with these defined? Or is it really the correct best practice to go global? Is there a better pattern than either, given that many people consider Singletons to be globals in a pretty dress?
Just to be clear, the recommendation is to create immutable global variables instead of in-line string constants (hard to refactor and no compile-time checking) or #defines (no compile-time checking). Here's how you might do so...
in MyConstants.h:
extern NSString * const MyStringConstant;
in MyConstants.m:
NSString * const MyStringConstant = #"MyString";
then in any other .m file:
#import "MyConstants.h"
...
[someObject someMethodTakingAString:MyStringConstant];
...
This way, you gain compile-time checking that you haven't mis-spelled a string constant, you can check for pointer equality rather than string equality[1] in comparing your constants, and debugging is easier, since the constants have a run-time string value.
[1] In this use, you are essentially using the pointer values as the constants. It just so happens that those particular integers also point to strings that can be used in the debugger
Global variables or a singleton will accomplish the same thing here. Both can be used to turn 'key' names in Cocoa that won't throw a compiler error if it's misspelled into a compiler error. That's the main purpose. Global variables are a bit easier though seeing as it requires less typing.
Instead of doing this:
[myArray setObject:theObject forKey:MyGlobalVariableKeyName];
You'd have to do something along the lines of:
[myArray setObject:theObject
forKey:[[MySingletonVariableClass getInstance] myVariableKeyName];
Global variables are essentially less typing for the same effect.
Calling it a global variable is technically correct but misleading.
It is a global constant -- global in scope but constant and therefore not bad in the sense that global variables are bad.
To show how global constants are common, safe and numerous, consider these examples of global constants:
Every class in your program
Every #define
Every enum
Almost every name declared by Cocoa (excluding rare global variables like NSApp).
The only time you should worry about global constants is when their names are too generic (they may pollute the global namespace). So don't use names that are likely to conflict with anything (always use a prefix and always make the name task-specific like NSKeyValueObservingOptionNew).
Constant globals that are set at compile time and never change are acceptable to me. If you hard code a string, it's the same thing, just hidden by the compiler. I'll avoid mutable globals like the plague.
Remember, Apple itself uses the same technique. Many of the constants I expected to be defines are actually constants. You'll get link errors if the headers are reachable but the framework is not.
building on #Barry Wark's and #Matt Gallagher's excellent answers, and my initial response (see end of this answer) there is a third approach, and that is to use a macro/include combination that ensures you only type the variable name once, and therefore it is included in both the .h and .m files simultaneously.
< EDIT >
"there is always another way..."
After thinking about how to make it even simpler, without involving an additional header file, here is a more concise approach using nested macros.
in .h file
#define defineKeysIn_h_File(key) extern NSString * const key;
#define defineKeysIn_m_File(key) NSString * const key = ##key;
#define myKeyDefineKeys(defineKey) \
/**start of key list*/\
defineKey(myKeyABC);\
defineKey(myKeyXYZ);\
defineKey(myKey123);\
/*end of key list*/
myKeyDefineKeys(defineKeysIn_h_File);
in .m file
myKeyDefineKeys(defineKeysIn_m_File);
implementation note
You can use this more than once in multiple headers, however you need to change the
name of "myKeyDefineKeys" to be unique, I suggest giving it the same prefix as the keys you are defining - for the sake of an example I have used "myKey" throughout.
In another file I might use "myOtherKeyDefineKeys".
Also don't mess with the defineKeysIn_h_File and defineKeysIn_m_File macros or you will get a warning the definition has changed.
< END EDIT >
ORIGINAL ANSWER, STILL VALID, BUT WITHOUT REFINEMENTS
First, make a vanilla.h file and remove the default #ifdef etc,and enter your keys as below:
(This is a cut and paste from a category I wrote to extend AVAudioPlayer)
// playFromConsts.h
define_key(AVAudioPlayer_key_player);
define_key(AVAudioPlayer_key_duration);
define_key(AVAudioPlayer_key_filename);
define_key(AVAudioPlayer_key_filepath);
define_key(AVAudioPlayer_key_fileurl);
define_key(AVAudioPlayer_key_urlString);
define_key(AVAudioPlayer_key_envelope);
define_key(AVAudioPlayer_key_startDate);
define_key(AVAudioPlayer_key_linkToPlayer);
define_key(AVAudioPlayer_key_linkFromPlayer);
define_key(AVAudioPlayer_key_linkToPlayerEnvelope);
define_key(AVAudioPlayer_key_linkFromPlayerEnvelope);
define_key(AVAudioPlayer_key_deviceStartTime);
define_key(AVAudioPlayer_key_currentVolume);
define_key(AVAudioPlayer_key_fadeFromVolume);
define_key(AVAudioPlayer_key_fadeToVolume);
define_key(AVAudioPlayer_key_fadeTime);
define_key(AVAudioPlayer_key_segueTime);
Then in your normal.h file (where your #interface, #protocol etc is declared) place these 3 lines (substituting your header file of course)
#define define_key(x) extern NSString * const x;
#include "playFromConsts.h"
#undef define_key
finally in your .m file, that is paired with your "#interface .h" file, place these 3 lines:
#define define_key(x) NSString * const x = ##x;
#include "playFromConsts.h"
#undef define_key
note the "#include" and not "#import" - we actually do want to include this file more than once.
this will do all the dirty work, and ensure the keys are NSString * const.
the trailing ; is optional, as it's included in the macro, however i personally prefer it.
So after all. I came up with 3 files.
Constants.h
#define def_key(name) extern NSString *const name
#define def_int(name, value) extern int const name
#define def_type(type, name, value) extern type const name
#include "ConstantsDefs.h"
Constants.m
#import "Constants.h"
#undef def_key
#define def_key(name) NSString *const name = ##name
#undef def_int
#define def_int(name, value) int const name = value
#undef def_type
#define def_type(type, name, value) type const name = value
#include "ConstantsDefs.h"
ConstantsDefs.h
def_key(kStringConstant);
def_int(kIntConstant, 313373);
def_type(float, kFloatConstant, 313373.0f);
It depends on the design of your software. Suppose you have a job management software and one of your "defaults" is a list of directories in which various items can be saved.
For each Job you can have a storagefile member that is a singleton that load up the user preferred locations at startup.
Or you could have a Storagefile member of a global variable called User Preferences. Still could be a singleton but doesn't really matter in this case.
For me complex defaults (dozens of different types of classes) should reside in their own "space" accessible to model.
However there may be preferences that are important to how a Job is setup so those preference need to be stored in the Job Object so when you open it in another user's application it works as intended.
Again it depends on your design.