I am trying to convert this swift code to Objective-C
convenience init(fromString string: String, format:DateFormat)
{
if string.isEmpty {
self.init()
return
}
let string = string as NSString
switch format {
case .DotNet:
let startIndex = string.rangeOfString("(").location + 1
let endIndex = string.rangeOfString(")").location
let range = NSRange(location: startIndex, length: endIndex-startIndex)
let milliseconds = (string.substringWithRange(range) as NSString).longLongValue
let interval = NSTimeInterval(milliseconds / 1000)
self.init(timeIntervalSince1970: interval)
So far, I am doing this:
-(id) initFromString: (NSString *) string format: (DateFormat *) format{
if (string == nil) {
self = [self init];
return self;
}
switch (format) {
case .DotNet:
NSRange *startIndex = [[string rangeOfString:#"("] location]+1;
}
}
and have already run into the following errors:
for the switch(format): statement requires expression of integer type (DateFormat * __strong' invalid)
and for the 2 following lines: Expected expression
Any ideas ?
In Objective-C, the string is impliedly optional. Testing for nil merely tests whether a string was supplied. It doesn't check whether an empty string was supplied. You probably want to switch to string.length == 0 as, by the magic of nil-messaging, that'll work to check for either an empty string or no string at all.
Objective-C uses C's switch statement. So you can switch on integral types only. If this were Objective-C code originally, DateFormat would probably be an NS_ENUM — an integral type rather than an object type. It looks like the original was an enumeration from your use of dot syntax? If you can make it an Objective-C enumeration then do so and simply use:
- (id)initFromString:(NSString *)string format:(DateFormat)format {
....
switch(format)
{
case DateFormatDotNet: {
....
} break;
}
(with the curly brackets within the case being because you want to declare variables in there).
Otherwise, if it must be an object format then you're looking at a painful construction like:
if([format isEqual:[DateFormat dotNetFormat]]) {
}
else if([format isEqual:[DateFormat otherFormat]]) {
}
... etc ...
Also Objective-C has a syntactic distinction between structs, which are exactly what they are in C — named fields but no built-in behaviour — and object types, which is again because it's a strict superset of C. NSRange is a struct. So square bracket messaging syntax doesn't work on it. Instead of:
[[string rangeOfString:#"("] location]
Use:
[string rangeOfString:#"("].location
Square brackets around the rangeOfString call because it's a message dispatch to an object, then a dot for location because you get back a C struct as a value, and that's how one accesses a field in a C struct.
(dot syntax also works for properties on Objective-C objects, but explicitly to alias to getter and setter calls, and only for about the most recent of Objective-C's three decades)
Assuming this code is related to How to convert a Swift enum: String into an Objective-C enum: NSString?
Since your DateFormat variable is an object with a dateFormatType that is an NSString, you are going to have to use a chained if-else construct to select from the various possibilities:
if([format.dateFormatType compare: DotNetDateFormatType] == NSOrderedSame) {
[self handleDotNetDateFormat: format]
} else if ([format.dateFormatType compare: RSSDateFormatType] == NSOrderedSame) {
[self handleRSSDateFormat: format]
...
Objective-C has no concept of the dot-value syntax for enum values (so ".DotNet" is not a valid term in Objective-C). That's why the compiler is complaining about those either lines.
Related
I've searched Stackoverflow and other sites, but I can't seem to find this answer.
In Apple Text Editor source, they have at least one routine that does some apparently strange logical ANDing between two non-boolean variables. Casting them as Bools CAN be done, but doesn't make much sense. I'm learning Swift and much less familiar with Objective-C, but for the life of me, I can't figure out how they are trying to achieve the goal stated as "Build list of encodings, sorted, and including only those with human readable names."
Here is the code:
/* Return a sorted list of all available string encodings.
*/
+ (NSArray *)allAvailableStringEncodings {
static NSMutableArray *allEncodings = nil;
if (!allEncodings) { // Build list of encodings, sorted, and including only those with human readable names
const CFStringEncoding *cfEncodings = CFStringGetListOfAvailableEncodings();
CFStringEncoding *tmp;
NSInteger cnt, num = 0;
while (cfEncodings[num] != kCFStringEncodingInvalidId) num++; // Count
tmp = malloc(sizeof(CFStringEncoding) * num);
memcpy(tmp, cfEncodings, sizeof(CFStringEncoding) * num); // Copy the list
qsort(tmp, num, sizeof(CFStringEncoding), encodingCompare); // Sort it
allEncodings = [[NSMutableArray alloc] init]; // Now put it in an NSArray
for (cnt = 0; cnt < num; cnt++) {
NSStringEncoding nsEncoding = CFStringConvertEncodingToNSStringEncoding(tmp[cnt]);
if (nsEncoding && [NSString localizedNameOfStringEncoding:nsEncoding]) [allEncodings addObject:[NSNumber numberWithUnsignedInteger:nsEncoding]];
}
free(tmp);
}
return allEncodings;
}
The line in question contains the "&&." Any guidance would be appreciated.
Objective-C is a strict superset of C, so the same rules for logical
operators apply. In contrast to Swift, which is much more strict with
types, the logical operators in C take arbitrary scalar operands.
(The boolean type bool did not even exist in early versions of C,
it was added with the C99 standard.)
The C standard specifies (see e.g. http://port70.net/~nsz/c/c11/n1570.pdf, which is a draft of the C11 standard):
6.5.13 Logical AND operator
Constraints
2 Each of the operands shall have scalar type.
Semantics
3 The && operator shall yield 1 if both of its operands compare
unequal to 0; otherwise, it yields 0. The result has type int.
In your case, in
if (nsEncoding && [NSString localizedNameOfStringEncoding:nsEncoding])
the left operand has type NSUInteger (which can be unsigned long
or unsigned int, depending on the platform), and the right
operand has type NSString *, which is a pointer type. Therefore
the above expression is equivalent to
if (nsEncoding != 0 && [NSString localizedNameOfStringEncoding:nsEncoding] != 0)
where the zero in the right operand is the null pointer constant
which is usually written as NULL, or nil for Objective-C pointers:
if (nsEncoding != 0 && [NSString localizedNameOfStringEncoding:nsEncoding] != nil)
Some more information how this relates to Swift
Cocoa/Cocoa Touch Objective-C methods which return an object pointer
usually return nil to indicate an error
(compare Handling Error Objects Returned From Methods
in the "Error Handling Programming Guide"). So
[NSString localizedNameOfStringEncoding:nsEncoding] != nil
would mean "no localized name for the encoding could be determined".
The Swift equivalent would be a method returning an optional string,
and you could check the success with
NSString.localizedNameOfStringEncoding(nsEncoding) != nil
However, this does not compile, and here is the reason why: If you option-click on the Objective-C localizedNameOfStringEncoding method
in Xcode to show its declaration then you'll see
+ (NSString * _Nonnull)localizedNameOfStringEncoding:(NSStringEncoding)encoding
Here _Nonnull indicates that the method is not expected to return
nil. This kind of nullability annotations were introduced to
improve the mapping of Objective-C methods to Swift, see for example
"Nullability and Objective-C" in the Swift Blog.
Because of this _Nonnull annotation, the method is imported to Swift
as
public class func localizedNameOfStringEncoding(encoding: UInt) -> String
So testing the return value in Objective-C can be done but makes no
sense because the method always returns a non-nil value.
In Swift the compiler assumes that the return value is never nil
and returns a non-optional String.
The translation of that if-statement to Swift would therefore just be
if nsEncoding != 0 {
// ...
}
How can I have a method/function that can return any type? For example sometimes the type will need to be float and sometimes it will need to be NSString* so id won't work because float isn't an id. I am not opposed to doing it in a c or c++ function if it's easier.
The reason why I need a dynamic return type is because I'm using objc/runtime to get an Ivar.
I would like some_type to be able to anything:
- (some_type)getIvarWithName:(const char *)name in:(id)obj
{
Ivar ivar(class_getInstanceVariable(object_getClass(obj),name));
return (some_type)ivar;
}
Return a float wrapped in an NSNumber, then you can use the id return type.
To simplify it, you can even use boxing literals, for example:
return #(1.1f);
The first thing to think about is why you would need a function that can return any type. It really doesn't make sense because you wouldn't be able to assign the value to anything, since you don't know the type. Of course, the situation is different when dealing strictly with Obj-C objects, as the language utilizes unknown objects with the id keyword. Unknown objects are like mixing Honeycrisp apples with Macintosh apples (no pun intended), and what you are trying to do is like mixing Honeycrisp apples with airplanes! However, if you want a certain type returned based off of the parameters (such as returning int for int parameters and float for float parameters), then you can overload the functions. Otherwise, then only way that I know of to return absolutely anything would be a void pointer (void *). This would point to a chunk of data that could really be anything. But back to the original problem. What does it represent and how long is it? Good luck!
UPDATE: As other answers mention, you can wrap simple data types (int, float, etc.) in objects such as NSNumbers or NSValues, which will work for your case. But when extending to more general scenarios with complex types such as structs, these generally can't be wrapped in built-in classes. You would need to make your own class using Obj-C.
There is no polymorphism of that kind in Obj-C.
If you know in advance what will be returned then you could use to methods of course.
Retruning id would work when you use an NSNumber for the float value.
You could even introduce a response object that either carries a number or a string and provides (bool) isNumber and (bool) isString methods for later processing.
But what are you really up to? In which context are you using that and what do you really try to achieve. To me it sounds as if there may be better solutions available.
Ofcourse it's weird solution, but you have weird question.
You need enable objective-c++: rename .m-file to .mm
Then yours code will look something like that:
void weird_function(int a)
{
switch (a)
{
case 0: throw #"Hello";
default: throw a;
}
}
void some_code(int a)
{
try
{
weird_function(a);
}
catch (int a)
{
NSLog(#"Catch int: %d", a);
}
catch (NSString* str)
{
NSLog(#"Catch string: %#", str);
}
}
Yours method can be implemented something like that:
union ValueHolder
{
void* voidPtrValue;
int intValue;
float floatValue;
NSString* nssstringValue;
};
- (void)getIvarWithName:(const char *)name in:(id)obj
{
ValueHolder vh;
Ivar ivar = object_getInstanceVariable(obj,name, &vh.voidPtrValue));
if (NULL == ivar)
return;
const char* encoding = ivar_getTypeEncoding(ivar);
if (0 == strcmp(encoding, #encode(int)))
throw vh.intValue;
if (0 == strcmp(encoding, #encode(float)))
throw vh.floatValue;
if (0 == strcmp(encoding, "#\"NSString\""))
throw vh.nsstringValue;
}
I found that using a template in c++ works to have a custom type
The following code works best for my situation:
template <typename _type>
static inline _type & hookVar(id self, NSString*name)
{
Ivar ivar(class_getInstanceVariable(object_getClass(self),[name UTF8String]));
#if __has_feature(objc_arc)
void *pointer(ivar == NULL ? NULL : reinterpret_cast<char *>((__bridge void *)self) + ivar_getOffset(ivar));
#else
void *pointer(ivar == NULL ? NULL : reinterpret_cast<char *>(self) + ivar_getOffset(ivar));
#endif
return *reinterpret_cast<_type *>(pointer);
}
To call the function I just use something like:
NSWindow *win = hookVar<NSWindow*>(self, #"_window");
I'm getting a value back from an Objective C library (it's Firebase, but that doesn't really matter) of type id. The documentation states that this value will be an NSNumber for both boolean and numeric results. I want to take a different action based on whether or not this result corresponds to a boolean.
I know this is possible because printing out the class of the result via NSStringFromClass([value class]); gives "__NSCFBoolean" for booleans, but I'm not really sure how to correctly structure the comparison.
The objCType method gives information about the type of the data contained in the
number object:
NSNumber *n = #(1.3);
NSLog(#"%s", [n objCType]); // "d" for double
NSNumber *b = #YES;
NSLog(#"%s", [b objCType]); // "c" for char
The possible values are documented in
"Type Encodings"
in the "Objective-C Runtime Programming Guide".
Since BOOL is defined as unsigned char, it is reported as such by this method.
This means that you cannot distinguish it from a NSNumber object containing any char.
But it is sufficient to check between "boolean" and "numeric":
if (strcmp([obj objCType], #encode(BOOL)) == 0) {
// ...
} else if (strcmp([obj objCType], #encode(double)) == 0) {
// ...
} else {
// ...
}
I want to write a function or a directive like NSLog() that takes any kind of variable, primitives and objects. In that function I want to distinguish those.
I know how it works for objects:
- (void)test:(id)object {
if ([object isKindOfClass:[NSString class]])
...
but how do I distinguish objects from structs or even integer or floats.
Something like:
"isKindOfStruct:CGRect" or "isInt"
for example?
Is this possible?
I thought since you can send everything to NSLog(#"...", objects, ints, structs) it must be possible?
Thanks for any help!
EDIT
My ultimate goal is to implement some kind of polymorphism.
I want to be able to call my function:
MY_FUNCTION(int)
MY_FUNCTION(CGRect)
MY_FUNCTION(NSString *)
...
or [self MYFUNCTION:int]...
and in MY_FUNCTION
-(void)MYFUNCTION:(???)value {
if ([value isKindOf:int])
...
else if ([value isKindOf:CGRect])
...
else if ([value isKindOfClass:[NSString class]])
...
}
I know that isKindOf doesn't exists and you can't even perform such methods on primitives. I'm also not sure about the "???" generic type of "value" in the function header.
Is that possible?
#define IS_OBJECT(T) _Generic( (T), id: YES, default: NO)
NSRect a = (NSRect){1,2,3,4};
NSString* b = #"whatAmI?";
NSInteger c = 9;
NSLog(#"%#", IS_OBJECT(a)?#"YES":#"NO"); // -> NO
NSLog(#"%#", IS_OBJECT(b)?#"YES":#"NO"); // -> YES
NSLog(#"%#", IS_OBJECT(c)?#"YES":#"NO"); // -> NO
Also, check out Vincent Gable's The Most Useful Objective-C Code I’ve Ever Written for some very handy stuff that uses the #encode() compiler directive (that) returns a string describing any type it’s given..."
LOG_EXPR(x) is a macro that prints out x, no matter what type x is, without having to worry about format-strings (and related crashes from eg. printing a C-string the same way as an NSString). It works on Mac OS X and iOS.
A function like NSLog() can tell what types to expect in its parameter list from the format string that you pass as the first parameter. So you don't query the parameter to figure out it's type -- you figure out what type you expect based on the format string, and then you interpret the parameter accordingly.
You can't pass a C struct or primitive as a parameter of type id. To do so, you'll have to wrap the primitive in an NSNumber or NSValue object.
e.g.
[self test: [NSNumber numberWithInt: 3.0]];
id is defined as a pointer to an Objective-C object.
#alex gray answer did not work(or at least did not work on iOS SDK 8.0). You can use #deepax11 answer, however I want to point how this 'magic macro' works. It relies on type encodings provided from the system. As per the Apple documentation:
To assist the runtime system, the compiler encodes the return and argument types for each method in a character string and associates the string with the method selector. The coding scheme it uses is also useful in other contexts and so is made publicly available with the #encode() compiler directive. When given a type specification, #encode() returns a string encoding that type. The type can be a basic type such as an int, a pointer, a tagged structure or union, or a class name—any type, in fact, that can be used as an argument to the C sizeof() operator.
To break the macro apart, we first get "typeOf" our variable, then call #encode() on that type, and finally compare returned value to 'object' and 'class' types from encoding table.
Full example should look like:
const char* myType = #encode(typeof(myVar));//myVar declared somewhere
if( [#"#" isEqualToString:#(myType)] || [#"#" isEqualToString:#(myType)] )
{
//myVar is object(id) or a Class
}
else if ( NSNotFound != [[NSString stringWithFormat:#"%s", myType] rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:#"{}"]].location )
{
//myVar is struct
}
else if ( [#"i" isEqualToString:#(myType)] )
{
//my var is int
}
Please note that NSInteger will return int on 32-bit devices, and long on 64-bit devices. Full list of encodings:
‘c’ - char
‘i’ - int
’s’ - short
‘l’ - long
‘q’ - long long
‘C’ - unsigned char
‘I’ - unsigned int
’S’ - unsigned short
‘L’ - unsigned long
‘Q’ - unsigned long long
‘f’ - float
‘d’ - double
‘B’ - C++ bool or a C99 _Bool
‘v’ - void
‘*’ - character string(char *)
‘#’ - object(whether statically typed or typed id)
‘#’ - class object(Class)
‘:’ - method selector(SEL)
‘[<some-type>]’ - array
‘{<some-name>=<type1><type2>}’ - struct
‘bnum’ - bit field of <num> bits
‘^type’ - pointer to <type>
‘?’ - unknown type(may be used for function pointers)
Read more about Type Encodings at Apple
#define IS_OBJECT(x) ( strchr("##", #encode(typeof(x))[0]) != NULL )
This micro works which I got somewhere in stack overflow.
It's important to note that id represents any Objective-C object. And by Objective-C object, I mean one that is defined using #interface. It does not represent a struct or primitive type (int, char etc).
Also, you can only send messages (the [...] syntax) to Objective-C objects, so you cannot send the isKindOf: message to a normal struct or primitive.
But you can convert a integer etc to a NSNumber, a char* to a NSString and wrap a structure inside a NSObject-dervied class. Then they will be Objective-C objects.
in Objective-c I have this function prototype:
-(NSString*)formatSQL:(NSString*) sql, ...
I may pass to this function any type of parameters: NSString, NSNumber, integer, float
How can I determine in the function if a parameter is an object (NSString..) or a primitive (integer...)?
thanks
BrochPirate
If you're going to have a parameter that accepts multiple types, you can only safely do it by using Obj-C objects, which means using id as the type. You can't safely inter-mingle id with float, integer etc.
If you wrapped up all floats and ints in NSNumbers, you could have a method like so:
- (NSString *)formatSQL:(id)obj
{
if ([obj isKindOfClass:[NSString class]]) {
// Format as a string
}
else if ([obj isKindOfClass:[NSNumber class]]) {
// Further processing will be required to differentiate between ints and floats
}
}
There are a few caveats to using isKindOfClass:, but this should serve as a good starting point.