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 {
// ...
}
Related
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.
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.
As part of a unit test framework, I'm writing a function genArray that will generate NSArrays populated by a passed in generator block. So [ObjCheck genArray: genInt] would generate an NSArray of random integers, [ObjCheck genArray: genChar] would generate an NSArray of random characters, etc. In particular, I'm getting compiler errors in my implementation of genArray and genString, a wrapper around [ObjCheck genArray: genChar].
I believe Objective C can manipulate blocks this dynamically, but I don't have the syntax right.
ObjCheck.m
+ (id) genArray: (id) gen {
NSArray* arr = [NSMutableArray array];
int len = [self genInt] % 100;
int i;
for (i = 0; i < len; i++) {
id value = gen();
arr = [arr arrayByAddingObject: value];
}
return arr;
}
+ (id) genString {
NSString* s = #"";
char (^g)() = ^() {
return [ObjCheck genChar];
};
NSArray* arr = [self genArray: g];
s = [arr componentsJoinedByString: #""];
return s;
}
When I try to compile, gcc complains that it can't do gen(), because gen is not a function. This makes sense, since gen is indeed not a function but an id which must be cast to a function.
But when I rewrite the signatures to use id^() instead of id, I also get compiler errors. Can Objective C handle arbitrarily typed blocks (genArray needs this), or is that too dynamic?
Given that blocks are objects, you can cast between block types and id whenever you want, though if you cast the block to the wrong block type and call it, you're going to get unexpected results (since there's no way to dynamically check at runtime what the "real" type of the block is*).
BTW, id^() isn't a type. You're thinking of id(^)(). This may be a source of compiler error for you. You should be able to update +genArray: to use
id value = ((id(^)())(gen))();
Naturally, that's pretty ugly.
*There actually is a way, llvm inserts an obj-c type-encoded string representing the type of the block into the block's internal structure, but this is an implementation detail and would rely on you casting the block to its internal implementation structure in order to extract.
Blocks are a C-level feature, not an ObjC one - you work with them analogously to function pointers. There's an article with a very concise overview of the syntax. (And most everything else.)
In your example, I'd make the gen parameter an id (^gen)(). (Or possibly make it return a void*, using id would imply to me that gen generates ObjC objects and not completely arbitrary types.)
No matter how you declare your variables and parameters, your code won't work. There's a problem that runs through all your compiler errors and it would be a problem even if you weren't doing convoluted things with blocks.
You are trying to add chars to an NSArray. You can't do that. You will have to wrap them them as some kind of Objective C object. Since your only requirement for this example to work is that the objects can be inputs to componentsJoinedByString, you can return single-character NSStrings from g. Then some variety of signature like id^() will work for genArray. I'm not sure how you parenthesize it. Something like this:
+ (id) genArray: (id^()) gen;
+ (id) genString {
...
NSString * (^g)() = ^() {
return [NSString stringWithFormat:#"%c", [ObjCheck genChar]];
};
...
}
NSString * is an id. char is not. You can pass NSString * ^() to id ^(), but you get a compiler error when you try to pass a char ^() to an id ^(). If you gave up some generality of genArray and declared it to accept char ^(), it would compile your call to genArray, but would have an error within genArray when you tried to call arrayByAddingObject and the argument isn't typed as an id.
Somebody who understands the intricacies of block syntax feel free to edit my post if I got some subtle syntax errors.
Btw, use an NSMutableArray as your local variable in genArray. Calling arrayByAddingObject over and over again will have O(n^2) time performance I imagine. You can still declare the return type as NSArray, which is a superclass of NSMutableArray, and the callers of genArray won't know the difference.