I create two function with ObjectiveC, like:
- (void)showName:(NSString *)name, ...;
- (void)showTitle:(NSString *)title;
I can call showTitle: in my swift code, but can't compile if call showName:
Here is my code:
//Objective_C code:
#interface DemoObject : NSObject
- (void)showName:(NSString *)name, ...;
- (void)showTitle:(NSString *)title;
#end
#implementation DemoObject
- (void)showName:(NSString *)name, ... {
NSLog(#"name=%#", name);
}
- (void)showTitle:(NSString *)title {
[self showName:title, #""];
}
#end
//Swift Code:
var obj = DemoObject()
obj.showTitle("");
obj.showName(""); //compile error here
How to fix this problem. Because I use a third library, it contains Variable parameters functions.
Swift does not import C functions or Objective-C methods with varargs.
Any good API that has varargs functions also has an alternative form of the function that takes a va_list (e.g. printf has vprintf); or else it has an easy way to achieve the same thing by adding the arguments one by one.
Related
This sounds ridiculous but its not working. Can someone see what I am missing?
I have a ViewController in which I defined a function called ProcessData. I am trying to call ProcessData from the viewDidLoad function but it does not see it. I declared it in the .h file and then defined it in the .m file. Here is the code.
ViewController.h
#interface ViewController : UIViewController
- (void) ProcessData: (NSString *)response;
#end
ViewController.m
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[ProcessData nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) ProcessData: (NSString *)response
{
}
#end
To call a class / static function you have to write
[ViewController ProcessData: nil];
you have to call the method ProcessData on the class ViewController.
But what you have here is not a class / static method since it is - (void) instead of + (void). Your mistake is basically using the wrong syntax for calling any method. You call methods on objects like
[self ProcessData: nil];
instead of
[ProcessData: nil];
Final Note: method names should start with a lower character! The method should be called processData!
In this case, you haven't declared a class method, you've declared an instance method, so you'd call it by doing:
[self ProcessResponse: nil];
If you want to declare a class function, you need to use a + at the start if the declaration instead of a -:
+ (void) ProcessResponse: (NSString *)response;
You'd call this like this:
[ViewController ProcessResponse: nil];
As a side note, you should actually follow common convention by formatting like this:
+ (void)processResponse:(NSString *)response;
Note pascal case (no capital at the start of the method name), no space between return type and name, and no space between method name and the parameter type.
It is not a class method. It's a instance method. You have to call it like this:
[self ProcessResponse:nil];
Class method is defined with "plus" symbol (+), not a hyphen (-).
And method name should start with lowercase.
I can declare a method in the #interface having parameter type NSString*:
- (id) initWithString:(NSString*)str;
while in the implementation it is NSNumber*:
- (id) initWithString:(NSNumber*)str
For a full example, see the code below. When calling [Work test] the output is a.x = Hi, so the passed-in NSString* went through and one can see that the "correct" initWithString method was called.
Why is this code accepted by the compiler?
Can I make the compiler complain when parameter types differ?
Citing from Apple's documentation Defining Classes :
The only requirement is that the signature matches, which means you must keep the name of the method as well as the parameter and return types exactly the same.
My test code:
#interface ClassA : NSObject
#property (strong, nonatomic) NSNumber *x;
- (id) initWithString:(NSString*)str;
- (void) feed:(NSString*)str;
#end
#implementation ClassA
- (id) initWithString:(NSNumber*)str
{
self = [super init];
if (self) {
self.x = str;
}
return self;
}
- (void) feed:(NSNumber*)str
{
self.x = str;
}
#end
#implementation Work
+ (void) test
{
ClassA *a = [[ClassA alloc] initWithString:#"Hi"];
NSLog(#"a.x = %#", a.x);
}
#end
I added the feed method to see, whether it is "special" to init-like methods, but the compiler doesn't complain either.
(Ran this on Yosemite / Xcode 6.4 / iOS8.4 Simulator.)
PS: If I didn't use the right terms, please correct me :-)
Can I make the compiler complain when parameter types differ?
There's a warning for this which you can activate by including the following line in the header:
#pragma clang diagnostic error "-Wmethod-signatures"
You can also put -Wmethod-signatures into the project's "Other Warning Flags" Xcode build setting to activate this for the whole project.
I don't really understand why Apple is so hesitant to activate helpful warnings like this by default.
My standard pattern on virtually every project is to put -Weverything in "Other Warning Flags". This activates all warnings clang has to offer.
Since there are some warnings that are a little too pedantic or don't serve my coding style, I individually deactivate unwanted warning types as they pop up.
I'm surprised by the quote you found stating that param and return types matter to the uniqueness of the method signature. Re-reading, I think you found a bug in the doc.
Defining a parameter type in the interface will generate a warning for callers that do not pass that type (or cast the parameter to that type), no matter the implementation. Changing the parameter type in the implementation is exactly like casting the parameter within the method. Nothing wrong with that, not even a cause for warning. So long as the different type shares methods (polymorphic or inherited) with the declared type.
In other words, restating by example...
The following will cause a compiler error, proving that distinct param types offers no distinction to the compiler (same is true for return type)...
// .h
- (void)foo:(NSString *)str;
// .m
- (void)foo:(NSString *)str {
NSLog(#"called foo %#", [str class]);
}
- (void)foo:(NSNumber *)str { <----- duplicate declaration error
}
The following will cause no compiler warnings, errors or runtime errors...
// .h
- (void)foo:(NSString *)str;
// .m
- (void)foo:(NSNumber *)str {
// everything implements 'class', so no problem here
NSLog(#"called foo %#", [str class]);
}
The following is exactly like the previous example in every respect...
// .h
- (void)foo:(NSString *)str;
// .m
- (void)foo:(NSString *)str {
NSNumber *number = (NSNumber *)str;
NSLog(#"called foo %#", [number class]);
}
The following will cause no warnings, but will generate a runtime error because we are abusing the cast by invoking a method that the passed type doesn't implement (presuming the caller calls with a string as the interface indicates)...
// .h
- (void)foo:(NSString *)str;
// .m
- (void)foo:(NSNumber *)str {
NSLog(#"does str equal 2? %d", [str isEqualToNumber:#2]); <--- crash!
}
All of the foregoing matches intuition and behavior in practice, just not that passage in the doc. Interesting find!
In Objective-C a method is defined as a string (known as a selector) in the form of doSomethingWithParam:anotherParam:. Or in your case it will be initWithString:. Note there's no parameter types in these strings. One side-effect of defining methods like this is that Objective-C, unlike Java or C++ doesn't allow overloading operators by just changing the parameter type. Another side-effect is the behavior you observed.
EDIT: Additionally, it appears that the compiler does not look at the implementation at all when checking method calls, just the interface. Proof: declare a method in a header, don't specify any implementation for that method, and call this method from your code. This will compile just fine, but of course you'll get an "unrecognized selector" exception when you run this code.
It'd be great if someone could provide a nice explanation of the default compiler behavior.
I will explain the need based on below example
This is the method that need to be called after an async operation
-(void) myCallbackMethodOne: (NSString *)response
{
//method to be called
}
-(void) myCallbackMethodTwo: (NSString *)response
{
//method to be called
}
-(void) getDataFromServerWithCallback: (NSString *)requestString _Callback(CallbackMethod *) methodName
{
//logic to send request and
//to set callback method something similar to
[setCallbackMethod methodName];
}
-(void) onDataRecievedFromServerWithResponse: (NSString *) response //this method gets called as part of framework
{
[callTheCallbackMethod: response];
}
A place to call the method to demonstrate the requirement
-int main()
{
[getDataFromFromServerWithCallback: #"getTopNews" _Callback:myCallbackMethodOne]; //this is the requirement; I should be able to pass myCallbackMethod as argument
[getDataFromFromServerWithCallback: #"getBusinessNews" _Callback:myCallbackMethodTwo]; //this is the requirement; I should be able to pass myCallbackMethod as argument
}
There are two well established patterns for this type of functionality:
1) Delegate:
#protocol ResponseDelegate
- (void)handleResponse:(NSString *)response;
#end
#interface CommsClass : NSObject
#property (weak) id<ResponseDelegate> delegate;
- (void)sendRequest:(NSString *)request;
#end
#interface CallingClass : NSObject <ResponseDelegate>
{
CommsClass _commsClass;
}
- (void)callingCode;
#end
#interface CallingCode
- (void)callingCode
{
_commsClass = [CommsClass new];
_commsClass.delegate = self;
[_commsClass sendRequest:#"Blah"];
}
- (void)handleResponse:(NSString *)response
{
NSLog(#"Whoot: %#", response);
}
#end
2) Blocks.
typedef (^HandleResponseBlock)(NSString *response);
#interface CommsClass : NSObject
- (void)sendRequest:(NSString *)request
withCompletionBlock:(HandleResponseBlock)block;
#end
For objective C, I believe you have to pass the callback using block. But for Swift, since methods are also first class citizens, we can do something like this:
func buttonDidTapped(sender: AnyObject!) {
doSomethingWithCallback(callbackFunc: myCallback)
}
func doSomethingWithCallback(callbackFunc: (NSDictionary)->()) {
//do something
callbackFunc(["param": "pass any param by dynamic dictionary"])
}
func myCallback(infoDict: NSDictionary) {
//callback implementation
}
We can define the callback and as any functions and pass it like any parameters.
For more information on using objective-c and swift in the same project, please refer to Apple document:
https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html
Hope that it helps.
I got my requirement working by using selectors
-(void) myCallbackMethodOne: (NSString *)response
{
//method to be called
}
-(void) myCallbackMethodTwo: (NSString *)response
{
//method to be called
}
-(void) getDataFromServerWithCallback: (NSString *)requestString _Callback:(SEL) methodName _Caller(id):callingClass
{
//write the logic here to store methodname and caller to reference variables
//so that it will be accessible in onDataRecievedFromServerWithResponse
//and to send the request
}
-(void) onDataRecievedFromServerWithResponse: (NSString *) response //this method gets called as part of framework
{
[callingClass performSelector:methodName withObject:response];
}
-int main()
{
SEL methodOneSelctor =#selector(myCallbackMethodOne:);
[getDataFromFromServerWithCallback: #"getTopNews" _Callback:methodOneSelctor _MethodCaller:self]; //I should be able to pass myCallbackMethod as argument
SEL methodTwoSelctor =#selector(myCallbackMethodTwo:);
[getDataFromFromServerWithCallback: #"getBusinessNews" _Callback:methodTwoSelctor _MethodCaller:self]; //I should be able to pass myCallbackMethod as argument
}
I wanted to use
for (TBL_CardView *cardView in cardsInHand)
{
// <#statements#>
}
TBL_CardView is my custom class, and cardsInHand is just (TBL_CardViewArray*)
So I need to implement countByEnumeratingWithState:objects:count: for my TBL_CardViewArray class.
Is this correct ?
This is my TBL_CardViewArray.h
/**
* Keep TBL_CardView in array
*/
#interface TBL_CardViewArray : NSObject
- (TBL_CardView *)drawCard;
- (void)addCard:(TBL_CardView *)card;
- (NSUInteger)cardsRemaining;
- (NSArray*) cardViewArray;
- (TBL_CardView *)drawRandomCard;
#end
Some important part from TBL_CardViewArray.m
#implementation TBL_CardViewArray
{
NSMutableArray *_cards;
}
So I am just using TBL_CardViewArrayas s wrapper around NSMutableArray for storing my TBL_CardViewclass.
Question
How to implement countByEnumeratingWithState:objects:count: for my TBL_CardViewArray class.
I did google it, but not found some example that I could reuse easy.
My assumption is that because I am already using NSMutableArray for storing that it is not so complicated, but I can not figure it how ?
Quite simply, forward it to the underlaying NSMutableArray:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])stackbuf count:(NSUInteger)len {
return [_cards countByEnumeratingWithState:state objects:stackbuf count:len];
}
You have to avoid mutating the array while it's being enumerated.
I'm new to Objective-C and I'm trying to determine if the NSString being passed to my method is the same as the NSString previously passed to the same method.
Is there a simple way to do this?
If you're looking to do this per instance of your class (and not globally):
#interface MyClass : NSObject
- (void)myMethod:(NSString *)value;
#end
#interface MyClass ()
#property (copy, nonatomic) NSString *value;
#end
#implementation MyClass
- (void)myMethod:(NSString *)value
{
if ([self.value isEqualToString:value])
{
// Values are the same!
}
else
{
self.value = value;
}
}
#end
Store the string as an instance variable of the class, each time the method is called, compare the instances and replace with the new parameter.
Just to elaborate on what #Wain said:
Add Instance Variable:
#interface ViewController ()
{
NSString * lastString;
}
Then in your method:
- (void) methodWithString:(NSString *)string {
if ([string isEqualToString:lastString]) {
NSLog(#"Same String");
}
else {
NSLog(#"New String");
lastString = string;
}
}
A variation on the theme most answers are following: If you wish to do this per instance of your class then you can use an instance variable. However as this is something which is really specific to your method, you don't want to declare this variable in any interface and recent compilers help you with this by enabling instance variable declaration in the implementation. For example:
#implementation MyClass
{
NSString *methodWithString_lastArgument_; // make it use clear
}
- (void) methodWithString:(NSString *)string
{
if ([string isEqualToString:methodWithString_lastArgument_])
{
// same argument as last time
...
}
else
{
// different argument
isEqualToString:methodWithString_lastArgument_ = string.copy; // save for next time
...
}
}
(The above assumes ARC.)
The string.copy is shorthand for [string copy] and is there to handle mutable strings - if the method is passed an NSMutableString then this will copy its value as an (immutable) NSString. This protects the method from the mutable string changing value between calls.
If you want to do this on a global basis, rather than per instance, you can declare a static variable within your method and thus completely hide it from outside the method:
- (void) methodWithString:(NSString *)string
{
static NSString *lastArgument = nil; // declare and init private variable
if ([string isEqualToString:lastArgument])
{
// same argument as last time
...
}
else
{
// different argument
isEqualToString:lastArgument = string.copy; // save for next time
...
}
}
HTH