Swift总的来说是一门比较容易编写的静态编译且略带一些动态特性的编程语言。由于Swift在2014年才刚诞生,因此当前在语法上修改的幅度比较大,尤其是从Swift 1.0到Swift 2.0;Swift 2.0到Swift 3.0。然而,此编程语言也逐步走入正轨,当前所有的改动都准对编程语言的稳定性、语法体系的完备性、API命名规则的一致性等问题进行展开,并且Swift从一开始就在保留对Objective-C以及C语言的相兼容性。我们在Swift中可以使用大量C语言形式的系统API,这些API中有不少包含了指针参数,因此这篇博文将给大家介绍在于C API进行交互时,Swift 2.2如何妥善处理指针的问题。


首先给大家的建议时,当我们现在用Swift时,如果有些算法或功能模块需要用C语言完成,那么涉及到函数回调的情况时请优先使用Blocks语法而不是函数指针。Blocks语法是Apple在LLVM开源项目中为Clang编译器提供的Lambda表达式,可用于纯C语言、C++、以及Objective-C/C++。由于Swift编程语言中的函数以及闭包是与Blocks无缝兼容的,此外Blocks与传统的函数调用比起来有许多强大、灵活的地方,想必各位在使用时已经能体会到这一点了,呼呼~

那么下面我们就说一下Swift在使用异步回调API时可能会发生的问题。在Apple官方的《Using Swift with Cocoa and Objective-C》一书中已经明确谈到——传递给函数的指针只有在函数调用期间才确保是有效的。当函数返回之后,不要设法去持有它并访问它。这句话很明确地表述了,在Swift中只有在调用函数的生命周期里,传递给它的指针对象才是有效的,当函数返回之后就无法保证该指针还有效。这里,笔者还曾给Apple提过一个bug(https://bugs.swift.org/browse/SR-1983),Apple本部的Swift开发工程师也是耐心地跟我解释,后来再次查阅《Using Swift with Cocoa and Objective-C》一书时,果然有这句话存在!所以通过以下这种函数去hold着指针对象是不可靠的:

public func getMutablePointer<T>(ptr: UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T> {
    return ptr
}

class ViewController: NSViewController {
        
    override func viewDidLoad() {
        super.viewDidLoad()
        
        var obj = 10
        let p = getMutablePointer(&obj)
        // 指针对象p在这个位置起就无法保证其有效性
        print("obj = \(p[0])")
    }
}

尽管一般情况下,上述代码能得到可预期的结果,但不建议这么使用。与此同时,我们可以看到,Swift中提供的UnsafePointer与UnsafeMutablePointer这两个类中也没有直接提供将一个对象直接转为一个指针的初始化器或其它类方法和成员方法。指针只能通过函数调用或方法调用的形式得到!


由此可知,我们在Swift中尽量使用更上层的API,如果在C语言层涉及到函数回调等情况,也尽量使用Blocks。下面我们将演示一段Swift中使用pthread以及Grand Central dispatch来做多线程并行计算的代码:

/**
 线程执行例程<br/>
 这里使用private仅仅表示该函数仅在此Swift文件内可见
 - parameters:
    - arg: 线程执行的环境参数
 - returns: 返回一个void*指针对象,这里将直接返回空
 */
private func threadProc(arg: UnsafeMutablePointer<Void>) -> UnsafeMutablePointer<Void> {
    
    // 这里arg是指向一个ViewController对象的指针
    if arg == nil {
        return nil
    }
    
    let obj = UnsafeMutablePointer<ViewController>(arg).memory
    obj.test()
    
    return nil
}

class ViewController: NSViewController {
    
    private func test() {
        print("This is a test!")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 由于self是常量对象,不能作为引用操作符&的操作数
        // 因此这里再定义一个var变量
        var ptr = self
        
        // 这里各位需要注意,由于这里想让threadID初始化为空,
        // 但它作为pthread_create的参数不被允许是optional的,
        // 因此这里笔者采用了直接上值为0的指针这种巧妙的手法来对它初始化为空
        var threadID: pthread_t = pthread_t(bitPattern: 0)
        
        // 尽管threadID它不是一个optional类型,但仍然可以与nil进行比较,
        // 并且这里确实比较结果为空!
        if threadID == nil {
            print("null threadID!")
        }
        
        // 这种方式不太可取
        pthread_create(&threadID,nil,threadProc,&ptr)
        
        // 下面直接使用Grand Central dispatch的方式做多线程计算,推荐使用!
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY,0)) {
            self.test()
        }
    }
}

这里需要提醒各位的是,在Swift中只有函数以及不捕获外部对象的闭包才能作为C函数指针的实参,类的成员方法、以及捕获外部对象的闭包都不能作为C函数指针的实参!

当然,如果各位想要更体面地使用pthread也不是没有办法,这时大家可以通过直接提供所要使用对象的类属性或类方法即可,然后在线程执行例程中进行调用。比如以下代码所示:

/**

线程执行例程<br/>

这里使用private仅仅表示该函数仅在此Swift文件内可见

- parameters:

- arg: 线程执行的环境参数

- returns: 返回一个void*指针对象,这里将直接返回空

*/

private func threadProc(arg: UnsafeMutablePointer<Void>) -> UnsafeMutablePointer<Void> {

guard let obj = ViewController.theInstance else {

return nil

}

obj.test()

return nil

}


class ViewController: NSViewController {

private func test() {

print("This is a test!")

}

/// 此单利仅在线程调度前被初始化

private static var theInstance: ViewController?

override func viewDidLoad() {

super.viewDidLoad()

// 这里各位需要注意,由于这里想让threadID初始化为空,

// 但它作为pthread_create的参数不被允许是optional的,

// 因此这里笔者采用了直接上值为0的指针这种巧妙的手法来对它初始化为空

var threadID: pthread_t = pthread_t(bitPattern: 0)

// 在做线程调度之前为theInstance进行初始化

ViewController.theInstance = self

pthread_create(&threadID,nil,threadProc,nil)

}

}

这么一来,我们就无需为所传递的指针实参是否有效而抓头皮了。

Swift中使用C API时传递指针注意事项的更多相关文章

  1. html5利用canvas实现颜色容差抠图功能

    这篇文章主要介绍了html5利用canvas实现颜色容差抠图功能,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

  2. three.js模拟实现太阳系行星体系功能

    这篇文章主要介绍了three.js模拟实现太阳系行星体系功能,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

  3. HTML5页面无缝闪开的问题及解决方案

    这篇文章主要介绍了HTML5页面无缝闪开方案,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. ios – 如何使用Objective C类中的多个参数调用Swift函数?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. ios – Swift 4添加手势:覆盖vs @objc

    我想在我的视图中添加一个手势,如下所示:但是,在Swift4中,我的编译器给出了以下错误:建议添加@objc以将此实例方法公开给Objective-C.实现此目的的另一个选项将覆盖touchesBegan()函数并使用它来处理点击.我试图以“Swift”的方式做到这一点,而不必带入Obj-C.有没有纯粹的Swift方式来添加这个轻击手势而不使用@objc?

  6. ios – 将视频分享到Facebook

    我正在编写一个简单的测试应用程序,用于将视频从iOS上传到Facebook.由于FacebookSDK的所有文档都在Objective-C中,因此我发现很难在线找到有关如何使用Swift执行此操作的示例/教程.到目前为止我有这个在我的UI上放置一个共享按钮,但它看起来已禁用,从我读到的这是因为没有内容设置,但我看不出这是怎么可能的.我的getVideoURL()函数返回一个NSURL,它肯定包含视

  7. ios – 以编程方式在Swift中添加联系人

    我想在Swift中以编程方式添加联系人.我发现了一些Objective-C示例,但我没有让它们工作,甚至在Objective-C中也没有.我不希望这涉及到AddressBookUI,因为我想从我自己的UI中获取值.解决方法这是在Swift中添加联系人的快速方法.我在我的iPhone5iOS7.1上验证了它,因为我发现模拟器并不总是与我的手机对AB的东西相同.您可以添加一个按钮并指向此方法:顺便说一下–它假设你已经分配了一个地址簿var,你可以通过覆盖viewDidAppear来打开视图.它也会执行安全提示

  8. iOS:核心图像和多线程应用程序

    我试图以最有效的方式运行一些核心图像过滤器.试图避免内存警告和崩溃,这是我在渲染大图像时得到的.我正在看Apple的核心图像编程指南.关于多线程,它说:“每个线程必须创建自己的CIFilter对象.否则,你的应用程序可能会出现意外行为.”这是什么意思?我实际上是试图在后台线程上运行我的过滤器,所以我可以在主线程上运行HUD(见下文).这在coreImage的上下文中是否有意义?

  9. ios – 多个NSPersistentStoreCoordinator实例可以连接到同一个底层SQLite持久性存储吗?

    我读过的关于在多个线程上使用CoreData的所有内容都讨论了使用共享单个NSPersistentStoreCoordinator的多个NSManagedobjectContext实例.这是理解的,我已经使它在一个应用程序中工作,该应用程序在主线程上使用CoreData来支持UI,并且具有可能需要一段时间才能运行的后台获取操作.问题是NSPersistentStoreCoordinator会对基础

  10. ios – XCode断点应该只挂起当前线程

    我需要调试多线程错误.因此,为了获得生成崩溃的条件,我需要在代码中的特定点停止一个线程,并等待另一个线程到达第二个断点.我现在遇到的问题是,如果一个线程遇到断点,则所有其他线程都被挂起.有没有办法只停止一个线程,让其他线程运行,直到它们到达第二个断点?)其他更有趣的选择:当你点击第一个断点时,你可以进入控制台并写入这应该在该断点处暂停当前上下文中的线程一小时.然后在Xcode中恢复执行.

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部