Im currently learning some objective-c from the big ranch guide book. My understanding is that methods with multiple parameters use colons to separate each parameter, but when reading about creating arrays, i found this snippet of code:
NSArray *dateList = [NSArray arrayWithObjects:now, tomorrow, yesterday, nil];
This has left me confused as i thought objective-c method parameters must each be preceded by a portion of the method name along with a colon. Can anybody explain this to me?
This is an exception to the rule; this is commonly called a variadic method. If you look at the definition in NSArray.h:
+ (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
you see that you can specify an arbitrary number of parameters, as long as the last one is nil (this is called the sentinel).
This saves the developers from creating a large number of different methods having roughly the same functionality, each of which accept a different number of parameters. They did so in NSObject, where you have
- (id)performSelector:(SEL)aSelector withObject:(id)object1;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
(but no further methods).
The method only has one parameter, a variable parameter list.
Here is the Objective-C declaration from the Apple Developer website:
+ (instancetype nonnull)arrayWithObjects:(ObjectType nonnull)firstObj, ...;
There's no need for colon separation, because the object list is treated as one parameter, even thought it looks like many parameters!
Related
Read a function call like this in Apple's tutorial for OC. a bit confused about how function stringWithFormat is defined or its signature...
[NSString stringWithFormat:#"The magic number is %i", magicNumber];
A relative question is about NSLog as
NSLog(#"%i is a number", someScalarVarNumber);
Should a function call be like
[Obj FuncName:param FuncName1:param1 FuncName2:param2];
You said:
[I am] a bit confused about how function stringWithFormat is defined or its signature.
If you command-click on stringWithFormat in your code, it will take you directly to its declaration (and you can hit the "back" button to return to your code). Anyway, stringWithFormat is defined as follows:
+ (instancetype)stringWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
Those ellipses (...) indicate that it is a "variadic function", that it takes a variable length list of parameters separated by commas. This is a C programming pattern which is also incorporated into Objective-C.
In case you're wondering, that NS_FORMAT_FUNCTION is a hint to the compiler that the first parameter (1) is a printf-style format string (or more accurately, slightly richer rendition that NSString uses), and that the parameters starting at the second parameter (2) should match up with what appears in the format string. That lets the compiler check your list of parameters to see if it matches the format string.
The fact that they felt compelled to add this special logic for printf-style parameters is actually a clue to the deeper problem of variadic parameters: With the exception of printf-style case, it's hard to ensure that the parameters passed to the function match what the function was expecting.
As a result, you will generally only see variadic method declarations where the number of parameters being passed to a method is variable and that it has a printf-style format string. (Technically, you can use it in any situation with variable number of parameters, but in those situations there are generally better approaches, e.g. pass an array. In fact, if you look at Apple's newer Cocoa API, where they need variable number of parameters, they generally pass an array rather than using the variadic patterns that you'll see in some of the older API.)
So, you're right, we generally invoke a method like so:
[objectName funcName:firstValue secondParameterName:secondValue thirdParameterName:thirdValue];
But, in special cases, you can employ variadic functions.
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.
We are using a web service to fetch data. As project progressed, number of params being passed between functions also increased. To make it more readable and easier to add/remove parameters in future thought of using a dictionary instead. So every function calls will have only one parameter , a dictionary with all required parameters in it.
So to set a parameter I need to do [aDictionary setObject:foo forkey:#"bar"] and to get a parameter, it would be [aDictionary objectForKey:#"bar"].
To access the variables easily I thought of using a Model class with all common parameters used as properties. So for one function call say 3 out of 10 properties would be used and rest will be not be set. So it would be like paramModel.foo = #"bar".
If I would do it like this, will I be wasting memory for unused properties which are not initialised. And is this right in doing so ?
Continuing Amin Negm-Awad's points: If you are finding yourself passing along a lot of parameters to many methods, you almost certainly have incorrectly defined your model classes. Rather than creating a generic dictionary, create a simple value object that holds the related properties.
As an example, consider NSURL. You would never do this:
[self handleScheme:scheme host:host path:path];
Instead you do this:
NSURL *URL = [[NSURL alloc] initWithScheme:scheme host:host path:path];
[self handleURL:URL];
This is the correct way to consolidate parameters, and to improve your design.
Also, if you're taking a large number of boolean parameters or other kinds of "options," first consider whether you have a method that is doing too many things. Perhaps it should just be broken up into multiple methods that each works a specific way. If you really do need to pass multiple boolean options, in ObjC, this is often done with bit fields rather than with many parameters. Look at [NSString rangeOfString:options:] for an example.
No, it is strange. (Of course this is a subjective opinion.)
A. "As project progressed, number of params being passed between functions also increased."
There is only a connection between size of a software and numbers of parameters to a method (in your example there is no function): if you do something wrong in your design.
Split your code into modules, into classes, define good relationships between them and so on.
Define classes that can hold connected data.
B. I cannot see, what becomes more readable using dictionaries:
[receiver doSomethingWithA:#"a"
B:#"b"
C:#"c"];
NSMutableDictionary *aDictionary = [NSMutableDictionary new];
[aDictionary setObject:#"a" forKey:#"A"];
[aDictionary setObject:#"b" forKey:#"B"];
[aDictionary setObject:#"c" forKey:#"C"];
[receiver doSomethingWithABC:aDictionary];
It is more code, it is more complex code (because it needs an additional instance object), arguments and its "parameter destination" are wrested apart.
Inside the method you have to read out the arguments from the dictionary. This is additional boiler plate code.
I understand how to create my own methods that accept input parameters in objective-c but I have never actually created a method with more than one input parameter!
From methods I have used with multiple input parameters each has a name along the lines of
first:second:third:
and look like
- (void)first:(NSString *)fname second:(NSString *)mname third:(NSString *)lname;
my question is when creating your own method with multiple input parameters do you have to create a name like first:second:third or can you just have something like C++ where you have the one name followed by a list of input parameter types followed by the parameter names... if I remember correctly.
fullName:(NSString, NSString, NSString) fname, mname, lname;
No. A method must have the format as you described:
- (void)first:(NSString *)fname second:(NSString *)mname third:(NSString *)lname;
You have to have the parameters interleaved with the method signature. It's ok because xcode has code completion and it can give you nice descriptive names about what your method is doing and what it requires.
e.g.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
In the example above without even looking at the API for UIViewController you can get a pretty good understanding of how this method works and what it's params are. It is good practice to name your methods well to describe what they do (it can remove the need for most commenting if done well).
You may well of course see a method written like this
- (void)myMethodThatAcceptsARectangle:(float)x :(float)y :(float)w :(float)h;
But this will not be very clear in use as to what the parameters relate to:
[self myMethodThatAcceptsARectangle:1.0f :1.0f :1.0f :1.0f];
So you should avoid this (I added it incase you ever see this and wonder what's happening).
fullName:(NSString, NSString, NSString) fname, mname, lname;
Yes, you can do something like that. It'd look like this instead:
-(void)fullName:(NSString*)fname :(NSString*)mname :(NSString*)lname
and you'd call it like this:
[foo fullName:first :middle :last];
That largely defeats the point of Objective-C's method names, though, and the main reason to do something like that is to register your dislike of the normal Objective-C convention, or perhaps to get yourself kicked off whatever project you're working on.
Another option could be variadic parameters. They're used to provide a variable amount of parameters, even though you wouldn't have a name on each one of them. e.g.
[NSString stringWithFormat:#"My name is %# %#", #"John", #"Doe"];
It would be something like this:
- (void)names:(NSString *)names, ...;
Implementation, additional info
Here's a simple example for Method with parameters.
- (void)methodName:(NSString *)parameterOne methodNameContinues:(NSString *)parameterTwo;
For Example,
-(void)showAlertMsg:(NSString *)message withTitle:(NSString *)title;
Here you can see, we've a prefix "withTitle" for the second parameter. We've to proceed the same for the other parameters too.
I can think of a perfectly good reason to use NSDictionary to pass arguments. I also believe it answers the question.
You can place all of the items in an NSDictionary then unpack them. This maybe useful if you have say a persitanceStore NSObject that you want to send a list of parameters to.
First, I'm not sure I really understand what a selector is. From my understanding, it's the name of a method, and you can assign it to a class of type 'SEL' and then run methods such as respondToSelector to see if the receiver implements that method. Can someone offer up a better explanation?
Secondly, to this point, I have the following code:
NSString *thing = #"Hello, this is Craig";
SEL sel = #selector(lowercaseString:);
NSString *lower = (([thing respondsToSelector:sel]) ? #"YES" : #"NO");
NSLog (#"Responds to lowercaseString: %#", lower);
if ([thing respondsToSelector:sel]) //(lower == #"YES")
NSLog(#"lowercaseString is: %#", [thing lowercaseString]);
However, even though thing is clearly a kind of NSString, and should respond to lowercaseString, I cannot get the 'respondsToSelector' conditional to return "YES"...
You have to be very careful about the method names. In this case, the method name is just "lowercaseString", not "lowercaseString:" (note the absence of the colon). That's why you're getting NO returned, because NSString objects respond to the lowercaseString message but not the lowercaseString: message.
How do you know when to add a colon? You add a colon to the message name if you would add a colon when calling it, which happens if it takes one argument. If it takes zero arguments (as is the case with lowercaseString), then there is no colon. If it takes more than one argument, you have to add the extra argument names along with their colons, as in compare:options:range:locale:.
You can also look at the documentation and note the presence or absence of a trailing colon.
Selectors are an efficient way to reference methods directly in compiled code - the compiler is what actually assigns the value to a SEL.
Other have already covered the second part of your q, the ':' at the end matches a different signature than what you're looking for (in this case that signature doesn't exist).
That's because you want #selector(lowercaseString), not #selector(lowercaseString:). There's a subtle difference: the second one implies a parameter (note the colon at the end), but - [NSString lowercaseString] does not take a parameter.
In this case, the name of the selector is wrong. The colon here is part of the method signature; it means that the method takes one argument. I believe that you want
SEL sel = #selector(lowercaseString);
NSString's method is lowercaseString (0 arguments), not lowercaseString: (1 argument).
Don't think of the colon as part of the function name, think of it as a separator, if you don't have anything to separate (no value to go with the function) then you don't need it.
I'm not sure why but all this OO stuff seems to be foreign to Apple developers. I would strongly suggest grabbing Visual Studio Express and playing around with that too. Not because one is better than the other, just it's a good way to look at the design issues and ways of thinking.
Like
introspection = reflection
+ before functions/properties = static
- = instance level
It's always good to look at a problem in different ways and programming is the ultimate puzzle.
From my understanding of the Apple documentation, a selector represents the name of the method that you want to call. The nice thing about selectors is you can use them in cases where the exact method to be called varies. As a simple example, you can do something like:
SEL selec;
if (a == b) {
selec = #selector(method1)
}
else
{
selec = #selector(method2)
};
[self performSelector:selec];
As per apple docs:
https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Selector.html
A selector is the name used to select a method to execute for an object, or the unique identifier that replaces the name when the source code is compiled. A selector by itself doesn’t do anything. It simply identifies a method. The only thing that makes the selector method name different from a plain string is that the compiler makes sure that selectors are unique. What makes a selector useful is that (in conjunction with the runtime) it acts like a dynamic function pointer that, for a given name, automatically points to the implementation of a method appropriate for whichever class it’s used with. Suppose you had a selector for the method run, and classes Dog, Athlete, and ComputerSimulation (each of which implemented a method run). The selector could be used with an instance of each of the classes to invoke its run method—even though the implementation might be different for each.
Example:
(lldb) breakpoint --set selector viewDidLoad
This will set a breakpoint on all viewDidLoad implementations in your app.
So selector is kind of a global identifier for a method.