EXC_BAD_ACCESS exception when calling a Swift closure from C - ios

I'm trying to call a Swift closure from C.
The following piece of code shall represent what I'm currently working on.
First, in Swift, I initialize a static constant, the closure that is supposed to be called later.
This closure is then passed to a C function (api_set_callback_block) that stores the block pointer.
Some time after that, the C function api_trigger_block is called. This function should invoke the Swift closure. Instead of doing so, it always throws a runtime error: EXC_BAD_ACCESS when trying to access cb_block_cb() (also see below).
Usually, this should mean that something tries to access a previously stored variable was deallocated. However, I don't get how that could be the case as I was passing a static constant.
I double-checked that cb_block_cb is not NULL when accessing it.
void (^cb_block_cb)(int, int) = NULL;
void api_set_callback_block(void (^cb_block)(int, int))
{
if (cb_block == NULL)
{
puts("error: when setting callback block: cb_block is null");
return;
}
cb_block_cb = cb_block;
}
void api_trigger_block()
{
if (cb_block_cb == NULL)
{
puts("error: when triggering callback block: cb_block_cb is null");
return;
}
cb_block_cb(3,3); // <-- This is where the exception gets thrown
}
class CustomClass: NSObject {
public static let callback: (Int32, Int32) -> Swift.Void = { (cid, aid) in
print("Callback block called!")
}
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
api_set_callback_block(CustomClass.callback)
DispatchQueue.main.asyncAfter(deadline: .now() + 7) {
api_trigger_block()
}
return true
}
// ...
}

Thank you for the interesting question.
I see a few options.
Option 1:
If you have control over the C API, make (some part of) it Objective-C by just changing the extension from .c to .m. Then your Swift code should interoperate with it just fine. At least the part of it that directly interacts with Swift code should be made Objective-C. Objective-C portion will hopefully be interoperable with the remaining C parts (the ones in .c files).
Option 2:
Write an Objective-C wrapper (in a .m file) around the C API and use the wrapper in Swift code. Sample wrapper based on your example:
// This is where we store the block passed in from Swift
void (^cb_block_cb2)(int, int) = NULL;
// This wrapper will be used in the block passed to C API
void block_wrapper(int i1, int i2) {
cb_block_cb2(i1, i2);
}
// Obj-C wrapper around C API block setter
// Make this available to Swift, e.g. via bridging header.
void api_set_callback_block2(void (^cb_block)(int, int))
{
if (cb_block == NULL)
{
puts("error: when setting callback block: cb_block is null");
return;
}
cb_block_cb2 = cb_block;
api_set_callback_block(^(int a, int b){ block_wrapper(a, b);});
}
// Obj-C wrapper around C API block trigger
// Make this available to Swift, e.g. via bridging header.
void api_trigger_block2()
{
puts("Entered api_trigger_block2()");
if (cb_block_cb2 == NULL)
{
puts("error: when triggering callback block: cb_block_cb2 is null");
return;
}
api_trigger_block();
puts("Returned from callback in api_trigger_block2()!!!!");
}
Conceptually, the above options are similar. You may be able to come up with some other options along the same lines. At this point I can't give a good technical explanation as to why the above works. As far as I can tell from experimenting, Swift functions/closures can't always be passed to C functions compiled with a C compiler and taking compatible blocks. However, it works fine if the same functions are compiled as Objective-C. A block defined in an Objective-C file can be passed to C code just fine.
For the record, I also tried using #objc and #convention(c) annotations in Swift code trying to fix this, but to no avail.
Hope this helps.

Related

Calling block from Objective C collection in Swift

I have a Swift object that takes a dictionary of blocks (keyed by Strings), stores it and runs block under given key later at some point depending on external circumstances (think different behaviours depending on the backend response):
#objc func register(behaviors: [String: #convention(block) () -> Void] {
// ...
}
It's used in a mixed-language project, so it needs to be accessible from both Swift and Objective-C. That's why there's #convention(block), otherwise compiler would complain about not being able to represent this function in Objective-C.
It works fine in Swift. But when I try to invoke it from Objective-C like that:
[behaviorManager register:#{
#"default": ^{
// ...
}
}];
The code crashes and I get following error:
Could not cast value of type '__NSGlobalBlock__' (0x...) to '#convention(block) () -> ()' (0x...).
Why is that, what's going on? I thought #convention(block) is to specifically tell the compiler that Objective C blocks are going to be passed, and that's exactly what gets passed to the function in the call.
That's why there's #convention(block), otherwise compiler would
complain about not being able to represent this function in
Objective-C
For the sake of consistency: commonly you use #convention attribute the other way around - when there is an interface which takes a C-pointer (and implemented in C) or an Objective-C block (and implemented in Objective-C), and you pass a Swift closure with a corresponding #convention as an argument instead (so the compiler actually can generate appropriate memory layout out of the Swift closure for the C/Objective-C implementation). So it should work perfectly fine if it's Objective-C side where the Swift-created closures are called like blocks:
#interface TDWObject : NSObject
- (void)passArguments:(NSDictionary<NSString *, void(^)()> *)params;
#end
If the class is exposed to Swift the compiler then generates corresponding signature that takes a dictionary of #convention(block) values:
func passArguments(_ params: [String : #convention(block) () -> Void])
This, however, doesn't cancel the fact that closures with #convention attribute should still work in Swift, but the things get complicated when it comes to collections, and I assume it has something with value-type vs reference-type optimisation of Swift collections. To get it round, I'd propose to make it apparent that this collection holds a reference type, by promoting it to the [String: AnyObject] and casting later on to a corresponding block type:
#objc func takeClosures(_ closures: [String: AnyObject]) {
guard let block = closures["One"] else {
return // the block is missing
}
let closure = unsafeBitCast(block, to: ObjCBlock.self)
closure()
}
Alternatively, you may want to wrap your blocks inside of an Objective-C object, so Swift is well aware of that it's a reference type:
typedef void(^Block)();
#interface TDWBlockWrapper: NSObject
#property(nonatomic, readonly) Block block;
#end
#interface TDWBlockWrapper ()
- (instancetype)initWithBlock:(Block)block;
#end
#implementation TDWBlockWrapper
- (instancetype)initWithBlock:(Block)block {
if (self = [super init]) {
_block = block;
}
return self;
}
#end
Then for Swift it will work as simple as that:
#objc func takeBlockWrappers(_ wrappers: [String: TDWBlockWrapper]) {
guard let wrapper = wrappers["One"] else {
return // the block is missing
}
wrapper.block()
}

'OSSpinLock' was deprecated in iOS 10.0: Use os_unfair_lock() from <os/lock.h> instead

I went through this Question but the provided solution didn't work. Can someone please explain any alternative approach or proper implementation using os_unfair_lock()?
when I am using 'OS_UNFAIR_LOCK_INIT', it seems unavailable.
Thanks!
In iOS 16 (and macOS 13) and later, you should use OSAllocatedUnfairLock. As the documentation says:
it’s unsafe to use os_unfair_lock from Swift because it’s a value type and, therefore, doesn’t have a stable memory address. That means when you call os_unfair_lock_lock or os_unfair_lock_unlock and pass a lock object using the & operator, the system may lock or unlock the wrong object.
Instead, use OSAllocatedUnfairLock, which avoids that pitfall because it doesn’t function as a value type, despite being a structure. All copied instances of an OSAllocatedUnfairLock control the same underlying lock allocation.
So, if you have a counter that you want to interact with in a thread-safe manner:
import os.lock
let counter = OSAllocatedUnfairLock(initialState: 0)
...
counter.withLock { value in
value += 1
}
...
counter.withLock { value in
print(value)
}
For support of earlier OS versions, see my original answer below.
In Concurrent Programming With GCD in Swift 3, they warn us that we cannot use os_unfair_lock directly in Swift because “Swift assumes that anything that is struct can be moved, and that doesn't work with a mutex or with a lock.”
In that video, the speaker suggests that if you must use os_unfair_lock, that you put this in an Objective-C class (which won't move the struct). Or if you look at some of the stdlib code, they show you can stay in Swift, but use a UnsafeMutablePointer instead of the struct directly. (Thanks to bscothern for confirming this pattern.)
So, for example, you can write an UnfairLock class that avoids this problem:
final class UnfairLock: NSLocking {
private let unfairLock: UnsafeMutablePointer<os_unfair_lock> = {
let pointer = UnsafeMutablePointer<os_unfair_lock>.allocate(capacity: 1)
pointer.initialize(to: os_unfair_lock())
return pointer
}()
deinit {
unfairLock.deinitialize(count: 1)
unfairLock.deallocate()
}
func lock() {
os_unfair_lock_lock(unfairLock)
}
func tryLock() -> Bool {
os_unfair_lock_trylock(unfairLock)
}
func unlock() {
os_unfair_lock_unlock(unfairLock)
}
}
Then you can do things like:
let lock = UnfairLock()
And then use lock and unlock like you would with NSLock, but using the more efficient os_unfair_lock behind the scenes:
lock.lock()
// critical section here
lock.unlock()
And because this conforms to NSLocking, you can use extensions designed for that. E.g., here is a common method that we use to guarantee that our locks and unlocks are balanced:
extension NSLocking {
func synchronized<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
And
lock.synchronized {
// critical section here
}
But, bottom line, do not use os_unfair_lock from Swift without something like the above or as contemplated in that video, both of which provide a stable memory address for the lock.
You can use os_unfair_lock as below,
var unfairLock = os_unfair_lock_s()
os_unfair_lock_lock(&unfairLock)
os_unfair_lock_unlock(&unfairLock)

Swift: Passing callbacks to struct instance

I would like to do something along the lines of the pseudo code below:
struct Foo {
let BarInstance = Bar(Callback: CallBarInstance)
func CallBarInstance() -> Void {
BarInstance.FunctionToCall()
}
}
struct Bar {
var Callback: () -> Void
func FunctionToCall() -> Void {
print("Hello")
}
// More code that calls Callback
}
I get the error that I can't convert (Foo)->()->Void to ()->Void. I think I understand this is because the instance of Foo is being passed in as it is a member function. I figured it could then be made a static function that calls but getting access to the member variables felt hacky - is there a good way to get the functionality I want in Swift?
You seem to be trying to do something dangerous here, and Swift is stopping you from doing it.
In this line:
let BarInstance = Bar(Callback: CallBarInstance)
You are leaking an uninitialised self to Bar. Why? Because at the point in time when Bar.init is called, Foo is not be fully initialised. Namely, what is the value of BarInstance at this point? It is undefined. Yet you are trying to pass self.CallbarInstance to Bar.init!
Imagine what could happen if this were allowed. Bar.init had called the passed in function directly, before it returns. Now we have a very weird situation: CallBarInstance actually makes use of the value of BarInstance in its implementation, but what's the value of BarInstance? Bar.init hasn't returned so it's undefined!
The error message is a bit unclear though. Swift treats CallBarInstance as a (Foo) -> () -> Void in this situation (as if you were calling it as Foo.CallBarInstance), because self is unavailable.
You can kind of fix it by initialising BarInstance with some other value first, then assigning the intended Bar instance, but I don't know whether this will produce your intended behaviour or not.
struct Foo {
var BarInstance = Bar(Callback: {})
init() {
BarInstance = Bar(Callback: CallBarInstance)
}
func CallBarInstance() -> Void {
BarInstance.FunctionToCall()
}
}

How to disable logging?

I have an app that is making use of UITextChecker class provided by Apple. This class has a following bug: If the iOS device is offline (expected to be often for my app) each time I call some of UITextCheckers methods it logs following to console:
2016-03-08 23:48:02.119 HereIsMyAppName [4524:469877] UITextChecker sent string:isExemptFromTextCheckerWithCompletionHandler: to com.apple.TextInput.rdt but received error Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.TextInput.rdt was invalidated."
I do not want to have logs spammed by this messages. Is there a way to disable logging from code? I would disable logging before call to any of UITextCheckers methods and reenable it afterwards. Or is there perhaps any way how to disable logging selectively per class (event if it is foundation class not mine)? Or any other solution?
Warning: This answer uses the private-yet-sort-of-documented Cocoa C functions _NSSetLogCStringFunction() and _NSLogCStringFunction().
_NSSetLogCStringFunction() is an interface created by Apple to handle the implementation function for NSLog. It was initially exposed for WebObjects to hook into NSLog statements on Windows machines, but still exists in the iOS and OS X APIs today. It is documented in this support article.
The function takes in a function pointer with the arguments const char* message, the string to log, unsigned length, the length of the message, and a BOOL withSysLogBanner which toggles the standard logging banner. If we create our own hook function for logging that doesn't actually do anything (an empty implementation rather than calling fprintf like NSLog does behind-the-scenes), we can effectively disable all logging for your application.
Objective-C Example (or Swift with bridging header):
extern void _NSSetLogCStringFunction(void(*)(const char*, unsigned, BOOL));
static void hookFunc(const char* message, unsigned length, BOOL withSysLogBanner) { /* Empty */ }
// Later in your application
_NSSetLogCStringFunction(hookFunc);
NSLog(#"Hello _NSSetLogCStringFunction!\n\n"); // observe this isn't logged
An example implementation of this can be found in YILogHook, which provides an interface to add an array of blocks to any NSLog statement (write to file, etc).
Pure Swift Example:
#asmname("_NSSetLogCStringFunction") // NOTE: As of Swift 2.2 #asmname has been renamed to #_silgen_name
func _NSSetLogCStringFunction(_: ((UnsafePointer<Int8>, UInt32, Bool) -> Void)) -> Void
func hookFunc(message: UnsafePointer<Int8>, _ length: UInt32, _ withSysLogBanner: Bool) -> Void { /* Empty */ }
_NSSetLogCStringFunction(hookFunc)
NSLog("Hello _NSSetLogCStringFunction!\n\n"); // observe this isn't logged
In Swift, you can also chose to ignore all of the block parameters without using hookFunc like so:
_NSSetLogCStringFunction { _,_,_ in }
To turn logging back on using Objective-C, just pass in NULL as the function pointer:
_NSSetLogCStringFunction(NULL);
With Swift things are a little different, since the compiler will complain about a type mismatch if we try to pass in nil or a nil pointer (NULL is unavailable in Swift). To solve this, we need to access another system function, _NSLogCStringFunction, to get a pointer to the default logging implementation, retain that reference while logging is disabled, and set the reference back when we want to turn logging back on.
I've cleaned up the Swift implementation of this by adding a NSLogCStringFunc typedef:
/// Represents the C function signature used under-the-hood by NSLog
typealias NSLogCStringFunc = (UnsafePointer<Int8>, UInt32, Bool) -> Void
/// Sets the C function used by NSLog
#_silgen_name("_NSSetLogCStringFunction") // NOTE: As of Swift 2.2 #asmname has been renamed to #_silgen_name
func _NSSetLogCStringFunction(_: NSLogCStringFunc) -> Void
/// Retrieves the current C function used by NSLog
#_silgen_name("_NSLogCStringFunction")
func _NSLogCStringFunction() -> NSLogCStringFunc
let logFunc = _NSLogCStringFunction() // get function pointer to the system log function before we override it
_NSSetLogCStringFunction { (_, _, _) in } // set our own log function to do nothing in an anonymous closure
NSLog("Observe this isn't logged.");
_NSSetLogCStringFunction(logFunc) // switch back to the system log function
NSLog("Observe this is logged.")

Will this static var work like C?

I want to define a static variable on a class using Swift 2 that is a NSLock.
After researching I discovered that I have to use a struct, in something like this:
class Entity: NSManagedObject {
struct Mechanism {
static let lock = NSLock()
}
func myFunction -> NSArray {
Mechanism.lock.lock()
// do something
Mechanism.lock.unlock()
}
}
Will this work like C? I mean, the first time Mechanism is used a static lock constant will be created and subsequent calls will use the same constant?
I feel that this is not correct because the line
static let lock = NSLock()
is initializing a NSLock. So it will initialize a new one every time.
If this was not swift I would do like this:
static NSLock *lock;
if (!lock) {
lock = ... initialize
}
How do I do the equivalent in Swift 2?
You said that "after researching, I discovered that I have to use a struct [to get a static]." You then go on to ask whether it really is a static a how it changes in Swift 2.0.
So, a couple of observations:
Yes, this static within a struct pattern will achieve the desired behavior, that only one NSLock will be instantiated.
The significant change in the language was in Swift 1.2 (not 2.0), which now allows static variables, eliminating the need for the struct altogether:
class Entity: NSManagedObject {
static let lock = NSLock()
func myFunction() -> NSArray {
Entity.lock.lock()
// do something
Entity.lock.unlock()
}
}
Seriously, nobody uses NSLock on MacOS X or iOS. In Objective C, you use #synchronized. In Swift, you use a global function like this:
func Synchronized (obj: AnyObject, _ block: dispatch_block_t)
{
objc_sync_enter (obj)
block ()
objc_sync_exit (obj)
}
First, this uses a recursive lock. That alone will safe you gazillions of headaches. Second, it works much more fine grained, with a lock for one specific object. To use:
func myFunction() -> NSArray {
Synchronized(someObject) {
// Stuff to do.
}
}

Resources