1. Trang chủ
  2. » Công Nghệ Thông Tin

Extending swift value(s) to the server

124 69 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 124
Dung lượng 1,74 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

But in Swift, the protocol serves that purpose: class Abstract_HTTP_Request { let url: URL // A constant instance variable init url : URL { self... For example, suppose you have two r

Trang 2

Extending Swift Value(s) to the

Server

David Ungar and Robert Dickerson

Trang 3

Extending Swift Value(s) to the Server

by David Ungar and Robert Dickerson

Copyright © 2017 IBM Corporation All rights reserved

Printed in the United States of America

Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North,Sebastopol, CA 95472

O’Reilly books may be purchased for educational, business, or salespromotional use Online editions are also available for most titles(http://safaribooksonline.com) For more information, contact ourcorporate/institutional sales department: 800-998-9938 or

corporate@oreilly.com.

Editors: Nan Barber and Susan Conant

Production Editor: Shiny Kalapurakkel

Copyeditor: Christina Edwards

Proofreader: Eliahu Sussman

Interior Designer: David Futato

Cover Designer: Karen Montgomery

Illustrator: Rebecca Panzer

January 2017: First Edition

Trang 4

Revision History for the First Edition

2017-01-25: First Release

The O’Reilly logo is a registered trademark of O’Reilly Media, Inc

Extending Swift Value(s) to the Server, the cover image, and related trade

dress are trademarks of O’Reilly Media, Inc

While the publisher and the authors have used good faith efforts to ensurethat the information and instructions contained in this work are accurate, thepublisher and the authors disclaim all responsibility for errors or omissions,including without limitation responsibility for damages resulting from the use

of or reliance on this work Use of the information and instructions contained

in this work is at your own risk If any code samples or other technology thiswork contains or describes is subject to open source licenses or the

intellectual property rights of others, it is your responsibility to ensure thatyour use thereof complies with such licenses and/or rights

978-1-491-97196-3

[LSI]

Trang 5

Preface: Swift for the Rest of

Your Application

Q: Why did the human put on his boxing gloves?

A: He had to punch some cards

Today’s applications do not run on a single platform Rather, some parts run

on resource-limited devices, and other parts run on a vast and mysteriouscloud of servers This separation has led to a schism in how we build theseapplications because different platforms have different requirements: themobile portions must conserve battery power, while the server portions musthandle a large number of requests simultaneously Consequently,

programmers use different languages for different parts of applications — forinstance, JavaScript for the browser, and Java for the server

However, constructing an application out of multiple languages is fraughtwith drawbacks: different teams in the same organization speak differentlanguages — literally — and must master different developer

ecosystems Precious time must be spent translating concepts across languagebarriers and a few developers must know all of the languages in order to beeffective Test cases and data models must be replicated in different

languages, introducing bugs and incurring future maintenance efforts

Because third-party libraries cannot be shared across groups, each team mustlearn different APIs to obtain merely the same functionality

Swift was introduced by Apple in 2014 and replaced Objective-C as the

recommended language for all new applications running on Apple devices.Later, when Swift became open source in 2015, it spread to new platforms.Currently, Swift is available on x86, ARM (including Raspberry Pi), and zOSarchitectures, as well as Linux, macOS, tvOS, watchOS, and iOS operatingsystems So, it is now possible to write a whole end-to-end mobile

application — front-end, middle, back, and even toaster — all in Swift

That’s why we wrote this book; we wanted to help you, the developer, who is

Trang 6

most likely writing in Java or JavaScript, to consider a switch to Swift.

Why adopt Swift?

The Swift language may well be better than what you are currently

using

You can develop and debug in a consistent environment Integrated

development environments (IDEs) offer a tremendous amount of

functionality such as text editing, static analysis, code completion,

debugging, profiling, and even source-control integration Switchingback and forth between say, Eclipse and Xcode is a bit like switchingbetween French horn and electric guitar: neither easy nor productive

You can reuse code When each bit of functionality is expressed exactlyonce, there is less work, more understanding, and fewer bugs

You can leverage Swift’s features — such as optional types, value types,and functional programming facilities — to detect many bugs at compiletime that would otherwise be hard to find

Since Swift uses the LLVM compiler toolchain for producing code binaries, your applications have the potential for competitive

native-performance in terms of speed, startup time, and memory usage

However, examination of performance is outside the scope of this book

You will find an active and approachable community of Swift

developers who are creating web posts, books, and videos In 2016,Swift was cited as the second “Most Loved” language in a

StackOverflow survey, and the third most upward trending technology This book will introduce you to the Swift language, illustrate some of itsmost interesting features, and show you how to create and deploy a simpleweb service Let’s get started!

Trang 7

CODING STYLE & IMPLEMENTATIONS

In the examples, the space constraints of this medium have led us to indent, break lines, and place brackets differently than we would in actual code In addition, space has precluded the inclusion of full implementations and blocks of code in this edition contain inconsistencies in color and font If the inconsistencies confuse you, please consult the repositories in Table P-1.

Table P-1 Where to find code examples

Repository name Referenced in

Book snippets Code snippets from the book

MiniPromiseKit Created in Chapter 3; used in Chapter 5

Kitura To-Do List Created in Chapter 5

Trang 8

This book would not have been possible without the support, encouragement,and guidance of the IBM Cloud and Swift@IBM leadership team, includingPat Bohrer, Eli Cleary, Jason Gartner, Sandeep Gopisetty, Heiko Ludwig,Giovanni Pacifici, John Ponzo, and Karl Weinmeister In addition, we want

to extend our thanks to the many IBM Swift engineers and Swift communitymembers working to bring Swift to the server — including Chris Bailey,Hubertus Franke, David Grove, David Jones, and Shmuel Kallner — forsharing their collective technical insights and creating the tools and librariesdescribed herein The Swift community’s embrace of Swift on the serverreassured us that our contribution would be valued The growing number oftheir instructive blog posts, videos, conference talks, and books have been ofgreat help We would like to thank our technical reviewers: Chris Devers,Shun Jiang, and Andrew Black Nan Barber and the O’Reilly team had thedaunting task of editing our lengthy technical drafts and producing this book

We owe a huge debt of gratitude to the Apple Core Swift Team for their

courage, intelligence, talent, wisdom, and generosity for bringing a new

language and ecosystem into existence and moving it to open source

Language design involves many difficult and complex tradeoffs, and bringing

a new language to the world requires a tremendous amount of work Therapid acceptance of Swift by developers is powerful testimony to the quality

of the language

Words fall short in plumbing the depths of our gratitude for the support andlove of our sweethearts, Barbara Hurd and Valerie Magolan

Trang 9

Chapter 1 A Swift Introduction

Swift supports several different programming paradigms This chapter

provides a brief overview of the parts of the Swift language that will be

familiar to a Java or JavaScript programmer Swift is not a small language,and this chapter omits many of its conveniences, including argument labels,shorthand syntax for closures, string interpolation, array and dictionary

literals, ranges, and scoping attributes Swift’s breadth lets you try Swiftwithout changing your programming style while you master its basics

Later, when ready, you can exploit the additional paradigms it offers

A beginning Swift developer may initially be overwhelmed by the

cornucopia of features in the Swift language, since it gives you many ways tosolve the same problem But taking the time to choose the right approach can

often catch bugs, shorten, and clarify your code For instance, value

types help prevent unintended mutation of values Paradigms borrowed from functional programming such as generics, closures, and protocols provide

ways to factor out not only common code, but also variations on commonthemes As a result, the underlying themes can be written once, used in

varying contexts, and still be statically checked Your programs will be mucheasier to maintain and debug, especially as they grow larger

As you read this chapter, you may want to refer to the documentation, TheSwift Programming Language (Swift 3 Edition)

Trang 10

Types and Type Inference

Swift combines strong and static typing with powerful type inference to keep

code relatively concise Swift’s type system and compile-time guaranteeshelp improve the reliability of nontrivial code

Trang 11

DECIPHERING TYPE ERRORS IN LONG

STATEMENTS

If your program won’t compile, you can often clarify a type error by breaking up an assignment statement into smaller ones with explicit type declarations for each intermediate result.

Trang 12

Swift’s syntax borrows enough from other languages to be easily readable Here’s a trivial example:

let aHost = "someMachine.com"

aHost = "anotherMachine.com" // ILLEGAL: can't change a constant

aHost is inferred by Swift to be of type String It is a constant, and Swiftwill not compile any code that changes a constant after it has been initialized

(Throughout this book, ILLEGAL means “will not compile.”) This constant is

initialized at its declaration, but Swift requires only that a constant be

initialized before being used

var aPath = "something"

aPath = "myDatabase" // OK

aPath is also a String, but is a mutable variable Swift functions usekeywords to prevent mixing up arguments at a call site For example, here is

a function:

func combine ( host : String , withPath path : String ) -> String {

return host + "/" + path

}

and here is a call to it:

// returns "someMachine.com/myDatabase"

combine ( host : aHost , withPath : aPath )

Swift’s syntax combines ease of learning, convenience of use, and prevention

of mistakes

Trang 13

An enumeration may be specified by 1) only a set of cases, 2) a set of cases,each with a fixed value, or 3) a set of cases, each with a set of assignablevalues (The last flavor is covered in Chapter 2.)

The simplest flavor merely associates a unique identifier with each case For

example:

enum Validity { case valid , invalid }

The second flavor of enumeration provides for each case to be associated with a value that is always the same for that case Such a value must be

expressed as a literal value, such as 17 or "abc" For example:

enum StatusCode: Int {

The value of this enumeration can be accessed via the rawValue attribute:

func printRealValue ( of e : StatusCode ) {

print ("real value is", e rawValue )

}

Trang 14

As in some other languages, a Swift tuple simply groups multiple values

together For example, here’s a function that returns both a name and serialnumber:

func lookup ( user : String ) -> ( String , Int ) {

// compute n and sn

return ( n , sn )

}

Tuple members can be accessed by index:

let userInfo = lookup ( user : "Washington")

print ( "name:", userInfo 0 , "serialNumber:", userInfo 1 )

or can be unpacked simultaneously:

let ( name , serialNumber ) = lookup ( user : "Adams")

print ( "name:", name , "serialNumber:", serialNumber )

Members can be named in the tuple type declaration:

func lookup ( user : String )

-> ( name : String , serialNumber : Int )

{

// compute n and sn

return ( n , sn )

}

and then accessed by name:

let userInfo = lookup ( user : "Washington")

print ("name:", userInfo name ,

"serialNumber:", userInfo serialNumber )

When an identifier is declared with let, every element of the tuple behaves

as if it were declared with a let:

Trang 15

let second = lookup ( user : "Adams")

second name = "Gomez Adams" // ILLEGAL: u is a let

var anotherSecond = lookup ( user : "Adams")

anotherSecond name = "Gomez Adams" // Legal: x is a var

print ( anotherSecond name ) // prints Gomez Adams

When you assign a tuple to a new variable, it gets a fresh copy:

var first = lookup ( user : "Washington")

var anotherFirst = first

first name // returns "George Washington"

anotherFirst name // returns "George Washington" as expected

first name = "George Jefferson"

first name // was changed, so returns "George Jefferson"

anotherFirst name // returns "George Washington" because

// anotherFirst is an unchanged copy

first and anotherFirst are decoupled; changes to one do not affectthe other This isolation enables you to reason about your program one smallchunk at a time Swift has other constructs with this tidy property; they are all

lumped into the category of value types (See Chapter 2.) The opposite of a

value type is a reference type The only reference types are classes and closures Consequently, these are the only types that allow shared

instances-of-access to mutable state

Tuples combine nicely with other language features: the standard built-inmethod for iterating through a dictionary uses key-value tuples Also, Swift’sswitch statements become very concise and descriptive by switching on atuple:

enum PegShape { case roundPeg , squarePeg }

enum HoleShape { case roundHole , squareHole , triangularHole }

func howDoes ( _ peg : PegShape , fitInto hole : HoleShape )

-> String

{

switch ( peg , hole ) { // switches on a tuple

case ( roundPeg , roundHole ):

return "fits any orientation"

case ( squarePeg , squareHole ):

return "fits four ways"

default:

return "does not fit"

Trang 16

}

}

Tuples are a convenient way to aggregate a few constant fields that have nomeaningful type apart from the types of the fields

Trang 17

Custom Operators

As in some other statically-typed languages, Swift allows you to define yourown operators based on static types

One custom operator we’ll be using in our examples later is apply, which

we’ll denote as |> Like a Unix pipe, it feeds a result on the left into a

function on the right:

9.0 |> sqrt // returns 3

This lets us read code from left to right, in the order of execution For

example:

send ( compress ( getImage ()))

can be rewritten as:

getImage () |> compress |> send

To define this custom operator, you first tell Swift about the syntax:

infix operator |> : LeftFunctionalApply

then provide a (generic) implementation:

func |> < In , Out > ( lhs : In , rhs : ( In ) throws -> Out )

Trang 18

Swift includes closures: anonymous functions defined inline, which can readand write to their enclosing lexical scope.1 A closure is a first-class entity, areference type which can be assigned to constants and variables, passed asarguments, and returned as results Functions and methods are just specialcases of closures with a slightly different syntax for their definition Closures

support functional programming, which can be particularly helpful in dealing

with asynchrony (Chapter 3)

Trang 19

DECIPHERING TYPES ERRORS IN CLOSURES

Most of the time, the Swift compiler can infer the types of closure arguments and results When it cannot, or when the code includes a type error, the error message from the compiler can be obscure You can often clarify type errors by adding types to a closure that are not strictly necessary In the example below, if the compiler were to complain about a type, you could add the (previously implicit) types:

}

Trang 20

Object Orientation

Swift includes full support for class-based object-oriented programming,including classes, instances, constructors (called “initializers”), instancevariables, static variables, computed virtual getters and setters, inheritance,

and final attributes When one method overrides another, it must be

annotated in the code This requirement prevents some errors Experienced

Swift programmers tend to reserve objects for things requiring shared access

to a mutable state (See Chapter 2.)

Trang 21

Protocols Define Interfaces

As with other typed languages, Swift includes a notion that the expectedbehavior of an entity is separate from any particular embodiment of that

entity The former is called a protocol and the latter a concrete type — think

interface versus class if you’re a Java programmer A Swift protocol lets youdefine what is expected of a type without specifying any implementation Forexample, the operations expected of any HTTP_Request might be that itcan supply a URL and a requestString:

protocol HTTP_Request_Protocol {

var url: URL {get}

var requestString: String {get}

}

In other languages, Abstract_HTTP_Request might need an abstractrequestString But in Swift, the protocol serves that purpose:

class Abstract_HTTP_Request {

let url: URL // A constant instance variable

init( url : URL ) { self url = url }

var requestString: String { return "POST" }

var data: String

init( url : URL , data : String ) {

self data = data

super.init( url : url )

}

}

When declaring a variable to hold a request, instead of using the type

Abstract_HTTP_Request, use the type HTTP_Request_Protocol:

Trang 22

let aRequest: HTTP_Request_Protocol

= Get_HTTP_Request ( url : … /* some URL */)

aRequest requestString // returns "GET"

In the following, you’ll see an example of a generic protocol, which could not

be used as a type As in other languages, protocols help to prevent errors aswell as remove dependencies on the representation

Trang 23

Generic Protocols

Generic entities allow the same basic code to apply to different types Inaddition to concrete types, Swift also allows protocols to be generalized todifferent types, although the mechanism differs

For example, suppose you have two responses, a TemperatureResponseand a FavoriteFoodResponse:

struct TemperatureResponse {

let city: String

let answer: Int

}

struct FavoriteFoodResponse {

let city: String

let answer: String

}

Even though each answer is a different type, they can share the same

description by adopting a common protocol:

protocol ResponseProtocol {

associatedtype Answer

var city: String {get}

var answer: Answer {get}

}

struct TemperatureResponse: ResponseProtocol {

let city: String

let answer: Int

}

struct FavoriteFoodResponse: ResponseProtocol {

let city: String

let answer: String

Trang 24

Unfortunately, generic protocols such as this one are more difficult to usethan nongeneric ones Specifically, they cannot be used in place of types, butonly as generic constraints So, you cannot write a declaration to hold a valuethat conforms to ResponseProtocol:

var someResponse: ResponseProtocol // ILLEGAL

But you can write a function that will work on any type that conforms toResponseProtocol:

func handleResponse < SomeResponseType : ResponseProtocol >

( response : SomeResponseType ) { … }

Because a generic protocol cannot be used as a type, it is often helpful to split

up a generic protocol into generic and nongeneric protocols A full discussion

of these generic protocols is beyond the scope of this book Generic protocolssupport generic functions, structures, and objects by providing a way to

express requirements and implementations that apply to entities that

themselves generalize over the types of their arguments, results, or

constituents

Trang 25

Extending Classes, Structures, and

Enumerations

Like many other languages, Swift allows you to add new behavior to a

preexisting construct In Swift, this capability is called an extension, andcan be used with classes, structures, enumerations, and protocols The lastcase is a bit different because protocol extensions supply default behavior,just as method bodies in Java interfaces do

As you might expect, Swift’s extension facility is especially useful forlarge programs because the entity you want to extend is likely to have beendefined in a separate library You might not even have source code for it!

Less obviously, Swift’s extensions help in two other situations:

1 An extension can add a bit of specialized functionality that is onlyvisible within a single file Suppose that in some computation youfind yourself squaring a number often, such as (a/b) * (a/b)+ (c/d) * (c/d) You could add the following to the file

containing that code:

private extension Int { var squared: Int { return self * self } }

Now you can rewrite the above as (a/b).squared +

(c/d).squared The extension adds a new member to Int withoutcluttering up its namespace everywhere

2 You might have a set of classes where each performs the same set offunctions Extensions let you group the code by function as opposed

to class An extension need not be in the same file or even the samemodule as the original definition For example, you might haveclasses for city and state that each perform a country lookup:

class City {

Trang 26

let name: String

init( name : String ) { self name = name } func lookupCountry () -> String { … } }

class State { let name: String

init( name : String ) { self name = name } func lookupCountry () -> String { … } }

Extensions let you group the lookup functions together:

extension City { func lookupCountry () -> String { … } }

extension State { func lookupCountry () -> String { … } }

This lets you put functionality where it makes the most sense, whether in atype defined by a library, limited to the scope of a single file, or together withsimilar functionality for different types

Unlike Smalltalk, Swift closures cannot return from the home method’s scope (a.k.a., nonlocal

return), so they cannot be used to extend the built-in control structures.

1

Trang 27

Chapter 2 Optional Types,

Structures, & Enumerations

Programming is hard and debugging is harder, but maintaining and

debugging large programs that run asynchronously and concurrently is

hardest It makes sense to place the burden of checking certain runtime

properties of your program on the compiler rather than the developer Swift’s

optional types and structures let you tell the compiler more about your

program now so that you spend less time debugging later These features rely

on a combination of compile-time checks and runtime techniques that, inmost cases, do not reduce performance

Trang 28

Optional Types Exterminate Nil-Value Bugs

Programs represent uninitialized or absent values with nil (a.k.a., null) If

your code fails to handle a nil value anywhere one can occur, bad things can

happen So Swift incorporated the might-be-nil versus can-never-be-nil

distinction into its static type system These are called “just” and “maybe” inHaskell For example, suppose you are trying to extract the “Content-Type”entry from the header fields of an HTTP request You have the header fieldsrepresented as a dictionary with String keys and values:

let headerFields: [ String : String ] = …

Swift uses subscript notation to look up the value of a given key in a

dictionary:

let contentType = headerFields ["Content-Type"]

and Swift will infer a type for contentType But that type is

not “String”! It is “String?” with a question mark, because String

represents a value that can never be nil, whereas String? represents a value

that be either a String or nil The latter is called an optional type The

dictionary lookup returns an optional type because the dictionary might notcontain a key for “Content-Type.” The type String? is not the same type asString and Swift won’t let you use the value of an optional type without anexplicit check:

if contentType hasPrefix ("text") // ILLEGAL

There are many convenient ways to perform this check For example, theif-let form checks if the value is nonnil If so, it assigns the value to a

new variable that is local to the then-part If the value is nil, it executes the

else-part, if any.

let contentType: String

Trang 29

if let ct = headerFields ["Content-Type"] {

let contentType = headerFields ["Content-Type"] ?? "none"

Swift’s treatment of nil values will significantly improve the quality of yourprograms over many other languages Swift’s optionals add security withoutinconvenience

SURPRISINGLY HELPFUL, A PERSONAL NOTE FROM DAVID

For decades, I had programmed in languages that treat null as a universal value of any type When

I adopted Swift, I was quite surprised by how much its treatment of nil improved the quality of my

code The unexpected benefit arose where a variable was not optional because it was guaranteed

to never be nil As a result, that variable was eliminated as a potential source of crashes.

Trang 30

Structures Isolate Mutation

A Swift structure (struct) is like an instance of a class: it groups values

together, the values can be fixed or variable, and it includes methods thatoperate on those values However, unlike an instance of a class, a structure is

a value type Assigning a structure to a variable creates a fresh copy,

decoupling the assignee from the assigned value Whenever a struct isassigned to a new variable, as for instance when passed as an argument to a

function, the struct is passed by value; in other words, the new variable

receives a copy of every value in the structure

Trang 31

STRUCTURE MUTATION GUARANTEES

A structure provides two guarantees about its mutability:

1 When passed to another routine or placed in another variable, the original structure is insulated from any changes to the passed structure and vice versa.1

2 When a structure is associated with an identifier via a let statement, the value

of the identifier may not change.

Using structures can prevent bugs Suppose you need to send a request to adatabase to find out the current temperature in a given city, and you also need

to check and see how long the database took You could use a class with an

instance variable, startTime:

class TemperatureRequestClass {

let city: String

var startTime: Date ? = nil // an optional Date

init( city : String ) {

self city = city

}

}

After creating a request object:

let request = TemperatureRequestClass ( city : "Paris")

you might hand it off to be processed:

request startTime = Date now

sendToDB ( request : request , callback : receivedResponse )

and later print the time difference:

func receivedResponse ( temperature : Int ) {

let dbTime = Date now timeIntervalSince ( request startTime !) print ("It took", dbTime ,

"seconds to discover that the temperature in",

Trang 32

request city , "is", temperature )

… // Do lots of slow work to prepare to connection

request startTime = Date now //The BUG!

… // Send the request on the prepared connection

}

Now your requestTime calculation would be wrong (See Figure 2-1.)

Figure 2-1 Because instances of classes are reference types, when request is passed to sendDB that function gets a reference to the same object Then when it incorrectly mutates startTime, the original

object is corrupted.

Swift provides a better way — using a structure instead of a class:

struct TemperatureRequestStruct {

let city: String

var startTime: Date ? = nil

init( city : String ) {

self city = city

}

}

Trang 33

Because your calling code alters the startTime, which is contained in astructure, it must put that structure in a var, not a let:

var request = TemperatureRequestStruct ( city : "Paris")

Now, the Swift compiler catches the error before the program can even run!

func sendToDB (

request : TemperatureRequestStruct ,

callback : ( Int ) -> Void

) {

… // Do lots of slow work to prepare to connection

request startTime = Date now // ILLEGAL: will not compile!! … // Send the request on the prepared connection

var mutableRequest = request

mutableRequest startTime = Date now

Swift will still protect your code from the bug because even though

sendToDB has changed the startTime, it would not change for the caller.The caller would not see the mutation because when the request was passed

to sendToDB, it was copied (See Figure 2-2.) Value types are always

copied.2

Trang 34

Figure 2-2 Because structures are value types, the sendToDB function receives a whole new copy of the request The first attempt at mutation won’t even compile because parameters are lets If the request is assigned to a mutableRequest var, the mutation will compile but won’t corrupt the original.

Trang 35

Mutating Methods

Unlike any other value type, a structure can include methods that mutate the

contents of var fields in the structure As a helpful signal to the programmer,such methods must be annotated with mutating This requirement

highlights the places where side effects could occur:

struct TemperatureRequestStruct {

var startTime: Date ? = nil

mutating func clearStartTime () { startTime = nil }

}

Since a let prohibits mutation, a mutating function may be invoked only

upon a var Unlike mutating an instance variable of a class, a mutation to a

structure only changes the copy referenced by one variable:

let request1 = TemperatureRequestStruct ( city : "Paris")

var request2 = request1 // makes a copy because is a struct

request1 clearStartTime () // ILLEGAL: cannot mutate a let

request2 clearStartTime () // OK, but does not change request1

Trang 36

STRUCTURES CONTAINING REFERENCES TO

OBJECTS

A structure may contain a reference to an instance of a class, and vice versa If a

structure contains such a reference, some of the desirable properties of a value type may

be lost In particular, if a structure includes an attribute that computes its result by

querying a class instance, the result may be subject to unexpected changes This problem

is transitive: it can occur even if your value type contains a value type from a library and that type contains a reference to a class instance.

Despite its potential for havoc, it is the above feature that enables the Swift standard

library to provide one of the most valuable features of Swift, collection value-types:

Swift’s Arrays, Dictionaries, and Sets are structures, and therefore provide strong support for mutation isolation and debugging Under the covers, they use class instances to optimize mutation with the goal of providing both performance and robust semantics For instance, if you write a function that passes an array into a library, you can rest assured that the library cannot change your array It behaves as if copied, but can often avoid the actual expense of copying.

Trang 37

Default Implementations with Protocol Extensions

Swift classes can inherit from each other, but if you want to factor out

behavior that two structures share, instead of creating a super-class, you must

use a protocol extension This technique is sometimes called

protocol-oriented programming For example, suppose you write two similar

structures, a temperature request and an ozone-level request:

struct TemperatureRequest {

let city: String

let state: String

var startTime: Date ? = nil

init( city : String , state : String ) {

self city = city

self state = state

}

var cityAndState: String { return city + ", " + state }

}

struct OzoneRequest {

let city: String

let state: String

var startTime: Date ? = nil

init( city : String , state : String ) {

self city = city

self state = state

}

var cityAndState: String { return city + ", " + state }

}

Both structures have identical cityAndState properties that return a

string combining the city and state You can factor out the common code with

a protocol and a protocol extension First, the Request protocol ensures thatthe properties used by the common code, in this case city and state, areimplemented by the adopter of the protocol Then the protocol extension addsthe new cityAndState property to all objects that conform to the

Request protocol

Trang 38

protocol Request {

var city: String {get}

var state: String {get}

}

extension Request {

var cityAndState: String { return city + ", " + state }

}

struct TemperatureRequest: Request {

let city: String

let state: String

var startTime: Date ? = nil

init( city : String , state : String ) {

self city = city

self state = state

}

}

struct OzoneRequest: Request {

let city: String

let state: String

var startTime: Date ? = nil

init( city : String , state : String ) {

self city = city

self state = state

}

}

A protocol extension cannot add new requirements to a protocol, nor can itadd additional data fields to an object; all it can do is add implementations ofnew behavior to the types that adopt the protocol (A full description of

protocol extensions is outside the scope of this book; see The Swift

Programming Language (Swift 3).)

What if you need to express a multilevel hierarchy? Protocols can inheritfrom other protocols, but it’s not quite the same as subclassing — for

example, there is no super.

Structures with protocols and protocol extensions provide one way to replace

a class hierarchy and limit mutation Swift includes yet another: enumerationswith associated values These can do an even better job in the right situations.Structures are like instances of classes that are always copied when beingpassed around Use structures wherever you don’t need separate references tothe same mutable state With structures, you don’t have to code the copy

Trang 39

operation, you don’t have to clutter up the code with copying, and in the case

of larger structures, the compiler can optimize away much of the expense ofcopying Substituting a structure for an instance of a class changes the easiestthing to write from passing a reference to copying a value Bye-bye bugs

Trang 40

Enumerations with Associated Values

In Swift, there is usually more than one way to express a given concept Forinstance, you can choose between a class and a structure Moreover, Swiftincludes many features from the functional programming school Judiciouslyexploited, these constructs can clarify, shorten, and generalize your code.Like color TV, air conditioning, or the smartphone, you may never have

missed this next construct, yet may soon find you would not think of living

without it One of these is the enumeration with associated values.

As described in “Simple Enumerations”, a Swift enumeration represents avalue taken from a fixed set — for example, one of a fixed set of HTTP

requests:

enum HTTP_Request_Kind {

case get , post // other request types omitted for brevity

}

But Swift enumerations can do so much more because any case can also

include one or more associated values Such an enumeration is more like a

discriminated union than a Java or C enumeration It can replace a small classhierarchy or a group of structures that implement a common protocol

For example, suppose you need to implement HTTP requests with an equality(==) operation You could create a class hierarchy, with an abstract

superclass containing common code and concrete subclasses for each kind ofrequest But since you don’t want any asynchronous code to mutate requests,you would do better to use structures and a protocol The protocol wouldhave to be generic (because a requirement of == references the associatedtype, Self) However, recall that Swift disallows the use of a

generic protocol as a type:

protocol HTTP_Request {

static func == ( a : Self, b : Self) -> Bool

}

struct Get: HTTP_Request { }

struct Post: HTTP_Request { }

Ngày đăng: 05/03/2019, 08:31

TỪ KHÓA LIÊN QUAN