iOS开发培训Swift学习技巧.docx

上传人:b****9 文档编号:26195934 上传时间:2023-06-17 格式:DOCX 页数:13 大小:261.51KB
下载 相关 举报
iOS开发培训Swift学习技巧.docx_第1页
第1页 / 共13页
iOS开发培训Swift学习技巧.docx_第2页
第2页 / 共13页
iOS开发培训Swift学习技巧.docx_第3页
第3页 / 共13页
iOS开发培训Swift学习技巧.docx_第4页
第4页 / 共13页
iOS开发培训Swift学习技巧.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

iOS开发培训Swift学习技巧.docx

《iOS开发培训Swift学习技巧.docx》由会员分享,可在线阅读,更多相关《iOS开发培训Swift学习技巧.docx(13页珍藏版)》请在冰豆网上搜索。

iOS开发培训Swift学习技巧.docx

iOS开发培训Swift学习技巧

相对于Objective-C,Swift是一种编译代码时速度更快、安全性与可靠性更高、同时具有可预测性的语言。

下面iOS开发培训列出了在实践中使用这种新语言时,所获取一些Swift使用技巧。

这些技巧有助于让开发者编写出更干净的代码,并能帮助更熟悉Objective-C的程序员适应Swift编程,同时适用于在Swift上具有各种背景经历的人,请继续往下看。

iOS开发培训这篇的章节顺序是按照使用者对Swift的熟悉程度来排列的。

第一部分是针对不太了解Swift的人,第二部分是针对初级入门者,而最后一部分是对于已在使用Swift的人。

你应当了解,但有可能不知道的Swift技巧

提高常数的可读性

在Swift中使用struct的简洁办法,就是在应用中制作一个适用所有常数的文件。

由于Swift允许我们嵌用下面的结构,这种办法非常有用:

importFoundation

structConstants{

structFoursquareApi{

staticletBaseUrl="

}

structTwitterApi{

staticletBaseUrl="

}

structConfiguration{

staticletUseWorkaround=true

}

}

嵌套让我们可以为常数生成一个命名空间(namespace)。

例如:

我们可以使用Constants.FoursquareApi.BaseUrl来访问Foursquare的BaseUrl常数,这样会使得数据可读性更高,并为相关的常数提供一系列封装。

为了提高性能,要避免NSObject与@objc

Swift允许我们将分类进行扩展,从NSObject到获取对象的Objective-Cruntime系统功能。

还允许我们用@objc来注释Swift方法,以便在Objective-Cruntime中使用。

支持Objective-Cruntime,代表着系统不再通过通过静态或vtable分配,而是动态分配来调用方法。

结果就是:

在调用支持Objective-C运行的方法时,性能损失会高达四倍。

在实际应用中,这种情况对性能的影响也许微不足道,不过这样一来,我们就知道通过Swift执行方法调用要比使用Objective-C快四倍。

在Swift中使用方法调配(MethodSwizzling)

方法调配是替换一个已存在的方法实现。

如果对此不熟悉,可以阅读这篇文章。

Swift优化后,不再像Objective-C中那样,在runtime寻找方法的位置,而是直接调用内存地址。

因此默认情况下,在Swift类中调配无法起效,除非:

∙用动态关键字禁用这种优化。

这是最佳选择,如果数据库完全以Swift构建的话,这种选择也是最合理的方式。

∙扩展NSObject。

如果单纯为了方法调配的话,不要用这种方式(而要采用动态的)。

需要了解:

在将NSObject作为基础类的已存在类中,方法调配是有效的,不过最好使用动态选择的方法。

∙在要调配的方法中使用@objc注释。

如果我们想要调配的方法同时也需要使用Objective-C的代码,那么这种方法是最合适的。

引用

更新:

根据要求,我们增加了一个完全使用Swift的调用样例。

在这个样例中仍需要Objective-Cruntime,不过类并非继承自NSObject,方法也未标记成@objc。

importUIKit

classAwesomeClass{

dynamicfuncoriginalFunction()->String{

return"originalFunction"

}

dynamicfuncswizzledFunction()->String{

return"swizzledFunction"

}

}

letawesomeObject=AwesomeClass()

print(awesomeObject.originalFunction())//prints:

"originalFunction"

letaClass=AwesomeClass.self

letoriginalMethod=class_getInstanceMethod(aClass,"originalFunction")

letswizzledMethod=class_getInstanceMethod(aClass,"swizzledFunction")

method_exchangeImplementations(originalMethod,swizzledMethod)

print(awesomeObject.originalFunction())//prints:

"swizzledFunction"

入门者所需的Swift技巧

清理异步代码

Swift在编写补齐函数(completionfunction)上语法非常简洁。

在Objective-C中有completionblock,不过出现的很晚,语法也有些粗糙,如下:

[selfloginViaHttpWithRequest:

requestcompletionBlockWithSuccess:

^(LoginOperation*operation,idresponseObject){

[selfshowMainScreen];

}failure:

^(LoginOperation*operation,NSError*error){

[selfshowFailedLogin];

}];

在Swift中有一种更简单的新型闭包语法。

任何将闭包作为末尾参数的方法都可以使用Swift的新语法,让回调更简洁,如下:

loginViaHttp(request){responsein

ifresponse.success{

showMainScreen()

}else{

showFailedLogin()

}

}

控制对代码的访问

应该坚持用合适的访问控制修饰符(accesscontrolmodifier)来封装代码。

如果封装的好,无需记下思维过程,也无需询问代码编写者,就能理解这段代码是如何交互的。

Swift常见的访问控制机制有三种:

私人访问、内部访问和公共访问。

不过Swift中并没有常见于其它面向对象语言中的protected访问控制修饰符。

为什么会这样呢?

那是因为在子类中通过新的公共方法或属性,就可以显示protected方法或属性,因此实际上保护是无效的。

而且由于从任何地方都能重写,因此protected并未给Swift编译器开启优化的机会。

最后,由于protected阻止子类helper访问子类能够访问的信息,会让封装变差。

想要了解Swift团队关于protected更多的想法,请点击这里查看。

实地实验与验证

Playground是苹果在2014年随Swift一起推出的一款交互式编程工具,可以用来测试及验证想法、学习Swift、与其他人分享概念。

无需创建新项目,只需在运行Xcode的时候将playground选中就可以了。

也可以在Xcode中创建新的playground:

一旦有了playground,在编程时便能实时看到结果:

通过Playground可以将想法原型化,并以代码形式展示,同时还不会造成开启新项目的额外开销。

安全地使用可选值

可选值(optional)属性指的是这个属性或有效值或无值(为空)。

通过可选值的名称+感叹号,格式为optionalProperty!

,便可隐式解开一个可选值。

一般这是需要避免的,因为感叹号暗示着“危险”。

不过有些情况下,隐式解开可选值是可以接受的。

比如IBOutlets就是默认将可选值隐式解开的(在InterfaceBuilder中点击拖拽时),因为UIKit假定我们是将对象接口(outlet)与IB连接起来的。

IBOutlets在初始化之后已经设置好了,因此接口是可选值的,同时根据Swift规则,在初始化之后所有非可选值的属性必须有值。

另一个通过名称获得UIImage的案例是存在于我们的assetcatalog之中的:

letimageViewSavvyNewYearsParty=UIImageView(image:

UIImage(named:

"Savvy2016.png")!

将默认值设置为常量属性,在不隐式打开可选值的情况下是无法做到的。

也就是说,!

仍旧代表“危险!

”但在这种情况下,是告知我们需要当心错误,并在运行前验证名称是否相符。

一般来讲,假如我们必须使用空值,app就会有崩溃的风险。

用!

来隐式打开值会让编译器知道,我们已经知道在运行时可选值不会为空。

在几乎所有场景之中,这都是带有赌博性质的,因此最好使用iflet模式来确定可选值是有有效值还是为空:

ifletname=user.name{

print(name)

}else{

print("404NameNotFound")

}

抛弃数字对象(NSNumber)

Objective-C使用Cprimitives来代表数字,用FoundationObjective-CAPI来提供数字对象类型,将primitives装箱拆箱。

需要在primitives与对象类型之间切换时,代码会像[arrayaddObject:

@(intPrimitive)]和[array[0]intValue]这样。

Swift就不会有这种不当的机制。

相对的,我们实际上可以向Swift字典和数组中添加Int/Float/AnyObject值。

下面是代替数字对象的一些Swift最常用的类型:

∙Swift:

Objective-C

∙Int:

[NSNumberintegerValue]

∙UInt:

[NSNumberunsignedIntegerValue]

∙Float:

[NSNumberfloatValue]

∙Bool:

[NSNumberboolValue]

∙Double:

[NSNumberdoubleValue]

在用Objective-C编写的不同类型中,我们仍可以用数字对象来进行转换,不过在Swift中,转化值的常用方式是使用目标类型的构造函数。

举个例子,如果我们从API中获得一个数字userID,将其在数字对象中打开并显示为字符串,在Objective-C中需要输入[userIdstringValue]。

而在Swift中数字对象不再使用(除非要向后兼容Objective-C),因为在Swift中,数字结构与在Objective-C中限制不同。

引用

注意:

在使用Objective-C或依赖没有Swift封装的旧式代码库中,可能仍得使用数字对象。

在这种情况下,数字对象API基本没什么变化。

相反,在Swift中我们通过构造函数进行等效转换。

举个例子,如果userID是一个Int,而我们想要字符串的话,只需通过String(userId)进行转换。

这比一直将数字对象装箱拆箱容易多了,不过数字对象所提供的各种各样的转换,确实让API简单易用。

通过默认参数减少样板文件代码

在Swift中,函数自变量现在可以有默认值了。

这些默认的参数减少了杂乱程度。

如果某函数的被调用者选择使用默认值,由于默认参数可以省略,这个函数调用就能更短一些了。

例如:

uncprintAlertWithMessage(message:

String,title:

String="title"){

print("Alert:

\(title)|\(message)")

}

printAlertWithMessage("message")//prints:

Alert:

title|message

printAlertWithMessage("message",title:

"non-defaulttitle")//prints:

Alert:

non-defaulttitle|messagex

为更熟练的使用者提供的一些Swift技巧

通过Guard来验证方法

Swift的guard语句让代码更简洁、更安全。

guard语句会检查一到多个情况,找出不符合else部分的调用。

而else部分需要return,break,continue或throw语句来终止方法的执行,也就是说终止程序控制的执行。

我们使用guard语句来减少代码混乱,并避免在if/else语句中的嵌入。

由于在guard语句的else部分中,代码必须转移程序控制的范围,如果出现无效的情况,简单地采用if语句来调用return语句更为安全。

在编译时这些bug仍有可能出现。

如果guard语句的情况通过的话,在我们的范围中,解包后的可选值仍旧可用。

classProjectManager{

funcincreaseProductivityOfDeveloper(developer:

Developer){

guardletdeveloperName=developer.nameelse{

print("Papers,please!

")

return

}

letslackMessage=SlackMessage(message:

"\(developerName)isagreatiOSDeveloper!

")

slackMessage.send()

}

}

用Defer管理程序控制流

defer语句会推迟包含这个命令的代码执行,直到当前范围终止。

也就是说,在defer语句中清理逻辑是可以替换的,而且只要离开相应的调用范围,这段命令就肯定就会被调用。

这样可以减少冗余步骤,更重要的是增加安全性。

funcdeferExample(){

defer{

print("Leavingscope,timetocleanup!

")

}

print("Performingsomeoperation...")

}

//Prints:

//Performingsomeoperation...

//Leavingscope,timetocleanup!

简化单例模式(Singleton)

在任何语言中对单例模式的使用都属于热议话题,不过它仍是大多数开发人员非常熟悉的模式。

在Objective-C中,实现单例模式包括多个步骤,以便确保不会多次创建单例模式类。

在Swift中这种使用有了大幅简化。

下面我们会看到在Objective-C中实现单例模式的代码行数,是在Swift中实现单例模式代码的两倍。

除此之外,由于使用了dispatchtoken,不仅可读性较差,也很难记住。

Objective-C:

@implementationMySingletonClass

+(id)sharedInstance{

staticMySingletonClass*sharedInstance=nil;

staticdispatch_once_tonceToken;

dispatch_once(&onceToken,^{

sharedInstance=[[selfalloc]init];

});

returnsharedInstance;

}

Swift:

classMySingletonClass{

staticletsharedInstance=MySingletonClass()

privateinit(){

}

}

通过协议扩展减少重复的代码

在Objective-C中,我们通过分类来扩展已有的类型,不过这种做法对协议无效。

Swift允许向协议中添加功能,使用Swift可以扩展单协议(甚至在标准数据库中的那些!

),并将其应用在实现协议的类中。

协议扩展足够将我们的整个编程范式从面向对象式改为面向协议式。

这个概念的关键在于,我们默认通过协议来添加功能,而不是通过类,以便增加代码的可复用性。

想要了解更多面向协议编程的知识,请查看这个视频,摘自WWDC2015。

创建全局Helper函数

全局变量和函数经常被合称为“坏东西”,不过事实是两者都能让代码更干净,真正的坏东西是全局状态。

全局函数经常需要全局状态来完成相关工作,因此很容易理解它们为什么会有这样的坏名声。

下面是一些GrandCentralDispatch的helper函数样例,不是建立在全局状态之上,而且多少有些语法糖(指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用)的性质。

下面我们会采用dispatch_after函数,用Swift的方式来解包:

importFoundation

/**

Executestheclosureonthemainqueueafterasetamountofseconds.

-parameterdelay:

Delayinseconds

-parameterclosure:

Codetoexecuteafterdelay

*/

funcdelayOnMainQueue(delay:

Double,closure:

()->()){

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(delay*Double(NSEC_PER_SEC))),dispatch_get_main_queue(),closure)

}

/**

Executestheclosureonabackgroundqueueafterasetamountofseconds.

-parameterdelay:

Delayinseconds

-parameterclosure:

Codetoexecuteafterdelay

*/

funcdelayOnBackgroundQueue(delay:

Double,closure:

()->()){

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(delay*Double(NSEC_PER_SEC))),dispatch_get_global_queue(QOS_CLASS_UTILITY,0),closure)

}

下面是新解包的函数样例:

delayOnBackgroundQueue(5){

showView()

}

下面是未解包的函数样例:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(delay*Double(NSEC_PER_SEC))),dispatch_get_global_queue(QOS_CLASS_UTILITY,0)){

showView()

}

用Swift语法来解包C函数,让我们的代码更易于一眼理解。

找到你最喜欢的函数,试一下吧!

只要在正确方法命名上尽责,将来程序的维护者肯定感激我们。

如果我们将上面的方法签名修改为delay(delay:

Double,closure:

()->()),这就成了不负责任的方法命名反例,因为dispatch_after需要GCD队列,而从名称中看不出来使用的哪个队列。

然而,如果我们使用的代码库在主线程所有方法的执行上有既定规范,除非在名称或评论上另有指示,delay(delay:

Double,closure:

()->())就可以是一个正确的方法名称。

无论我们如何命名helper函数,它们都是为了通过包装样本代码节省时间,让代码更易读。

扩展集合性能

Swift增加了一些方法,帮助我们对集合进行简洁的查询和修改。

这些集合方法受到了函数式语言的启发。

我们使用集合将多个值保存到一个单独的数据结构中,通常我们也会查询和修改集合。

这些函数是基于Swift的标准数据库构建,协助简化常见的任务。

为了协助诠释下面这些函数,我们使用了这些样例:

letints=[0,1,2,3,4,5,6,7,8,9]。

对集合中的每个值执行闭包映射(map),之后返回填充有映射值的映射结果类型数组。

下面我们将Int数组转化为字符串数据:

letstrings=ints.map{returnString($0)}

print("strings:

\(strings)")//prints:

strings:

["0","1","2","3","4","5","6","7","8","9"]

对数组中的每个值执行函数筛选(filter),返回Bool值。

在结果数组中,只会返回true值,而不会返回false值。

下面我们从ints数组中筛选奇数:

letevenInts=ints.filter{return($0%2==0)}

print("evenInts:

\(evenInts)")//prints:

evenInts:

[0,2,4,6,8]

reduce比map和filter更复杂,不过因为非常有用,花时间学习也是有价值的。

第一个参数就是第一个reduce值(在下面的案例中为0)。

第二个参数是访问之前reduce值和数组现值的函数。

在本例中,我们的函数是将之前的函数值简单加到数组的现值中。

letreducedInts=ints.reduce(0,combine:

+)

print("reducedInts:

\(reducedInts)")//prints:

reducedInts:

45

//definedanotherway:

letreducedIntsAlt=ints.reduce(0){(previousValue:

Int,currentValue:

Int)->Intin

returnpreviousValue+currentValue

}

print("reducedIntsAlt:

\(reducedIntsAlt)")//prints:

reducedIntsAlt:

45

通过map,filter,reduce方面的技巧,就能减少筛选时和处理集合时的工作量,并增加可读性,方便以后的人维护。

结论

这份列表收集了一些最常用的技巧,其中很多在整个代码库中都很常见。

随着Swift这门编程语言的发展,像这样的技巧也在继续增加。

我们希望能继续看到Swift的变化,并期待在应用中更多地使用这种语言。

相关阅读:

河南iOS培训Swift实践规范(17条)

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1