How can I cast a '[Int]' too an 'UnsafeMutablePointer<Int32>?'? - objective-c

I have a variable called time_diff in an app I'm making. I need to pass it into an obj-c function that takes UnsafeMutablePointer?.
Swift:
var time_diff = [Int](repeating: 0, count: MAX_NODE)
Obj-C Function:
- (instancetype) initWithRawDataX:(float *)x Y:(float*)y pressure:(float *)p time_diff:(int *)time penColor:(UInt32)penColor penThickness:(NSUInteger)thickness startTime:(UInt64)start_at size:(int)size normalizer:(float)inputScale

See Manual Memory Management in Apple's docs: https://developer.apple.com/documentation/swift/swift_standard_library/manual_memory_management.
In this case, if the function is imported into Swift as having UnsafeMutablePointer<Int32> for the type of time_diff, you should declare the array in swift as [Int32] instead of [Int]. Then pass it as &time_diff (an inout argument).

Related

how to transfer objective-c language (`va_list`,`va_start `,`va_end ` ) to swift language?

This is my codes. they are objective-c language:
- (void)objcMethod:(NSString *)format, ...
{
va_list args;
va_start(args, format);
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
NSLog(#"%#", msg);
va_end(args);
va_start(args, format);
va_end(args);
}
how to transfer objective-c language (va_list,va_start,va_end ) to swift language?
I also need to call this swift method in objective-c xxx.m file.
Need help. thanks!
======================================================================
update:
I tried MartinR's answer NSLog is unavailable , but something wrong, I can not add #objc in front of the method, need help.thanks.
my codes...
From #MartinR comment and reference to NSLog is unavailable you know that Swift can call (Objective-)C functions & methods that take va_list arguments through the use of the Swift types CVarArg and CVaListPointer.
Many of the common (Objective-)C variadic functions & methods have a sibling which takes a va_list, so this support in Swift provides access to them.
I also need to call this swift method in objective-c xxx.m file.
However you wish to go the other way, and having written a Swift variadic function version of your Objective-C method you found you couldn't call it. You attempted to ask what the solution was, How do you call a Swift variadic method from Objective-C?, the indirect answer (your question was marked as duplicate) to that question provides a hint – to use an array – but doesn't handle the generality you require for your formatted-print type scenario. Let's see if we can get there...
(Using Xcode 10/Swift 4.2, any other version of Swift is probably different.)
We'll use the following Swift class as basis:
class SwiftLog : NSObject
{
// Swift entry point
static func Log(_ format : String, args : CVarArg...)
{
withVaList(args) { LogV(format, $0)}
}
// Shared core
private static func LogV(_ format : String, _ args: CVaListPointer)
{
NSLogv(format, args)
}
}
This provides Swift with a variadic function which will take all the Swift library types you are probably interested in, and a few more you not (3287 are listed in Apple's CVarArg documentation). The private core function here is trivial, you probably wish to do something a little more involved.
Now you wish to call Log() from Objective-C but, as you've discovered, you cannot due to the CVarArg. However Objective-C can call Swift functions which take NSObject arguments, and NSObject implements CVarArg, which gets us to our first attempt:
// Objective-C entry point
#objc static func Log(_ format : String, args : [NSObject])
{
withVaList(args) { LogV(format, $0) }
}
This works as-is but every argument must be an object and formatted with %#, switching to Objective-C:
[SwiftLog LogObjects:#"%#|%#|%#|%#|%#|%#" args:#[#"42", #4.2, #"hello", #31, #'c', NSDate.new]];
produces:
42|4.2|hello|31|99|Sun Nov 11 08:47:35 2018
It works within limits, we have lost the flexibility of formatting – no %6.2f, %x etc. – and the character has come out as 99.
Can we improve it? If you are prepared to sacrifice the ability to print NSNumber values as is, then yes. Over in Swift change the Log() function to:
#objc static func Log(_ format : String, args : [NSObject])
{
withVaList(args.map(toPrintfArg)) { LogV(format, $0) }
}
Skipping toPrintfArg for the moment (its just large and ugly) over in Objective-C we can call this version as:
[SwiftLog Log:#"%#|%4.2f|%10s|%x|%c|%#" args:#[#"42", #4.2, #((intptr_t)"hello"), #31, #'c', NSDate.new]];
which produces:
42|4.20| hello|1f|c|Sun Nov 11 08:47:35 2018
Much better, and the character is correct. So what does toPrintfArg do?
In the above we had to pass an array of objects to Swift, and to do that all the primitive values are wrapped as NSNumber objects.
In Objective-C an NSNumber object does not reveal much about what it wraps, the access methods (.doubleValue, .integerValue etc.) will convert whatever the wrapped value was into a value of the requested type and return it.
However NSNumber is "toll-free bridged" to the the Core Foundation types CFBoolean and CFNumber; the former of these is for booleans (obviously!) and the latter for all the other numeric types and, unlike NSNumber, provides a function that returns the type of the wrapped value so it can be unwrapped without conversion. Using this information we can extract the original (experts, yes, see below) values from the NSNumber objects, all those extracted value in Swift will all implement CVarArg, here goes:
private static func toPrintfArg(_ item : NSObject) -> CVarArg
{
if let anumber = item as? NSNumber
{
if type(of:anumber) == CFBoolean.self { return anumber.boolValue }
switch CFNumberGetType(anumber)
{
case CFNumberType.sInt8Type: return anumber.int8Value
case CFNumberType.sInt16Type: return anumber.int16Value
case CFNumberType.sInt32Type: return anumber.int32Value
case CFNumberType.sInt64Type: return anumber.int64Value
case CFNumberType.float32Type: return Float32(anumber.floatValue)
case CFNumberType.float64Type: return Float64(anumber.doubleValue)
case CFNumberType.charType: return CChar(anumber.int8Value)
case CFNumberType.shortType: return CShort(anumber.int16Value)
case CFNumberType.intType: return CInt(anumber.int32Value)
case CFNumberType.longType: return CLong(anumber.int64Value)
case CFNumberType.longLongType: return CLongLong(anumber.int64Value)
case CFNumberType.floatType: return anumber.floatValue
case CFNumberType.doubleType: return anumber.doubleValue
case CFNumberType.cfIndexType: return CFIndex(anumber.int64Value)
case CFNumberType.nsIntegerType: return NSInteger(anumber.int64Value)
case CFNumberType.cgFloatType: return CGFloat(anumber.doubleValue)
}
}
return item;
}
This function will unwrap (experts, yes, most, see below) NSNumber objects to the original value type while leaving all other objects as is to be formatted by %# (as shown by the NSString and NSDate objects in the example).
Hope that helps, at least more than it confuses! Notes for the curious/experts follow.
Notes & Caveats
Preserving C Pointers
In the above example the C string "hello" was passed by converting it to intptr_t, a C integer type the same size as a pointer, rather than as a pointer value. In this context this is fine, a va_list is essentially an untyped bob of bytes and the format tells NSLogv() what type to interpret the next bytes as, converting to intptr_t keeps the same bytes/bits and allows the pointer to be wrapped as an NSNumber.
However if you application needs to have an actual pointer on the Swift side you can instead wrap the C string as an NSValue:
[NSValue valueWithPointer:"hello"]
and unwrap it in toPrintfArg by adding:
if let ptr = (item as? NSValue)?.pointerValue
{
return ptr.bindMemory(to: Int8.self, capacity: 1)
}
This produces a value of type UnsafeMutablePointer<Int8>, which implements CVarArg (and as the latter the capacity is irrelevant).
Do You Always Get The Same Type Back?
If you wrap a C type as an NSNumber and then unwrap it as above, could the type change due to argument promotion (which means that integer types smaller than int get passed as int values, float values as double) in C? The answer is maybe but the required type for the CVarArg value is the promoted type so it should not make any difference in this context – the type of the unwrapped value with suit the expected format specifier.
What about NSDecimalNumber?
Well spotted, if you try to print an NSDecimalNumber, which is a subclass of NSNumber, the above toPrintfArg will unpack it as a double and you must use a floating-point format and not %#. Handling this is left as an exercise.

C array of ObjC objects as a Function argument

Whenever I am passing an argument of Objective C type A to my function then everything is alright:
- (void) f:(id<A>)argument
However, when it's an array of the same object type, Xcode requires that I add a strong attribute as:
- (void) f:(__strong id<A> [])argument
Any idea why is that?

Objective-C equivalent to Swift's "Any"

Is there any way that an Objective-C method could operate on primitive parameters without knowing their types? For instance,
-(Any)returnFirst:(Any)first notSecond:(Any)second {
return first;
}
This could then be used like:
int a = [self returnFirst:500 notSecond:1000];
char b = [self returnFirst:'a' notSecond:'b'];
There is no equivalent to Any in Objective-C. Your example returnFirst:notSecond: cannot even be declared in Objective-C.
In Swift 3, the id type in Objective-C now maps to the Any type in Swift, which describes a value of any type, whether a class, enum, struct, or any other Swift type.
Source: https://developer.apple.com/swift/blog/?id=39

CGContextRef/Objective C to CGContext/Swift confusion [duplicate]

I'm using an Objective-C class in my Swift project via a bridging header. The method signature looks something like this:
- (CFArrayRef)someMethod:(someType)someParameter;
I started by getting an instance of the class, calling the method, and storing the value:
var myInstance = MyClassWithThatMethod();
var cfArr = myInstance.someMethod(someValue);
Then try to get a value in the array:
var valueInArrayThatIWant = CFArrayGetValueAtIndex(cfArr, 0);
However I get the error Unmanaged<CFArray>' is not identical to 'CFArray'. What does Unmanaged<CFArray> even mean?
I looked through How to convert CFArray to Swift Array? but I don't need to convert the array to a swift array (however that would be nice). I just need to be able to get values from the array.
I have also tried the method of passing the CFArray into a function outlined in this answer:
func doSomeStuffOnArray(myArray: NSArray) {
}
However I get a similar error when using it:
doSomeStuffOnArray(cfArr); // Unmanaged<CFArray>' is not identical to 'NSArray'
I am using CFArray because I need to store an array of CGPathRef, which cannot be stored in NSArray.
So how am I supposed to use CFArray in Swift?
As explained in
Working with Core Foundation Types, there are two possible solutions when
you return a Core Foundation object from your own function that is imported in Swift:
Annotate the function with CF_RETURNS_RETAINED or CF_RETURNS_NOT_RETAINED.
In your case:
- (CFArrayRef)someMethod:(someType)someParameter CF_RETURNS_NOT_RETAINED;
Or convert the unmanaged object to a memory managed object with takeUnretainedValue() or takeRetainedValue() in Swift. In your case:
var cfArr = myInstance.someMethod(someValue).takeUnretainedValue()
An Unmanaged is a wrapper for an actual CF value. (Sort of like an optional.) It's there because ARC can't tell from looking at the declaration of someMethod: whether that method retains the value it returns.
You unwrap an Unmanaged by telling ARC what memory management policy to use for the value inside. If someMethod calls CFRetain on its return value:
let cfArr = myInstance.someMethod(someValue).takeRetainedValue()
If it doesn't:
let cfArr = myInstance.someMethod(someValue).takeUnretainedValue()
After you do that, cfArr is a CFArray, so you can use the bridging tricks from the other questions you linked to for accessing it like a Swift array.
If you own the code for someMethod you can change it a bit to not need this. There's a couple of options for that:
Annotate with CF_RETURNS_RETAINED or CF_RETURNS_NOT_RETAINED to tell the compiler what memory behavior is needed
Since it's an ObjC method, bridge to NSArray and return that--it'll automatically become an [AnyObject] array in Swift.

When I have use ARC in IOS project,how can I use void *?

LooK this method:
beginAnimations:context:
This is a method of class UIView. The context need parameter which is a type of void-pointer,and I need to send a UIImageView to context.
I get a warning,which says void* has been forbidden when I use ARC. So how can I send UIImageView to context except not use ARC.
The comments above provided the correct answer for this particular case (use the block-based animation methods) but in general if you have an API which takes a context void * and you'd like to pass an object, I find it best to convert your id to a CFTypeRef so you can get manual memory management semantics on the pointer (CFTypeRef is a typedef for void *), etc. Note however that this requires that the callback must be called in order to get your object released (i.e. converted back to ARC's built-in management).
Here's an example for some imaginary API I just dreamt up:
- (void) doSomethingWithObject: (id) object {
// use CFBridgingRetain() to turn object into a manually-retained CFTypeRef/void*
[someObject performBackgroundTaskWithTarget: self
selector: #selector(bgTask:)
context: CFBridgingRetain(object)];
}
- (void) bgTask: (void *) context
{
// use CFBridgingRelease() to turn our void*/CFTypeRef into an ARC-managed id
id object = CFBridgingRelease((CFTypeRef)context);
...
}