那些年,学swift踩过的坑

2017-08-01  本文已影响0人  早起的虫儿子被鸟吃

最近在学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 fileletnameSpace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"]as!String//拼接成固定格式letcontroller:AnyClass = NSClassFromString(nameSpace +"."+ controllerName)!//创建对象letviewController = (controlleras! 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: 用来表示任意类的类类型(元类型)

typealiasAnyClass=AnyObject.Type.Type用于获取类的元类型, 例如Person.Type就代表着获取Person的元类型  .self如果通过类名调用, 那么可以获取该类的类型, 说白了就是获取自己

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

在Swift中抓取异常需要do,catch,try这三个关键字

这里举个json序列化的例子

do{letpath =/..路径../letdata =/..转data..///编译器会要求你实行异常检测,于是在序列化前面添加try字段//外部包裹do,catch,显而易见出错自然会走catchletdicArr =tryNSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)        }catch{// 如果抛出异常就会来到catch}

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

由于swift没有宏,我们不能像oc那样去定义

直接在AppDelegate中写,反正哪里都可以用

用泛型传参

如何判断调试与发布状态呢?

在Build settings里找到Swift Compiler-custom Flags

在other swift flags 的Debug里添加两个字段

"-D"

"DEBUG"

代码中直接判断就行

funcHJSLog(message: T){    #ifDEBUGprint("\(message)")    #endif}

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

在swift中,单例有两种写法

一种是按照OC的思维去写

static var onceToken: dispatch_once_t =0staticvarinstance: NetworkTools?classfunc shareNetworkTools() -> NetworkTools    {        dispatch_once(&onceToken) { () ->Voidinprint("我被调用了")instance= NetworkTools()        }returninstance!    }

另一种就是swift的纯正写法

在swift中,let本身就只会创建一次,可以运用这个特性

let是线程安全的

staticletinstance:NetworkTools=NetworkTools()classfuncshareNetworkTools() ->NetworkTools{returninstance    }

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

一般我们不公开方法会在前面添加private

但是例如按钮点击方法,光是添加private是不够的

因为swift的方法调用是在编译时就决定了

而点击事件方法由于是来自于runloop中

编译器不会它一起编译进来,只有在运行时呼叫,这属于OC的调用方式

所以我们还需要再在方法前面加上@objc

//按钮点击handle@objcprivatefunccomposeClick(){    }`

8.在swift中如何懒加载

在swift中对懒加载有专门的关键字

///懒加载一个imageViewprivate lazy var icon:UIImageView= {        let imageV =UIImageView(image:UIImage(named:"visitordiscover_feed_image_smallicon"))returnimageV    }()

9.在swift中的协议(protocol)

在swift中定义协议也很简单

只需要在类前定义就行

@objcprotocolVisitorViewDelegate:NSObjectProtocol{//点击注册按钮optionalfuncvisitorViewDidRegisterBtnClick(visitView: VisitorView)//点击登录按钮optionalfuncvisitorViewDidLoginBtnClick(visitView:VisitorView)}

代理属性需要设定为weak,防止循环引用

weakvardelegate:VisitorViewDelegate?

在调用代理方法时,代理作为可选属性,已经帮我们预防代理不存在的可能

我们还需要借助可选属性来预防方法未实现

当然在确定实现的前提下可以解包

///注册handle@objcprivatefuncregisterClick(){delegate?.visitorViewDidRegisterBtnClick!(self)  }///登录handle@objcprivatefuncloginClick(){ delegate?.visitorViewDidLoginBtnClick?(self)  }

10.在swift中如何写分类

我刚从oc转过来就遇到了如何在swift中写分类的问题

swift中写分类很简单

extension就是swift中的分类

例如给UIBarbuttonItem添加分类

新建一个UIBarButtonItem+Extension.swift文件

importUIKitextensionUIBarButtonItem{    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 来在父类中强制抛出错误,以保证使用这些代码的开发者留意到他们必须在自己的子类中实现相关方法:

classMyClass{funcmethodMustBeImplementedInSubclass(){        fatalError("这个方法必须在子类中被重写")    }}classYourClass:MyClass{overridefuncmethodMustBeImplementedInSubclass(){print("YourClass 实现了该方法")    }}classTheirClass:MyClass{funcsomeOtherMethod(){    }}YourClass().methodMustBeImplementedInSubclass()// YourClass 实现了该方法TheirClass().methodMustBeImplementedInSubclass()// 这个方法必须在子类中被重写

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

requiredinit(coder:NSCoder) {fatalError("NSCoding not supported")}

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

在严谨的开发中会经常用到断言

前面一条介绍了fatalError来抛出错误

这条就来介绍一下guard与fatalError的配合使用达到断言的效果

guardletsafeValue = criticalValueelse{  fatalError("criticalValue cannot be nil here")}someNecessaryOperation(safeValue)

本来我认为if也可以达到这样的效果

ifletsafeValue = criticalValue {  someNecessaryOperation(safeValue)}else{  fatalError("criticalValue cannot be nil here")}

或者

ifcriticalValue ==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来避免循环引用:

__weaktypeof(self) weakSelf =self;

那么在swift中我们怎么办到这点呢?

很简单,看下面代码

//这里用gcd举例不好,毕竟系统的块不会造成循环引用,这里就勉强的学一下怎么改吧dispatch_async(dispatch_get_global_queue(0,0)) {[unownedself] () -> Voidinself.view//添加自己的代码}

只需要在闭包里加入[unowned self]即可

上一篇下一篇

猜你喜欢

热点阅读