Objective C method declaration/ calling - objective-c

I'm doing a video tutorial on iPhone programming, it's a very simple calculator app. At one point I declare the following method::
- (NSString*)calculate:(NSString*)operation withNumber:(NSInteger)number
{
return nil;
}
It's not implemented yet at this point. Then I want to call the method with:
self.display.text = [self calculate:[sender currentTitle] withNumber:[self.display.text intValue]];
Xcode is giving me an error here: 'expected expression'.
What's wrong here? And what is withNumber in the method? I would understand
- (NSString*)calculate :(NSString*)operation :(NSInteger)number;
Thats a method that takes a string and an int as parameters and returns a String. I don't get what withNumber does here.

OK, for it to work, you will need to remove the unnecessary spaces :
- (NSString*)calculate:(NSString*)operation withNumber:(NSInteger)number{
...
}
and on calling the method too of course.
As to 'what is withNumber ? ' : this is the way multi-input method look like in Objective-C, the name of the method does not precede the arguments. The method is actually named calculate:withNumber: in the runtime system
I strongly recommend reading some beginner's guide
You could do - (NSString*)calculate:(NSString*)operation :(NSInteger)number and then you will have to call [self calculate:myString :myNumber]; but the vast majority of Objective-C user would not do that : the language gives you the opportunity to clarify your code and specify what arguments is what : take that opportunity.

Related

EXC_BAD_ACCESS when accessing objective-c property

I am bridging an objective-c library to React. In order to return the data I need, I have to use a callback. The only problem is, the native method that kicks off the process that generates the data (which is the method that I call from my React js file), is NOT the method that returns the data. In fact, there are quite a few methods between the one that I call, and the final result.
So I have a few options. Either completely rewrite the entire objective-c program (f*ck that), pass the callback function as an argument to every single method and ultimately to the final method where it will be used (this 'works' but is ugly), or a third option which this post is about.
I am new to objective-c (very new) so go easy on me.
Instead of passing the callback function around to every single method, I want to try and make it 'global'. My attempt at doing this looks like
#property(nonatomic,copy)RCTResponseSenderBlock globalCallback;
and the method where it is 'initialized' (which is also the method I call from js)
RCT_EXPORT_METHOD(startScan:(RCTResponseSenderBlock)callback) {
self.globalCallback = callback;
...
}
Now just to test that this works, I added a line of code immediately below the line where I 'initialized' my globalCallback:
RCT_EXPORT_METHOD(startScan:(RCTResponseSenderBlock)callback) {
self.globalCallback = callback;
self.globalCallback(#[[NSNull null], #"Test string"]);
...
}
And it works (as far as I can tell). The callback returns "Test String" to React, and I am able to see it. So I went over to the method where I actually want to return information from and added the same line of code to it (obviously removing the one from the above example):
-(void)theMethodIWantToReturnFrom {
self.globalCallback(#[[NSNull null], #"Test string"]);
...
}
However, this time it does not work. I get EXC_BAD_ACCESS. From what I understand, this may mean that my globalCallback has been deallocated. But whatever it means, I have not been able to figure out a solution yet. Any ideas / explainations regarding this error?

ObjC: Error handling... what to return

Apologies for such a newbie question.
I have written a class method that takes three strings and returns a substring.
I have written conditional statements that only allow the substring to returned if certain criteria are met. However I am unsure what I need to return if the substring cannot be extracted. At the moment I have the method returning a default 'error' string, but I have the feeling that this may not be best practice.
Here is my method:
+(NSString *)ExtractSubstringFrom:(NSString *)sourceString
Between:(NSString *)firstString And:(NSString *)secondString {
NSRange stringRangeOne = [sourceString rangeOfString:secondString];
NSString *resultString;
if (stringRangeOne.location != NSNotFound) {
resultString = [sourceString substringToIndex:stringRangeOne.location];
}
NSRange stringRangeTwo = [sourceString rangeOfString:firstString];
if (stringRangeTwo.location !=NSNotFound) {
resultString = [resultString substringFromIndex:stringRangeTwo.location+stringRangeTwo.length];
return resultString;
}
else return #"Error!";
//To do... improve error checking
}
How can I make this method more error friendly?
There are several ways of handling errors of this kind in Objective C:
Returning the default value - typically, the default value is nil. Since performing operations on nil objects is allowed in Objective C, this is relatively safe.
Producing NSError objects - this is more typical of errors that can be addressed by end users, such as connection and configuration problems. The unfortunate side effect of this kind of API is the need to set up and pass an extra parameter.
Using an assertion - this is a good way of handling "programming errors", i.e. when your program's arguments are out of their specified ranges.
Throwing an exception - this option is available in the language, but Apple strongly discourages against using it. I mentioned it here for completeness, even though I never use this option myself.
The actual best practice here would be to NOT do this inside the class method, but instead implement Key Value Validation on the thing that will be accepting the value, and calling the validation method before it is set. So you would remove this validation/mutation logic from this method and instead put it inside your validation method, and in the controller the value is being modified called the validation method before setting the value. This gives your validation logic the opportunity to modify the value (i.e. to a default) or return an error if the input value can't be coerced into something useable.
For more general information on error handling in Objective-C, see Programming With Objective-C: Dealing with Errors and Error Handling Programming Guide

Is there something about this method name that indicates it's asynchronous?

I've inferred what a lot of things DO in Objective-C, and I've gone through several tutorials that simply talk about the data types, but I haven't run across anything that simply explains the syntax.
For starters, what does this mean? What it does is start a thread and get data returned from a server:
- (void)apiCall:(void (^)(NSMutableArray *list))block {
Does something in that function header tell me that it is asynchronous? is that what block means?
No, block doesn't mean asynchronous, a block in Obj-C is just a bit of code that can be passed as an argument to a method.
methods that start with - are instance methods and those that start with + are class methods.
^ is a syntactic marker to denote a block.
For your first question: you would have to look at the API documentation to find out if it is asynchronous.
For more information about blocks in general, see here:
Apple Blocks Programming Guide
Let's start with your second bullet:
Class methods are declared with +, instance methods are declared with -.
The first and third are related, the parameter named block is a code block, it's a piece of code intended to be run later. Given the name of this method apiCall, I suggest this being the method run after the call is done.
It would we natural to suspect that this method will do some work on another thread and then invoke the block you supplied, but for this you'd need to check the documentation or the code.
The signature: (void (^)(NSMutableArray* list)) block describes a code block with a void return type and a NSMutableArray* list as only parameter.
An example usage of the block parameter would be:
void (^apiCallCallback)(NSMutableArray*) = ^(NSMutableArray* list) {
NSLog(#"The API returned %d items in a list", [list length]);
}
[someApiInstance apiCall:apiCallCallback];
After the API instance is done doing whatever it is suppose to do, you'll see that the log statement is printed.

Type cast in cocoa for fix warnings

I have next situation:
Method:
-(void) myMethod:(id)inValue
{
long a = [inValue longValue];
}
Compiler shows me a warning that -longValue - is multiplied:
multiple methods named '-longValue' found
What can I do to resolve this warning without change method name?
Thank!
Strongly type your method's argument to tell the compiler which variant of the -longValue message you want to use, e.g.:
-(void) myMethod:(NSNumber *)inValue
{
long a = [inValue longValue];
}
If you want to accept multiple types that respond to -longValue (say, NSNumber and NSString) then you'll have to go back to using id and you'll see the warning. You see, something in your (yes, your, not Apple's) class hierarchy has bunged up and used a different signature for -longValue, so the compiler has no way of knowing which one you want to use. id tells it "this is an object" but it provides no explicit information that the compiler can use to resolve its conundrum.
Is there a particular reason why you're passing an id instead of a strongly-typed object?
I'm understood where from this warning - it completely right. If I were compiler developer - I were make possible something like this:
id a = [[[inValue class] alloc]] init];
for type cast; and after this may be:
a = inValue
[a longValue];
for resolve this warning.
But I'm not.)
And I'm only study programming for Mac. So i just asking - it possible or not.

Is there a reason why I sometimes read in Books "layoutSubviews Method" and sometimes "drawRect: Method"?

I am wondering why in Books and Apple Documentation sometimes a Method has that colon as suffix, but sometimes not. I guess it means that if it has no colon, it has no parameter. On the other side, if it has a colon, it has exactly one parameter. Right or wrong?
That's correct, though it is an easy typo to make. You should always check the documentation to ensure the signature of any method to avoid any runtime errors.
A method with signature:
- (void)refresh
Will be used like:
[myObject refresh];
A method with signature:
- (void)refreshView:(UIView *)view
Will be used like:
[myObject refreshView:view];
And finally, a method with signature:
- (void)refreshView:(UIView *)view updateLabels:(BOOL)update
Will be used like:
[myObject refreshView:view updateLabels:YES];
You're right that the trailing colon signifies a single parameter, and it's important to use the full including-colon name in code -- e.g. #selector(drawRect:)
However, while I can't find an example off hand, in prose, I believe you'll occasionally see methods written without the trailing colon just to make it read better. I know I do this when writing comments/documentation -- e.g. "Subclasses should customize the doFoo method" when I actually mean doFoo:. So, if you see method names in prose, it's probably a good idea to check in the header file or class reference documentation for the correct signature.
Objective-C refreshView: and refreshView are two different methods. The first takes one parameter, the other takes no paramaters. As you say.
This is important because that is the full name of the method, and you need to be able to write this correctly when passing selectors.
For example when showing a sheet:
- (void)beginSheet:(NSWindow *)sheet
modalForWindow:(NSWindow *)docWindow
modalDelegate:(id)modalDelegate
didEndSelector:(SEL)didEndSelector
contextInfo:(void *)contextInfo;
the didEndSelector is usually of the form:
- (void)sheetDidEnd:(NSWindow *)sheet
returnCode:(int)returnCode
contextInfo:(void *)contextInfo;
and so in the beginSheet method this will need to be passed to the didEndSelector parameter as:
#selector(sheetDidEnd:returnCode:contextInfo:);
Getting the signature of the selector wrong will lead to much late night head scratching while debugging.