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.
Related
Imagine this handy method called "cycle":
str = [str cycle:#[#"blue",#"white",#"red"]];
So, the string will cycle through each item of the array, looping around. (If str is not contained in the array, return the same value, let's say.)
Now of course, you could also write such a routine for integers, say, which would be very handy.
cyl = [cyl cycle:#[#(2),#(4),#(6),#(8),#(12)];
and so on.
Another example, it would be handy for enums, something like this:
self.picker.cameraFlashMode=
[self.picker.cameraFlashMode // you can't really do that :)
cycle: #[
UIImagePickerControllerCameraFlashModeOn,
UIImagePickerControllerCameraFlashModeOff,
UIImagePickerControllerCameraFlashModeAuto] ];
How to write a method / function / routine for this so it works for all types?
In other words, it should be possible to do something like this,
x = [x cycle:array];
or maybe some sort of macro like this ...
cycle( x, ..items.. );
no matter what x is. How would you do that?
You could create an NSObject category and implement that function. It would only work with objects inheriting from NSObject.
#implementation NSObject (myCategory)
- (id)cycle:(NSArray *)array
{
// code here
}
#end
This would not work for other types, but you could use C++ Templates to create a function that works for any data type.
template <class type> ret-type cycle(parameter list)
{
// body of function
}
This looks like an antipattern to me. Consider your example:
[str cycle:#[#"blue",#"white",#"red"]];
str is probably defined as an NSString, right? I would consider it a really bad idea for any method on NSString to modify the string in place, given that it’s an immutable class. You’d probably have to define a category on NSObject to get this kind of all-class behavior, but then what about this example?
[self.picker.cameraFlashMode cycle:#[
UIImagePickerControllerCameraFlashModeOn,
UIImagePickerControllerCameraFlashModeOff,
UIImagePickerControllerCameraFlashModeAuto]];
Is cameraFlashMode even an object? Remember, you can’t pass messages to instances of an enum type!
Edited to add…
If you write something like
int a = 1;
then a construction like
[a cycle:#[#1, #2, #3]];
is never going to be valid Objective-C syntax.
Sounds like it might be a good opportunity for an NSEnumerator subclass.
You'd need to implement - (id)nextObject and - (NSArray*)allObjects. The only problem I would see is that an NSEnumerator typically has an exhaustible list of objects, while this would theoretically continue forever.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
What's your preferred pointer declaration style, and why?
In C, why is the asterisk before the variable name, rather than after the type?
What makes more sense - char* string or char *string?
When declaring a new instance of an object in Objective-C, does it make any difference where you put the asterisk?
Is this just a matter of personal preference?
NSString* string = #"";
vs.
NSString *string = #"";
It doesn't make a difference, but there are good reasons to put it in each place:
It makes sense to put it near the class, because that makes it feel like a type: NSString*, a pointer to a string. Sensible.
It makes sense to put it near the variable, because that's what's actually happening: * is dereference. When you dereference your pointer string, you get an NSString. *string is an NSString. Sensible.
You may want to go with the latter because that's the way the compiler is thinking, so: NSString* oneString, anotherString will not work, whereas NSString *oneString, *anotherString is correct.
It's simply a matter of preference. Putting the * next to the type emphasizes that it's part of the type, i.e. "pointer to an NSString". However, this is usually frowned upon, because it ignores the fact that the * associates with the nearest variable name, not the type name. For instance, the following doesn't work:
NSString* a = #"string1", b = #"string2
This is because a is a pointer, but b is not.
Putting the * next to the variable name is, in my opinion, more of a C/C++ convention, because it emphasizes that the * and the variable name together act kind of like a variable.
Personally, I put a space on both sides of the *.
Another question that asked the same thing is here:
Declaring pointers; asterisk on the left or right of the space between the type and name?
It doesnt make the difference wher you put that pointer symbol. If you declare multiple objects in single line, you do it like NSString *str1, *str2. So its more appropriate to put that asterisk close to object. I prefer it close to object instance.
For the Objective-C gurus:
Suppose I have a simple method like so:
-(id)getValue{ return [NSNumber numberWithDouble:5.0]; }
Now, suppose within some other method I call the (id)getValue method like so:
NSNumber* myValue = [self getValue];
or what if I call it like this instead:
NSNumber* myValue = (NSNumber*)[self getValue];
The question is: Obviously these lines are equivalent but one of them utilizes an explicit cast. So what is the correct or best-practice way of doing this. It seams to me the cast is unnecessary since when it is placed in the pointer myValue, it will be type-safe at this point anyways (which is something I want) so the cast is basically pointless.
Let me just add that I'm sure people will point out: Why don't you just return (NSNumber*) from the getValue method but in my case I want to have the flexibility to return whatever I want much like the built in NSDictionary class returns id when you call: objectForKey because it allows you to place any type of NSObject or subclass inside of it. In other words my getValue method will not always be returning an NSNumber. Also consider this example is contrived because I am just concerned about whether to cast or not.
Thank you in advance,
-Ralph
The only reason to cast objects is to make the compiler happy. (Sometimes it also helps readability.) For example, you have to cast when making a property access directly on an object you're getting out of an array or dictionary:
((Foo *)[myArray objectAtIndex:0]).bar;
If you don't do the cast, the compiler can't do the property lookup, and will complain.
When you're getting an object from a method that returns id, it's impossible for the compiler to know what its actual type is. There isn't really any "type-safety", because id is a generic pointer; all the compiler can and will enforce is that the method says it returns some Objective-C object. It is perfectly happy to assign a generic pointer to any typed pointer.* (This is actually an advantage for containers, obviously.) Since the type of the variable to which you're assigning already documents the actual return type, I'd say there's no need for the cast.
As an aside, you shouldn't be calling your method getX. That has a specific meaning in Cocoa; methods which "get" something pass in a pointer to a pointer, which is then filled by the method. See -[NSArray getObjects:range:] as an example.
*The type will be enforced at run-time, of course, in the sense that sending messages to which the object does not respond will cause an error.
I'm trying to pass an NSString by reference but it doesn't work.
This is the function:
+(void)fileName:(NSString *) file
{
file = #"folder_b";
}
and this is the call:
NSString *file;
[function fileName:file];
nslog(#"%#",file); // and there is nothing in the string....
What I must do to pass my string by reference?
If you want to return a value, then return a value. Pass by reference in Cocoa/iOS is largely limited to NSError**.
Given:
+(void)fileName:(NSString *) file
Then do:
+(NSString *) fileName;
And be done with it.
If you need to return more than one value at a time, that begs for a structure or, more often, a class.
In Objective-C, pass by reference smells like you are doing it wrong.
Pass by reference in Objective-C is reserved largely for returning NSError* information about a recoverable failure, where the return value of the method itself indicates whether or not the requested task succeeded or failed (you can pass NULL as the NSError** argument to allow the method to optimize away creating said error metadata).
Pass by references is also used to retrieve interior state of objects where the return value is effectively a multi-value. I.e. methods from AppKit like the following. In these cases, the pass-by-reference arguments are typically either optional or are acting as secondary return values.
They are used quite sparingly across the API. There is certainly use for pass by reference, but -- as said above -- doing so should be quite rare and rarer still in application code. In many cases -- and in some of the cases below, potentially -- a better pattern would be to create a class that can encapsulate the state and then return an instance of said class instead of pass by reference.
NSWorkspace.h:- (BOOL)getInfoForFile:(NSString *)fullPath application:(NSString **)appName type:(NSString **)type;
NSTextView.h:- (void)smartInsertForString:(NSString *)pasteString replacingRange:(NSRange)charRangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString;
NSAttributedString.h:- (BOOL)readFromURL:(NSURL *)url options:(NSDictionary *)options documentAttributes:(NSDictionary **)dict;
NSNib.h:- (BOOL)instantiateWithOwner:(id)owner topLevelObjects:(NSArray **)topLevelObjects NS_AVAILABLE_MAC(10_8);
NSSpellChecker.h:- (NSRange)checkGrammarOfString:(NSString *)stringToCheck startingAt:(NSInteger)startingOffset language:(NSString *)language wrap:(BOOL)wrapFlag inSpellDocumentWithTag:(NSInteger)tag details:(NSArray **)details NS_AVAILABLE_MAC(10_5);
I believe you're looking for:
+ (void)fileName:(NSString **)file
{
*file = #"folder_b";
}
What's really done here is we're working with a pointer to a pointer to an object. Check C (yup, just plain C) guides for "pointer dereference" for further info.
(...But as has been pointed out repeatedly, in this particular example, there's no reason to pass by reference at all: just return a value.)
Passing a pointer to your object is the Objective C (and C) way of passing by reference.
I agree with 'bbum' that a perceived need to pass by reference is a signal to think about what you are doing; however, it is by no means the case that there are not legitimate reasons to pass by reference.
You should not create classes willy-nilly every time you have a function or method that needs to return more than one value. Consider why you are returning more than one value and if it makes sense to create a class for that then do so. Otherwise, just pass in pointers.
-Just my 2 cents
Try this
+(void)filename:(NSString **)file {
*file=#"folder_b";
}
and send the file as &file like:
NSString *file;
[function fileName:&file];
nslog(#"%#",file);
hope this will work.
I suspect this is because NSString is immutable. Have you tried NSMutableString?
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.