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.")
Related
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)
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.
Consider these lines:
I create a NSButton based class with this:
typealias onClickHandler = (NSTextfieldSuper)->Void
var onClick: onClickHandler?
When the user clicks on an instance of that button, I do this:
if (self.onClick != nil) {
onClick?(self)
}
I use that button later, from another class, with this:
let button = SuperButton()
button.onClick = { (textField: NSTextfieldSuper)->Void in
}
I am not sure if this is the correct syntax. I would like to process the button sent from the first closure on the parent class, where the button is created.
This was the only form I was able to type this without Xcode complaining. If this is correct, what is the purpose of this ->Void there? What could this possibly returning?
I just want to process that button sent.
By the way, as a bonus, I have to initialize several buttons with this, all running the same function. It would be nice to do something like
func doSomething () {
}
and then
let button = SuperButton()
button.onClick = doSomething
any ideas?
This was the only form I was able to type this without Xcode complaining. If this is correct, what is the purpose of this ->Void there? What could this possibly returning?
It is the same as in your typealias, in Swift a function type has the form:
(parameter definitions) -> return type
and functions which return nothing have a return type of Void (similar to C). The full form off a closure expression is:
{ (parameter definitions) ->return typeinbody}
Without any inference this expression provides the full type of the closure, and the -> Void Return type in your example specifies that your closure returns nothing. In your assignment this full type will be checked at compile time to conform to the type of onClick.
Now Swift will infer lots of stuff and there are various shorthands available for closure expressions, you will find that Swift accepts:
button.onClick = { textField in }
as well here with both the argument and return types of the closure being inferred.
By the way, as a bonus, [...] any ideas?
Just make the types match:
func doSomething(textField : NSTextfieldSuper) { }
button.onClick = doSomething
Unlike in (Objective-)C functions and closures (blocks in C) are interchangeable (as they are in plenty of other languages, C is the oddfellow here)
HTH
According to Apple Documentation:
When calling a function that takes a function pointer argument, you can pass a top-level Swift function, a closure literal, or nil.
I've tried this example from Apple Documentation: Apple Developer
func customCopyDescription(_ p: UnsafeRawPointer?) -> Unmanaged<CFString>? {
// return an Unmanaged<CFString>? value
}
var callbacks = CFArrayCallBacks(
version: 0,
retain: nil,
release: nil,
copyDescription: customCopyDescription,
equal: { (p1, p2) -> DarwinBoolean in
// return Bool value
}
)
but I got an error message in Xcode: (copyDescription: customCopyDescription error)
A C function pointer can only be formed from a reference to a 'func' or a literal closure
As mentioned at apple documentation , customCopyDescription can be passed as a top-level swift function, but it's seems like something is wrong in the documentation.
How to pass the customCopyDescription func to the CFArrayCallBacks as a swift function(not a closure literal)?
customCopyDescription needs to be a free function, not a method. When I copied your code into Xcode I got the error message only when customCopyDescription was inside a class, not otherwise.
Once placeholder return values are added and customCopyDescription is placed at file scope, the code compiles without a problem
I'm trying to convert a lua bridge from Swift 2 to Swift 3. I am not the original author so there are aspects of the library I don't know very well and the original author seems not interested to continue working on the project. I have most of the conversion done but there remain one place I'm stuck and could not figure out. I've tried searching on SO and on the Internet but could not find anything that could help me solve the problem.
If anyone is interested in looking at the full source code, here is my fork of the project on github: https://github.com/weyhan/lua4swift (My changes is in the Swift3 branch)
Allow me setup the context to the error I'm stuck on. There is a Userdata class, specifically in the method userdataPointer<T>() -> UnsafeMutablePointer<T> the c function lua_touserdata returns the block address of userdata as a void * pointer type.
Original code written in Swift 2:
public class Userdata: StoredValue {
public func userdataPointer<T>() -> UnsafeMutablePointer<T> {
push(vm)
let ptr = lua_touserdata(vm.vm, -1)
vm.pop()
return UnsafeMutablePointer<T>(ptr)
}
public func toCustomType<T: CustomTypeInstance>() -> T {
return userdataPointer().memory
}
public func toAny() -> Any {
return userdataPointer().memory
}
override public func kind() -> Kind { return .Userdata }
}
After the conversion with Xcode 8 migration tool, Xcode is complaining about the return line with error Cannot invoke initializer for type 'UnsafeMutablePointer<T>' with an argument list of type '(UnsafeMutableRawPointer?)':
return UnsafeMutablePointer<T>(ptr)
I've fixed it with:
return (ptr?.assumingMemoryBound(to: T.self))!
Following that above change, now Xcode 8 is now complaining about the calling statement in createCustomType:
public func createCustomType<T: CustomTypeInstance>(setup: (CustomType<T>) -> Void) -> CustomType<T> {
lua_createtable(vm, 0, 0)
let lib = CustomType<T>(self)
pop()
setup(lib)
registry[T.luaTypeName()] = lib
lib.becomeMetatableFor(lib)
lib["__index"] = lib
lib["__name"] = T.luaTypeName()
let gc = lib.gc
lib["__gc"] = createFunction([CustomType<T>.arg]) { args in
let ud = args.userdata
// ******* Here's the line that is causing problem in Swift 3
(ud.userdataPointer() as UnsafeMutablePointer<Void>).destroy()
// *******
let o: T = ud.toCustomType()
gc?(o)
return .Nothing
}
if let eq = lib.eq {
lib["__eq"] = createFunction([CustomType<T>.arg, CustomType<T>.arg]) { args in
let a: T = args.customType()
let b: T = args.customType()
return .Value(eq(a, b))
}
}
return lib
}
Where I'm getting stuck is the line :
(ud.userdataPointer() as UnsafeMutablePointer<Void>).destroy()
I believe the original author is attempting to clear the memory block where the pointer returned by userdataPointer() call is pointing to.
With the Xcode 8 auto migration tool the above line is converted as below:
(ud.userdataPointer() as UnsafeMutableRawPointer).deinitialize()
However Xcode now is then complains that Cannot convert call result type 'UnsafeMutablePointer<_>' to expected type 'UnsafeMutableRawPointer'.
From my research, the change to the return line in userdataPointer seems correct, so I think the issue is with the cast to UnsafeMutableRawPointer. I've tried dropping the cast to UnsafeMutableRawPointer and invoke ud.userdataPointer().deinitialize() directly but I get this error Generic parameter 'T' could not be inferred.
Other things I've tried is to convert the UnsafeMutablePointer to UnsafeMutableRawPointer but It always result in Xcode 8 complaining one thing or another. Any suggestion on how to get this to work?
As you may have already found out, Swift 3 attempts to provide better type safety when it comes to pointers. UnsafeMutablePointer can now only represent a pointer to an instance of a known type. In Swift 2, a C void * was represented by UnsafeMutablePointer<Void>, allowing void and non-void pointers to be treated in the same way, including trying to call a de-initializer of the pointed-to type, which is what the destroy() method in the problematic line of code does:
(ud.userdataPointer() as UnsafeMutablePointer<Void>).destroy()
In Swift 3 the de-initializer on the pointee is called using the deinitialize() method of the UnsafeMutablePointer structure. It appears that the migration assistant got confused. The line
(ud.userdataPointer() as UnsafeMutableRawPointer).deinitialize()
makes little sense because (1) UnsafeMutablePointer cannot be converted using as to UnsafeMutableRawPointer;
(2) UnsafeMutableRawPointer has not deinitialize() method. In Swift 3, UnsafeMutableRawPointer is a special type to represent void*. It is actually quite understandable why the migration tool made this mistake: it blindly replaced destroy() with deinitialize() and UnsafeMutablePointer<Void> with the corresponding Swift 3 type UnsafeMutableRawPointer, without realizing that the conversion would not work.
I don't quite understand why calling destroy() on a void pointer would work in Swift 2. Maybe this was a bug in the program or some compiler trick allowed the correct de-initializer to be called. Without knowing enough about the code, I can't be more specific than to suggest analyzing it to figure out what is the type pointed to by that pointer on which destroy() was called. For example, if we know for sure that it is always the placeholder type T used on the following line:
let o: T = ud.toCustomType()
then the offending line simply becomes
(ud.userdataPointer() as UnsafeMutablePointer<T>).deinitialize()
We need the conversion in the parentheses to allow the compiler to infer the generic parameter.
Thank you for bringing up an interesting problem. BTW, once you get over this obstacle, you are likely to run into other problems. One thing that jumps out is that UnsafeMutablePointer has no .memory in Swift 3; you'll have to use .pointee instead.
Here's an update. After playing with Swift 2.2 on Linux, I realize that calling destroy() on an UnsafeMutablePointer<A> cast as UnsafeMutablePointer<Void> won't call A's deinitializer, even if it has one. So, you have to be careful with that line...
Try creating an instance of UnsafeMutableRawPointer instead of trying to cast it:
UnsafeMutableRawPointer<T>(ud.userdataPointer()).destroy()