那些年,学swift踩过的坑

最近在学swift,本以为多是语法与oc不同,而且都是使用相同的cocoa框架,相同的API,但是或多或少还是有些坑在里,为了避免以后再踩,在这里记下了,以后发现新的坑,也会慢慢在这里加上

[TOC]


1.main文件去哪儿了?

  • OC中main.m中的代码, 通过@UIApplicationMain标记自动生成
  • 可以注掉AppDelegate里的@UIApplicationMain,自己实现Main,不过一般没人这样做
  • 实现下面代码就是OC中的main文件的函数
UIApplicationMain(Process.argc, Process.unsafeArgv, nil, NSStringFromClass(AppDelegate))

2.如何通过字符串创建类对象?

  • 在swift中打印对象时,会发现在类型前面总会有命名空间 .+类名
  • 在swift中用字符串生成类对象就需要拼接成这样的格式,才能成功生成类
  • 注意,命名空间不要加特殊符号,不然依然无法获取控制器类
//获取命名空间,在info.plist文件里就是Executable file
let nameSpace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
//拼接成固定格式
let controller:AnyClass = NSClassFromString(nameSpace + "." + controllerName)!
//创建对象
let viewController = (controller as! UIViewController.Type).init()

3.Swift中的Any,AnyObject,AnyClass分别代表是什么?

  • AnyObject: 相当于OC中的id, 表示所有class类型的数据, 所有继承与NSObject的类都隐式实现了protocol AnyObject协议, 所以他可以表示所有的class类型
  • Any:所有基本数据类型和enum/ struct都可以用Any来表示

    注意: 有的时候你会发现将基本数据类型或者enum/ struct通过AnyObject来保存也不会报错, 这是因为Swift中很多数据类型可以和OC中的数据类型进行自动转换, 系统内部已经将他们转换为了OC的对象类型

  • AnyClass: 用来表示任意类的类类型(元类型)
      typealias AnyClass = AnyObject.Type
      .Type用于获取类的元类型, 例如Person.Type就代表着获取Person的元类型
      .self如果通过类名调用, 那么可以获取该类的类型, 说白了就是获取自己

4.在Swife中如何抓取异常?

  • 在Swift中抓取异常需要do,catch,try这三个关键字
  • 这里举个json序列化的例子
do{
            let path = /..路径../
            let data =/..转data../
            //编译器会要求你实行异常检测,于是在序列化前面添加try字段
            //外部包裹do,catch,显而易见出错自然会走catch
            let dicArr = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)

        }catch
        {
            // 如果抛出异常就会来到catch

        }

5.在swift中如何定义全局打印方法

  • 由于swift没有宏,我们不能像oc那样去定义
  • 直接在AppDelegate中写,反正哪里都可以用
  • 用泛型传参
  • 如何判断调试与发布状态呢?
  • 在Build settings里找到Swift Compiler-custom Flags
  • 在other swift flags 的Debug里添加两个字段
  • "-D"
  • "DEBUG"
  • 代码中直接判断就行
func HJSLog<T>(message: T)
{
    #if DEBUG
        print("\(message)")

    #endif
}

6.在swift中,单例怎么写?

  • 在swift中,单例有两种写法
  • 一种是按照OC的思维去写
     static var onceToken: dispatch_once_t = 0
    static var instance: NetworkTools?

    class func shareNetworkTools() -> NetworkTools
    {

        dispatch_once(&onceToken) { () -> Void in
            print("我被调用了")
            instance = NetworkTools()
        }

        return instance!
    }
  • 另一种就是swift的纯正写法
  • 在swift中,let本身就只会创建一次,可以运用这个特性
  • let是线程安全的
static let instance: NetworkTools = NetworkTools()
    class func shareNetworkTools() -> NetworkTools
    {
        return instance
    }

7.在swift中如何私有化点击事件方法

  • 一般我们不公开方法会在前面添加private
  • 但是例如按钮点击方法,光是添加private是不够的
  • 因为swift的方法调用是在编译时就决定了
  • 而点击事件方法由于是来自于runloop中
  • 编译器不会它一起编译进来,只有在运行时呼叫,这属于OC的调用方式
  • 所以我们还需要再在方法前面加上@objc
//按钮点击handle
    @objc private func composeClick(){

    }`

8.在swift中如何懒加载

  • 在swift中对懒加载有专门的关键字
///懒加载一个imageView
private lazy var icon:UIImageView = {
        let imageV = UIImageView(image: UIImage(named: "visitordiscover_feed_image_smallicon"))
        return imageV
    }()

9.在swift中的协议(protocol)

  • 在swift中定义协议也很简单
  • 只需要在类前定义就行
    @objc
    protocol VisitorViewDelegate:NSObjectProtocol
    {
      //点击注册按钮
      optional func visitorViewDidRegisterBtnClick(visitView: VisitorView)
      //点击登录按钮
      optional func visitorViewDidLoginBtnClick(visitView:VisitorView)
    }
  • 代理属性需要设定为weak,防止循环引用
    weak var delegate:VisitorViewDelegate?
  • 在调用代理方法时,代理作为可选属性,已经帮我们预防代理不存在的可能
  • 我们还需要借助可选属性来预防方法未实现
  • 当然在确定实现的前提下可以解包
    ///注册handle
      @objc private func registerClick(){
    delegate?.visitorViewDidRegisterBtnClick!(self)
      }
      ///登录handle
      @objc private func loginClick(){ delegate?.visitorViewDidLoginBtnClick?(self)
      }

10.在swift中如何写分类

  • 我刚从oc转过来就遇到了如何在swift中写分类的问题
  • swift中写分类很简单
  • extension就是swift中的分类
  • 例如给UIBarbuttonItem添加分类
  • 新建一个UIBarButtonItem+Extension.swift文件
import UIKit

extension UIBarButtonItem
{
    convenience init(target:AnyObject?,action:Selector,image:String) {

        let btn = UIButton(type: UIButtonType.Custom)
        btn.setImage(UIImage(named: image), forState: UIControlState.Normal)
        btn.setImage(UIImage(named: image + "_highlighted"), forState: UIControlState.Highlighted)

        btn.addTarget(target, action: action, forControlEvents: UIControlEvents.TouchUpInside)

        self.init(customView:btn)
    }
}

11.为何经常被强制实现init(coder: NSCoder)

  • 因为Objective-C 和 Swift 中都没有直接的这样的抽象函数语法支持
  • 然而有些时候我们却有不想让别人调用某个方法,但又不得不将其暴露出来的时候。
  • 一般满足这种需求的就是抽象类型或者抽象函数
  • 在面对这种情况时,为了确保子类实现这些方法,而父类中的方法不被错误地调用,我们就可以利用 fatalError 来在父类中强制抛出错误,以保证使用这些代码的开发者留意到他们必须在自己的子类中实现相关方法:
class MyClass {
    func methodMustBeImplementedInSubclass() {
        fatalError("这个方法必须在子类中被重写")
    }
}

class YourClass: MyClass {
    override func methodMustBeImplementedInSubclass() {
        print("YourClass 实现了该方法")
    }
}

class TheirClass: MyClass {
    func someOtherMethod() {

    }
}

YourClass().methodMustBeImplementedInSubclass()
// YourClass 实现了该方法

TheirClass().methodMustBeImplementedInSubclass()
// 这个方法必须在子类中被重写
  • 不仅仅是对于类似抽象函数的使用中可以选择 fatalError,对于其他一切我们不希望别人随意调用,但是又不得不去实现的方法,我们都应该使用 fatalError 来避免任何可能的误会。比如父类标明了某个 init 方法是 required 的,但是你的子类永远不会使用这个方法来初始化时,就可以采用类似的方式, 被广泛使用 (以及被广泛讨厌的) init(coder: NSCoder) 就是一个例子。在子类中,我们往往会写
required init(coder: NSCoder) {
  fatalError("NSCoding not supported")
}

12.在swift中使用guard与fatalError配合抛出异常

  • 在严谨的开发中会经常用到断言
  • 前面一条介绍了fatalError来抛出错误
  • 这条就来介绍一下guardfatalError的配合使用达到断言的效果
    guard let safeValue = criticalValue else {
      fatalError("criticalValue cannot be nil here")
    }
    someNecessaryOperation(safeValue)
  • 本来我认为if也可以达到这样的效果
    if let safeValue = criticalValue {
      someNecessaryOperation(safeValue)
    } else {
      fatalError("criticalValue cannot be nil here")
    }
  • 或者
    if criticalValue == nil {
      fatalError("criticalValue cannot be nil here")
    }
    someNecessaryOperation(criticalValue!)
  • 但是看到有些博客这么说:

    这个flatten code以其他方式进入一个if let 代码块,并且在靠近相关的环境中过早地退出了,而不是进入else代码块。甚 至当你没有捕获一个值(guard let),这个模式在编译期间也会强制过早退出。在第二个if的例子里,尽管代码flattend得像guard一样,但是一个毁灭性的错误或者其他返回 一些无法退出的进程(或者基于确切实例的非法态)将会导致crash。一个过早的退出发生时,guard声明将会及时发现错误,并将其从else block中移除。(这博主翻译得真烂)

  • 所以,还是用guard比较好

    13.在swift中,互斥锁变成什么样了?

  • 在swift中,互斥锁如何写
  • oc中的互斥锁:
@synchronized(self) {
    //需要执行的代码块
}
  • swift中的互斥锁
objc_sync_enter(self)
//需要执行的代码块
objc_sync_exit(self)
  • 至于其他多线程的API和以前的一样,只是少了perform这一类的API,苹果已经去掉了

14.在swift中,引用self时,如何避免循环引用

  • 在oc中,我们需要在代码块用到self时,可以直接把self付给其他变量,然后在块中使用完毕后制空,或者像下面弱引用self来避免循环引用:

    __weak typeof(self) weakSelf = self;
  • 那么在swift中我们怎么办到这点呢?
  • 很简单,看下面代码
    //这里用gcd举例不好,毕竟系统的块不会造成循环引用,这里就勉强的学一下怎么改吧
    dispatch_async(dispatch_get_global_queue(0, 0)) {[unowned self] () -> Void in
              self.view
              //添加自己的代码
          }
  • 只需要在闭包里加入[unowned self]即可

文/哟_Json(简书作者)
原文链接:http://www.jianshu.com/p/b5c87824e33c
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

时间: 2024-10-20 19:04:49

那些年,学swift踩过的坑的相关文章

初学spring boot踩过的坑

一.搭建spring boot环境 maven工程 pom文件内容 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-

python抓取360百科踩过的坑!

学习python一周,学着写了一个爬虫,用来抓取360百科的词条,在这个过程中.因为一个小小的修改,程序出现一些问题,又花了几天时间研究,问了各路高手,都没解决,终于还是自己攻克了,事实上就是对list列表理解不够深入导致的.这个bug非常有借鉴意义,分享出现. 先看看终于抓取出的结果: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gr

《C++之那些年踩过的坑(附录一)》

C++之那些年踩过的坑(附录一) 作者:刘俊延(Alinshans) 本系列文章针对我在写C++代码的过程中,尤其是做自己的项目时,踩过的各种坑.以此作为给自己的警惕. [版权声明]转载请注明原文来自:http://www.cnblogs.com/GodA/p/6639526.html 本来上个月就开始动笔了,直到现在才发出来,实在太多事情.可能有些小朋友不知道写这一篇随笔的起因,那么你可以看一下我之前写的. 上一篇的最后,我提到了一个问题:代码优化.并留了一个小测试:无符号数与有符号数的性能比

【转载】Fragment 全解析(1):那些年踩过的坑

http://www.jianshu.com/p/d9143a92ad94 Fragment系列文章:1.Fragment全解析系列(一):那些年踩过的坑2.Fragment全解析系列(二):正确的使用姿势3.Fragment之我的解决方案:Fragmentation 本篇主要介绍一些最常见的Fragment的坑以及官方Fragment库的那些自身的BUG,这些BUG在你深度使用时会遇到,比如Fragment嵌套时或者单Activity+多Fragment架构时遇到的坑.如果想看较为实用的技巧,

用vue-cli搭建项目踩过的坑

1. 需要额外安装sass-loader: 我以为vue-cli会安装所有与css相关的loader,然并非,为这个死了很多脑细胞. npm install sass-loader node-sass webpack --save-dev 2. 修改style标签: 打开src目录下的components目录中的App.vue文件.然后修改 style标签如下: <style lang="sass" rel="stylesheet/sass"> 3. s

sqlalchemy 踩过的坑

记录下Sqlalchemy遇到的问题,不定时更新. 设置主键为非自增 sqlalchemy 在sql server中默认主键是自增的,如果在数据库设置的主键不是自增的,这个时候插入就会出现异常: 提示does not have the identity property 这个时候需要在主键中设置autoincrement=False,显示表示非自增,才能正常写入 sqlalchemy 踩过的坑

从零开始学Swift之Hello World进化版

上节课,也就是昨晚啦,我们学习到从零开始学Swift之Hello World,那一节只有一句代码,大家会觉得不够过瘾.那么这节课,就给大家来多点瘾货吧! 先上图! //var 代表变量的类型, start 代表变量的名称, "Hello" 代表变量的值. var start = "Hello" var dot = " , " var end = "world!" // let 是什么意思呢? let total = start

springmvc上传文件 踩过的坑

spring-root.xml中配置 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="9000000"/> <property name="maxInM

多线程和异步编程示例和实践-踩过的坑

上两篇文章,主要介绍了Thread.ThreadPool和TPL 多线程异步编程示例和实践-Thread和ThreadPool 多线程异步编程示例和实践-Task 本文中,分享两则我们在做多线程和异步编程中实际踩过的坑,实际生产环境遇到的问题,以及解决办法. 1. HttpClient 业务场景:使用HttpClient实现第三方业务推送,当第三方的Http服务器不通.或者返回很慢时 线程数暴涨 Asp.Net\Asp.Net MVC场景下,并发多线程导致的线程阻塞:HttpClient.Pos