During a talk at the #Scale 2014 conference (around 32:30), Facebook presented their implementation of a declarative UI approach. The slides for a more detailed version of the talk can be found here.
Basically they presented a function call like this (I made my own simplified example from the example in the talk):
[CPInsetComponent
newWithStyle:{
.margin = 15
}
];
My question is: Is this valid ObjC code? I tried to implement this myself
typedef struct {
uint margin;
} CPInsetComponentStyle;
#interface CPInsetComponent : NSObject
+ (SomeOtherStruct) newWithStyle:(CPInsetComponentStyle)style;
#end
but I still get an "expected expression" error on the newWithStyle:{ line. Could you give me a hint how the method declaration would look like?
No, that's not valid Objective-C code. A C99 compound literal of struct type looks like this:
(TheStructType) { .field1 = initializer1, .field2 = initializer2 }
where the field designators are optional.
I can imagine that the code they were presenting was actually Objective-C++. In C++11, the compiler can insert implicit calls to constructors taking an initializer list if certain conditions are met; hence, often you can pass just an initializer list to a function.
The compiler probably doesn't know if your literal struct declaration is of the correct type. For compound literals you need to provide the type in parenthesis followed by a brace-enclosed list of initializers.
[CPInsetComponent newWithStyle:(CPInsetComponentStyle){
.margin = 15
}];
Related
I have this NS_ENUM called ObserveType having two properties called Observe and ObserveAll. I can access the ObserveAll property as you can see from the picture, but I can't access Observe.
The NS_ENUM is in a header file of objective-C.
I know that changing Observe to ObserveX or ObserveXYZ will work.
But how do I access Observe without changing the name of the Observe?
Notice that I have to access the Observe on Swift.
In addition to answers above, I'd like to point out that you can give your Objective-C NS_ENUM a Swift name with NS_SWIFT_NAME macro:
typedef NS_ENUM(NSUInteger, XYZAwesomeEnum) {
XYZAwesomeEnumA,
XYZAwesomeEnumB,
XYZAwesomeEnumC,
} NS_SWIFT_NAME(AwesomeEnum);
Use it later in Swift:
AwesomeEnum.a
Apple Developer: Renaming Objective-C APIs for Swift
From Language Guide - Interoperability - Interacting with C APIs:
"The prefixes to C enumeration case names are removed when they are imported into Swift, whether they’re defined in system frameworks or
in custom code."
This means your first case in the ObserveType enum have no name after being imported to Swift (which I'm somewhat surprised doesn't yield a compile error). If we were to print out the conversion, it would look something like (conceptually)
typedef NS_ENUM(NSInteger, ObserveType) {
Observe = 0
ObserveAll = 1
};
// imported like ...
enum ObserveType: Int {
case _ = 0 // obviously illegal if applied directly in Swift
case All
}
You could try to access the nameless case by using its rawValue (0), but I would recommend updating the name of the first case in your Obj-C enum, if possible.
if let empty = ObserveType(rawValue: 0) {
print(empty) // prints ""?
}
dfri's answer is spot on, and to extend on his answer, it is probably best to name the enum and its cases following the widely adopted conventions. In the following code, I am making certain guesses on your intention.
typedef NS_ENUM(NSUInteger, XYZObserveType) {
XYZObserveOne,
XYZObserveAll
}
Another tip that I find useful when working with projects contain both Swift and Objective-C code is Generated Interface in the source editor.
When you are viewing an Objective-C class, selecting this option will show you the generated Swift header, which can be helpful in many occasions.
Why the compiler gives an error in this case of method declaration -
-(void) someMethod (void);
But approves this -
-(void) someMethod;
(SomeClass.h)
I've read that it is better to declare (void) in parameters than not declaring, but probalby I miss some point.
You cannot do this for Objective-C.
In Objective-C, every parameter must be after : e.g.
- (void)someMethod:(int)i;
- (void)someMethod:(int)i withString:(NSString *)string;
- (void)someMethod:(int)i :(int)i2 :(int)i3; // you can do this but is bad style
and it does not make sense to make something like
- (void)someMethod:(void)what_goes_here;
so if you want a method without parameter:
- (void)someMethod;
However you can do it in C/C++
void someMethod(void);
And I didn't see any benefit of declare void parameters (explicitly declare things is not always good).
Expanding on #xlc's answer
The answer is a difference in syntax between Objective-C and "normal" C/C++.
Way back in the origins of Unix and C days, late 60's/early 70's, when declaring (not defining) a function, you didn't need to specify how many arguments it accepted, or what types they needed to be. Nor did you need to specify whether or not it returned a value.
Later, folks realized that this would be a good idea, both for better error detection at compile time and for greater efficiency of generated code. So implementers added the ability to specify argument types in the function declaration. This was standardized as part of ANSI C in the late 80's.
However, there was a need to retain backwards compatibility with existing code. So a function declaration of foo() couldn't be assumed to mean "function with no arguments". To solve this, the void keyword was introduced. That allowed you to say foo(void) to mean "function named foo which takes no arguments".
When Objective-C was invented in the 90's, they added a new syntax for defining methods. Because there was no legacy code to deal with, they simply said that a method has to declare all its arguments; if there are none, then the method takes no arguments.
Objective-C still uses the void keyword to indicate that a method returns no value, though.
I am still learning Objective-C but I like to know the "why" behind everything I learn.
I would like to know why an Objective-C method requires that the types are enclosed in parentheses, such as:
- (IBAction) myAction: (UIButton *) sender;
Instead of:
- IBAction myAction: UIButton *sender;
I've tried finding answers and thought about it quite a bit but can't seem to see what the reasoning is for.
What troubles me is sometimes I actually forget that the asterisk (*) needs to be inside the parentheses, sometimes I accidentally type the following incorrect signature:
- (IBAction) myAction: (UIButton) *sender;
As to me, this more logically represents the argument is a pointer, not the type.
That's C casting syntax:
int foo = (int)bar;
Think of it as casting the parameters and return value to specific types.
In the very early days of Objective-C return values and parameters defaulted to the id type. So you'd see method declarations like this:
-myAction:sender;
For numerous reasons it became preferable to strongly type the return value and parameters in Objective-C code, to the point that all return values and parameters are strongly typed, even if they're id:
- (IBAction)myAction:(id)sender;
The asterisk is also C syntax. UIButton* is a specific type, different from UIButton and UIButton**. You could do this:
typedef UIButton* UIButtonRef;
and then use UIButtonRef instead of UIButton*:
- (IBAction)myAction:(UIButtonRef)sender;
__attribute__((const)) is a GCC attribution to check purity of the function execution.
I think this is being supported on Clang, but when I put this to a method, it doesn't seem to work.
#interface C1
- (id)method1 __attribute__((const));
#end
int a = 0;
#implementation C1
- (id)method1 __attribute__((const))
{
a++;
return nil;
}
#end
The code above doesn't generate any warning or error.
Is this attribution working on Clang? Or what should I do to make this to work?
The const attribute doesn't check the purity of the function. It declares the purity of the function, so the optimizer can eliminate calls to the function. It's up to you to actually make sure the function is pure.
Declaring a method const can not work in Objective-C because it contradicts the runtime's message passing mechanism. The documentation for const says:
Note that a function that has pointer arguments and examines the data pointed to must not be declared const.
Every Objective-C method has at least one (implicit) pointer argument: self. It is always examined as part of message passing (objc_msgSend()). That's what makes Objective-C dynamic.
I've tried to use id to create duck typing in objective-c. The concept looks fine in theory but failed in practice. I was unable to use any parameters in my methods. The methods were called but parameters were wrong. I was getting BAD_ACESS for objects and random values for primitives. I've attached a simple example below.
The question:
Does any one knows why the methods parameters are wrong?
What is happening under the hood of the objective-c?
Note: I'm interest in the details. I know how to make the example below work.
An example:
I've created a simple class Test that is passed to an other class using property id test.
#implementation Test
- (void) aSampleMethodWithFloat:(float) f andInt: (int) i {
NSLog(#"Parameters: %f, %i\n", f, i);
}
#end
Then in the class the following loop is executed:
for (int i=0; i < 10; ++i) {
float f=i*0.1f;
[tst aSampleMethodWithFloat:f andInt:i]; // warning no method found.
}
Here is the output that I'm getting. As you can see the method was called but the parameters were wrong.
Parameters: 0.000000, 0
Parameters: -0.000000, 1069128089
Parameters: -0.000000, 1070176665
Parameters: 2.000000, 1070805811
Parameters: -0.000000, 1071225241
Parameters: 0.000000, 1071644672
Parameters: 2.000000, 1071854387
Parameters: 36893488147419103232.000000, 1072064102
Parameters: -0.000000, 1072273817
Parameters: -36893488147419103232.000000, 1072483532
Update:
I've found out by accident that when I add a declaration of aSampleMethodWith... to the class with for loop the warning disappears and the method on the Test class is called correctly.
Update 2:
As pointed out by JeremyP the direct cause of the problem is that the floats are treated as doubles. But anyone knows why? (following the 5why principle :) ).
According to #eman the call is translated to simple C function call and compiler directive to get the SEL. So the #selector gets confused. But why? The compiler have all necessary type informations in the first method call. Does any one knows a good source of information about the Objective-C internals I've search The Objective-C Programming Language but i didn't find the answer.
By default floating point values are passed as doubles, not floats. The compiler does not know, at the point where [tst aSampleMethodWithFloat:f andInt:i]; occurs that it is only supposed to pass a float, so it promotes f to a double. This means that, in the method, when the compiler does know it is dealing with a float, f is the float formed by the first four bytes of the double passed to the method and i is an int formed from the second four bytes of the double passed.
You can fix this by either
changing the first parameter of aSampleMethodWithFloat:andInt: to a double
importing the interface declaration of Test into the file where you use it.
NB there is no gain except a small amount of space when using floats in C. You might as well use doubles everywhere.
I think JeremyP is correct about the problem being about doubles vs floats. As for implementation details, message dispatch in Objective-C uses the objc_msgSend(id theReceiver, SEL theSelector, ..) C function (for some deep nitty-gritty, see here). You can simulate the same results of method dispatch like so:
SEL theSelector = #selector(aSampleMethodWithFloat:andInt:);
objc_msgSend(self.test, theSelector, 1.5f, 5);
SEL is just a number that corresponds to a function (that is dynamically determined based on the method signature). objc_msgSend then looks up the actual function pointer (of type IMP) of the method and invokes it. Since objc_msgSend has a variable number of arguments, it will just use as many as you pass in. If you were to do:
objc_msgSend(self.test, theSelector, 1.5f);
It would use 1.5f correctly and have junk for the other variable. Since the method signature typically denotes the number of arguments, this is hard to do under normal usage.
You can make the warning go away by making a category like this:
#interface NSObject (MyTestCategory)
- (void) aSampleMethodWithFloat:(float) f andInt: (int) i;
#end
Without a signature available at the calling point, it isn't known what type the parameters are supposed to have. Undefined methods will be assumed to take ... as parameters, which isn't what yours does. If there is any interface seen by the compiler at this point, where the method in question exists, that definition will be used.
The trouble here is with the dividing line between C and Objective-C. The id type specifies any object, but ints and floats are not objects. The compiler needs to know the C type of all the arguments and the return type of any method you call. Without a declaration, it assumes that a method returns id and takes an arbitrary number of id arguments. But id is incompatible with int and float, so the value doesn't get passed correctly. That's why it works correctly when you provide a declaration — then it knows your int is an int and your float is a float.