In Objective-C, are there any ways to indicate that an NSNumber* should actually be a BOOL? Right now my code looks like:
NSNumber *audio; // BOOL wrapper
Without the comment, it is not immediately obvious that *audio is a boolean value.
My first thought was to try
typedef NSNumber* BOOL;
but this gave a compiler error apparently because typedef doesn't understand Objective-C.
Without changing the variable names (which is difficult when using existing APIs), how should I indicate that an NSNumber* holds a boolean value?
The code:
typedef NSNumber* BOOL;
doesn't compile because BOOL is already a typedef, and it's not allowed to redefine a typedef.
So you could use another name for that type, e.g.:
typedef NSNumber NSNumberBool;
NSNumberBool *audio;
Or, probably better, name the variable so that you know it is an NSNumber and contains a bool, this way you don't even need to go look for the variable type:
NSNumber *audioNumberBool;
...
[audioNumberBool boolValue];
Clearly, the best solution is to rename the property to be something like isAudio or hasAudio. But if you can't do it, then a mediocre solution is the typedef like you describe. The typedef you describe fails because BOOL is already defined in Objective C in objc.h:
typedef signed char BOOL;
beside which, that would be confusing since it doesn't indicate its actually an NSNumber and not just a bool/int. I would suggest something like:
typedef NSNumber NSNumberBool;
or perhaps in this case, better would be:
#define NSNumberBool NSNumber
and then use:
NSNumberBool *audio;
Or just use a comment as you have done.
The best way would be to call the variable audioBool or something that signifies a boolean value. Comments are good and all, but sometimes they can be missed or just not read.
If you have a problem with renaming many variables, you can use Xcode's refactor tool. Simply right click (or cmd+click) on the variable name, choose the Refactor... option and then rename the variable. Hit Preview, and Xcode will show you what it's about to change. If you like what you see, go ahead and apply, if not, cancel.
Xcode will also give you the option to make a snapshot before committing to a refactor operation. This is on by default. So if anything goes wrong, you can just roll back.
Related
I am learning Objective C, there is a function:
NSArray *desktops = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
When I press command and click function NSSearchPathForDirectoriesInDomains, I see something looks like:
FOUNDATION_EXPORT NSArray<NSString *> *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);
I don't understand why there is a * before the function name while when I use the function in main.m I don't put * in function name?
Let's pick a slightly simpler signature that will hopefully be clearer:
NSArray *GetArray()
You can move the space and write this way:
NSArray* GetArray()
This is a function that returns a pointer to an array. The "*" is part of the type, not part of the name.
It might also be clearer if you think of it this way:
(NSArray *) GetArray()
or
NSArray * GetArray()
It's generally a matter of style which you choose. In one case you're emphasizing that the return type is an array, and this function returns a pointer to it. In another, you're emphasizing that the return type is a pointer to an array.
The asterisk is between the type and the function name, that is what matters. It is a matter of style whether you physically attach the pointer to the name of the function, or attach it left, with the name of the return type. The asterisk is not actually part of the function name.
For example, lots of code will show something like NSArray* some_name and others will instead write NSArray *some_name but they mean the same thing.
In my experience, it seems the vast majority of code I see attaches the pointer to the type, not the variable/function name. But there is no "right" way. I personally have always preferred to attach it to the type to make it clear the return is a pointer.
In this case, it would be a little more odd-looking however, since you'd have:
NSArray<NSString *>*
as the return, and that double asterisk does look a bit cryptic at first. Maybe that's why they moved it in this particular case.
Here's an interesting one for the objective-C gurus out there...
Is there a way to declare an objective-C block typedef that contains an argument of that typedef?
typedef BOOL (^SSCellAction) ( UITableViewController* inTVC, SSCellAction inChainedAction );
The idea is that I wanted to used chained menu action system that allows a chain of work/response to occur (usually 1-3 items). When the last action invokes, it passes nil for inChainedAction. Since this seems relatively trivial to imagine, I'll be dammed if I can't figure out how to declare it without llvm saying no. :)
rmaddy's comment is correct. Just as in C, a typedef cannot use itself. Basically, a typedef does not make a real type, but just makes an alias that the compiler expands out at compile-time. It is always possible to manually expand all typedefs in your program yourself (which is sometimes an instructive exercise), so that your program is written without typedefs. However, a recursive typedef cannot be expanded.
Some possible workarounds:
Use id as the parameter type, and cast back into the right type inside the block. This loses type safety.
Or, use a struct type of one member, the block. A struct is a real type, so it can be used within its definition. The downside of this is that you explicitly "wrap" the block into the struct type to pass it, and explicitly "unwrap" the struct into the block by accessing the field when you need to call it. This way is type-safe.
I have to assign a variable to a constant like this (the code below is at the beginning of my file code, before #implementation):
#ifdef DEBUG
NSString *hostStr=[[NSString alloc]init];
hostStr=#"xxx.mycompany.com";
static NSString * const host = hostStr;
#endif
If i do like so:
#ifdef DEBUG
static NSString * const host = #"xxx.mycompany.com";
#endif
That will work.
Actually, in my real case, host will contain the value of a global value (declared in the app delegate and initialized in another view controller). But for the sake of simplifying my problem, i use this example (since both cases give me the same error).
How can i fix this problem please. Thanx in advance.
There are a couple of issues here.
Constants that are set outside of any function cannot be "dynamic". This means that the compiler has to know what the constant value is before the program is run. If you say something like this:
static int x = myFunction(459);
The compiler can't know what myFunction() will return until the program is actually run.
This is why:
NSString *hostStr=[[NSString alloc]init];
causes a syntax error. The compiler will not execute any code when making a constant.
The solution is simple:
NSString *host=#"www.mycompany.com";
Notice that I didn't use the "static" qualifier. That would make "host" only available to the code in the file it was declared it. Dropping the "static" qualifier makes it global.
To access this global variable from another file, the other file needs to declare
extern NSString *host;
at which point the other file will be able to see the contents of "host".
Another thing to point out, is that this:
NSString *hostStr=[[NSString alloc]init];
hostStr=#"xxx.mycompany.com";
doesn't really do much. You create an NSString with alloc/init, then immediately assign
a constant to it, moving the NSString you created aside, without disposing of it, creating a memory leak. (If you have ARC enabled, then it's a non-issue. ARC knows all.)
Occasionally, during development/debugging, I want to ensure that an object is of a certain type:
PageTopBottom *newPage = [notification object];
assert([newPage isKindOfClass:[PageTopBottom class]]);
which I've worked into this
#define assertType(_var_, _class_) assert([_var_ isKindOfClass:[_class_ class]])
and
PageTopBottom *newPage = (id)[notification object];
assertType(newPage, PageTopBottom);
but now I'd like to, if possible, just use
assertType(newPage)
Is it possible to get information about a variable's declared type from the variable?
I'm not positive that I'm framing the question correctly, but any answer that gets me to be able to assertType with one parameter would be great.
Is it possible to get information about a variable's declared type from the variable?
No. By the time the program is running, that information is lost. In your case, newPage is just a 32 or 64 bit number that points to a bit of memory that holds an Objective-C object.
I think your original unmacro'd version is the right thing to do here:
assert([newPage isKindOfClass:[PageTopBottom class]]);
That perfectly documents the assumption you are making i.e. that you assume newPage is an instance of PageTopBottom or one of its subclasses and it's completely clear to anybody who understands Objective-C. Your macro version slightly obfuscates that, in that somebody coming across it in the code might beleive it is asserting that newPage is a PageTopBottom and not one of its subclasses (you could change the name of the macro to prevent that, I suppose, but I just wouldn't bother).
Edit
What you could do is combine the declaration and assertion in one:
#define DECLARE_AND_ASSERT_IS_KIND_OF_CLASS(T, V, I) T* V = (T*)(I); assert([(V) isKindOfClass: [(T) class])
which would work like this:
DECLARE_AND_ASSERT_IS_KIND_OF_CLASS(PageTopBottom, newPage, [notification object]);
Hmm, with Objective-C++ there are two options:
Write a template function
template void assertType(T* obj) { ... }
For a pointer X* x, use NSClassFromString([NSString stringWithUTF8String:typeid(*x).name()]).
Without using C++, you might be able to use GCC extension typeof, but I'm not sure if [typeof(*x) class] is a legit operation...
The preprocessor only processes text; it has no knowledge of type, which is why it's sometimes considered 'dangerous'. The only way I could see doing it is wrapping the variable declarations in a macro, which I would strongly advise against, and probably wouldn't actually cut down on the code or complexity.
Also, shouldn't you check the type before casting?
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.