您的位置:首页 > 移动开发 > Swift

swift,NSUserDefaults的swift化封装

2016-03-09 02:00 375 查看


NSUserDefaultshtml,body{overflow-x:initial!important;}.CodeMirror{height:auto;}
.CodeMirror-scroll{overflow-y:hidden;overflow-x:auto;}
.CodeMirror-lines{padding:4px0px;}
.CodeMirrorpre{}
.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:white;}
.CodeMirror-gutters{border-right-width:1px;border-right-style:solid;border-right-color:rgb(221,221,221);background-color:rgb(247,247,247);white-space:nowrap;}
.CodeMirror-linenumbers{}
.CodeMirror-linenumber{padding:0px3px0px5px;text-align:right;color:rgb(153,153,153);}
.CodeMirrordiv.CodeMirror-cursor{border-left-width:1px;border-left-style:solid;border-left-color:black;z-index:3;}
.CodeMirrordiv.CodeMirror-secondarycursor{border-left-width:1px;border-left-style:solid;border-left-color:silver;}
.CodeMirror.cm-keymap-fat-cursordiv.CodeMirror-cursor{width:auto;border:0px;background-color:rgb(119,238,119);z-index:1;background-position:initialinitial;background-repeat:initialinitial;}
.CodeMirrordiv.CodeMirror-cursor.CodeMirror-overwrite{}
.cm-tab{display:inline-block;}
.cm-s-default.cm-keyword{color:rgb(119,0,136);}
.cm-s-default.cm-atom{color:rgb(34,17,153);}
.cm-s-default.cm-number{color:rgb(17,102,68);}
.cm-s-default.cm-def{color:rgb(0,0,255);}
.cm-s-default.cm-variable{color:black;}
.cm-s-default.cm-variable-2{color:rgb(0,85,170);}
.cm-s-default.cm-variable-3{color:rgb(0,136,85);}
.cm-s-default.cm-property{color:black;}
.cm-s-default.cm-operator{color:black;}
.cm-s-default.cm-comment{color:rgb(170,85,0);}
.cm-s-default.cm-string{color:rgb(170,17,17);}
.cm-s-default.cm-string-2{color:rgb(255,85,0);}
.cm-s-default.cm-meta{color:rgb(85,85,85);}
.cm-s-default.cm-qualifier{color:rgb(85,85,85);}
.cm-s-default.cm-builtin{color:rgb(51,0,170);}
.cm-s-default.cm-bracket{color:rgb(153,153,119);}
.cm-s-default.cm-tag{color:rgb(17,119,0);}
.cm-s-default.cm-attribute{color:rgb(0,0,204);}
.cm-s-default.cm-header{color:blue;}
.cm-s-default.cm-quote{color:rgb(0,153,0);}
.cm-s-default.cm-hr{color:rgb(153,153,153);}
.cm-s-default.cm-link{color:rgb(0,0,204);}
.cm-negative{color:rgb(221,68,68);}
.cm-positive{color:rgb(34,153,34);}
.cm-header,.cm-strong{font-weight:bold;}
.cm-del{text-decoration:line-through;}
.cm-em{font-style:italic;}
.cm-link{text-decoration:underline;}
.cm-s-default.cm-error{color:rgb(255,0,0);}
.cm-invalidchar{color:rgb(255,0,0);}
div.CodeMirrorspan.CodeMirror-matchingbracket{color:rgb(0,255,0);}
div.CodeMirrorspan.CodeMirror-nonmatchingbracket{color:rgb(255,34,34);}
.CodeMirror-activeline-background{background-color:rgb(232,242,255);background-position:initialinitial;background-repeat:initialinitial;}
.CodeMirror{position:relative;overflow:hidden;}
.CodeMirror-scroll{margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;padding-right:30px;height:100%;outline:none;position:relative;box-sizing:content-box;}
.CodeMirror-sizer{position:relative;}
.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none;}
.CodeMirror-vscrollbar{right:0px;top:0px;overflow-x:hidden;overflow-y:scroll;}
.CodeMirror-hscrollbar{bottom:0px;left:0px;overflow-y:hidden;overflow-x:scroll;}
.CodeMirror-scrollbar-filler{right:0px;bottom:0px;}
.CodeMirror-gutter-filler{left:0px;bottom:0px;}
.CodeMirror-gutters{position:absolute;left:0px;top:0px;padding-bottom:30px;z-index:3;}
.CodeMirror-gutter{white-space:normal;height:100%;box-sizing:content-box;padding-bottom:30px;margin-bottom:-32px;display:inline-block;}
.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4;}
.CodeMirror-lines{cursor:text;}
.CodeMirrorpre{border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-right-radius:0px;border-bottom-left-radius:0px;border-width:0px;background-color:transparent;font-family:inherit;font-size:inherit;margin:0px;white-space:pre;word-wrap:normal;color:inherit;z-index:2;position:relative;overflow:visible;background-position:initialinitial;background-repeat:initialinitial;}
.CodeMirror-wrappre{word-wrap:break-word;white-space:pre-wrap;word-break:normal;}
.CodeMirror-codepre{border-right-width:30px;border-right-style:solid;border-right-color:transparent;width:-webkit-fit-content;}
.CodeMirror-wrap.CodeMirror-codepre{border-right-style:none;width:auto;}
.CodeMirror-linebackground{position:absolute;left:0px;right:0px;top:0px;bottom:0px;z-index:0;}
.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto;}
.CodeMirror-widget{}
.CodeMirror-wrap.CodeMirror-scroll{overflow-x:hidden;}
.CodeMirror-measure{position:absolute;width:100%;height:0px;overflow:hidden;visibility:hidden;}
.CodeMirror-measurepre{position:static;}
.CodeMirrordiv.CodeMirror-cursor{position:absolute;visibility:hidden;border-right-style:none;width:0px;}
.CodeMirrordiv.CodeMirror-cursor{visibility:hidden;}
.CodeMirror-focuseddiv.CodeMirror-cursor{visibility:inherit;}
.CodeMirror-selected{background-color:rgb(217,217,217);background-position:initialinitial;background-repeat:initialinitial;}
.CodeMirror-focused.CodeMirror-selected{background-color:rgb(215,212,240);background-position:initialinitial;background-repeat:initialinitial;}
.cm-searching{background-color:rgba(255,255,0,0.4);background-position:initialinitial;background-repeat:initialinitial;}
.CodeMirrorspan{}
@mediaprint{
.CodeMirrordiv.CodeMirror-cursor{visibility:hidden;}
}

html{font-size:14px;}
body{margin:0px;padding:0px;height:auto;bottom:0px;top:0px;left:0px;right:0px;font-family:'HelveticaNeue',Helvetica,Arial,sans-serif;font-size:1rem;line-height:1.42857143;color:rgb(51,51,51);background-color:rgb(255,255,255);overflow-x:hidden;}
a:active,a:hover{outline:0px;}
.in-text-selection,::selection{background-color:rgb(181,214,252);text-shadow:none;background-position:initialinitial;background-repeat:initialinitial;}
#write{margin:0pxauto;height:auto;width:inherit;word-break:normal;word-wrap:break-word;position:relative;padding-bottom:70px;}
body.typora-export{padding-left:30px;padding-right:30px;}
.typora-export#write{margin:0pxauto;}
#write>p:first-child,#write>ul:first-child,#write>ol:first-child,#write>pre:first-child,#write>blockquote:first-child,#write>div:first-child,#write>table:first-child{margin-top:30px;}
img{max-width:100%;}
input,button,select,textarea{color:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-variant:inherit;font-weight:inherit;line-height:inherit;}
input[type="checkbox"],input[type="radio"]{line-height:normal;padding:0px;}
::before,::after,*{box-sizing:border-box;}
#writep,#writeh1,#writeh2,#writeh3,#writeh4,#writeh5,#writeh6,#writediv,#writepre{width:inherit;}
#writep,#writeh1,#writeh2,#writeh3,#writeh4,#writeh5,#writeh6{position:relative;}
h1{font-size:2rem;}
p{-webkit-margin-before:1rem;-webkit-margin-after:1rem;-webkit-margin-start:0px;-webkit-margin-end:0px;}
.mathjax-block{margin-top:0px;margin-bottom:0px;-webkit-margin-before:0rem;-webkit-margin-after:0rem;}
.hidden{display:none;}
.md-blockmeta{color:rgb(204,204,204);font-weight:bold;font-style:italic;}
a{cursor:pointer;}
#writeinput[type="checkbox"]{cursor:pointer;width:inherit;height:inherit;margin:4px0px0px;}
tr{page-break-inside:avoid;page-break-after:auto;}
thead{display:table-header-group;}
table{border-collapse:collapse;border-spacing:0px;width:100%;overflow:auto;page-break-inside:auto;}
table.md-tabletd{min-width:80px;}
.CodeMirror-gutters{border-right-width:0px;background-color:inherit;}
.CodeMirror{text-align:left;}
.CodeMirror-placeholder{opacity:0.3;}
.CodeMirrorpre{padding:0px4px;}
.CodeMirror-lines{padding:0px;}
div.hr:focus{cursor:none;}
pre{white-space:pre-wrap;}
.show-fences-line-numberpre.md-fences{padding-left:0px;}
.CodeMirror-gutters{margin-right:4px;}
.md-fences,pre.md-fences{font-size:0.9rem;display:block;page-break-inside:avoid;text-align:left;overflow:visible;white-space:pre;background-image:inherit;background-size:inherit;background-attachment:inherit;background-origin:inherit;background-clip:inherit;background-color:inherit;position:relative!important;background-position:inheritinherit;background-repeat:inheritinherit;}
.md-fences.CodeMirror.cm-s-default.CodeMirror-wrap{top:-1.6em;margin-bottom:-1.6em;}
.md-fences.mock-cm{white-space:pre-wrap;}
.footnotes{color:rgb(136,136,136);font-size:0.9rem;padding-top:1em;padding-bottom:1em;}
.footnotes+.footnotes{margin-top:-1em;}
.md-reset{margin:0px;padding:0px;border:0px;outline:0px;vertical-align:top;background-color:transparent;text-decoration:none;color:rgb(51,51,51);font-family:'HelveticaNeue',Helvetica,Arial,sans-serif;font-size:1rem;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;box-sizing:content-box;direction:ltr;background-position:initialinitial;background-repeat:initialinitial;}
lidiv{padding-top:0px;}
blockquote{margin:1rem0px;}
lip,li.mathjax-block{margin:0.5rem0px;}
li{margin:0px;position:relative;}
blockquote>:last-child{margin-bottom:0px;}
blockquote>:first-child{margin-top:0px;}
.footnotes-area{color:rgb(136,136,136);margin-top:0.714rem;padding-bottom:0.143rem;}
@mediaprint{
html,body{height:100%;}
.typora-export*{-webkit-print-color-adjust:exact;}
}
.footnote-line{margin-top:0.714em;font-size:0.7em;}
aimg,imga{cursor:pointer;}
#writepre.md-meta-block{font-size:0.8rem;min-height:2.86rem;white-space:pre-wrap;background-color:rgb(204,204,204);display:block;background-position:initialinitial;background-repeat:initialinitial;}
p>.md-image:only-child{display:inline-block;width:100%;text-align:center;}
#write.MathJax_Display{margin:0.8em0px0px;}
.mathjax-block{white-space:pre;overflow:hidden;width:100%;}
p+.mathjax-block{margin-top:-1.143rem;}
.mathjax-block:not(:empty)::after{display:none;}
[contenteditable="true"]:active,[contenteditable="true"]:focus{outline:none;box-shadow:none;}
.task-list{list-style-type:none;}
.task-list-item{position:relative;padding-left:1em;}
.task-list-iteminput{position:absolute;top:0px;left:0px;}
.math{font-size:1rem;}
.md-toc{min-height:3.58rem;position:relative;font-size:0.9rem;border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;}
.md-toc-content{position:relative;margin-left:0px;}
.md-toc::after,.md-toc-content::after{display:none;}
.md-toc-item{display:block;color:rgb(65,131,196);text-decoration:none;}
.md-toc-inner:hover{text-decoration:underline;}
.md-toc-inner{display:inline-block;cursor:pointer;}
.md-toc-h1.md-toc-inner{margin-left:0px;font-weight:bold;}
.md-toc-h2.md-toc-inner{margin-left:2em;}
.md-toc-h3.md-toc-inner{margin-left:4em;}
.md-toc-h4.md-toc-inner{margin-left:6em;}
.md-toc-h5.md-toc-inner{margin-left:8em;}
.md-toc-h6.md-toc-inner{margin-left:10em;}
.md-toc-h6{margin-left:12em;}
@mediascreenand(max-width:48em){
.md-toc-h3.md-toc-inner{margin-left:3.5em;}
.md-toc-h4.md-toc-inner{margin-left:5em;}
.md-toc-h5.md-toc-inner{margin-left:6.5em;}
.md-toc-h5.md-toc-inner{margin-left:8em;}
.md-toc-h6{margin-left:9.5em;}
}
a.md-toc-inner{color:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;text-decoration:inherit;line-height:inherit;}
.footnote-linea:not(.reversefootnote){color:inherit;}
.md-attr{display:none;}
.md-fn-count::after{content:'.';}
.md-tag{opacity:0.5;}
code{text-align:left;}
h1.md-tag,h2.md-tag,h3.md-tag,h4.md-tag,h5.md-tag,h6.md-tag{font-weight:initial;opacity:0.35;}
a.md-header-anchor.md-print-anchor{border:none!important;display:inline-block!important;position:absolute!important;width:1px!important;right:0px!important;outline:none!important;background-color:transparent!important;text-shadow:initial!important;background-position:initialinitial!important;background-repeat:initialinitial!important;}
.md-inline-math.MathJax_SVG.noError{display:none!important;}
.MathJax_SVG_Display{text-align:center;margin:1em0em;position:relative;text-indent:0px;max-width:none;max-height:none;min-width:0px;min-height:0px;width:100%;display:block!important;}
.MathJax_SVG.MJX-monospace{font-family:monospace;}
.MathJax_SVG.MJX-sans-serif{font-family:sans-serif;}
.MathJax_SVG{display:inline;font-style:normal;font-weight:normal;line-height:normal;font-size:100%;text-indent:0px;text-align:left;text-transform:none;letter-spacing:normal;word-spacing:normal;word-wrap:normal;white-space:nowrap;float:none;direction:ltr;max-width:none;max-height:none;min-width:0px;min-height:0px;border:0px;padding:0px;margin:0px;}
.MathJax_SVG*{-webkit-transition:none;transition:none;}

html{font-size:15px;}
html,body{margin:auto;background-color:rgb(254,254,254);background-position:initialinitial;background-repeat:initialinitial;}
body{font-family:Vollkorn,Palatino,Times;color:rgb(51,51,51);line-height:1.4;text-align:justify;font-size:19px;}
#write{max-width:900px;margin:0pxauto2em;line-height:1.53;}
#write>h2:first-child,#write>h3:first-child,#write>h4:first-child,#write>p:first-child{margin-top:1.2em;}
#write>h1:first-child,h1{margin-top:1.6em;font-weight:normal;}
h1{font-size:3em;}
h2{margin-top:2em;font-weight:normal;}
h3{font-weight:normal;font-style:italic;margin-top:3em;}
h1,h2,h3{text-align:center;}
h2::after{border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(47,47,47);content:'';width:100px;display:block;margin:0pxauto;height:1px;}
h1+h2,h2+h3{margin-top:0.83em;}
p,.mathjax-block{margin-top:0px;}
ul{list-style:square;padding-left:1.2em;}
ol{padding-left:1.2em;}
blockquote{margin-left:1em;padding-left:1em;border-left-width:1px;border-left-style:solid;border-left-color:rgb(221,221,221);}
code,pre{font-family:Consolas,Menlo,Monaco,monospace,serif;font-size:0.9em;background-color:white;background-position:initialinitial;background-repeat:initialinitial;}
pre.md-fences{margin-left:1em;padding-left:1em;border:1pxsolidrgb(221,221,221);padding-bottom:8px;padding-top:6px;margin-bottom:1.5em;}
a{color:rgb(36,132,193);text-decoration:none;}
a:hover{text-decoration:underline;}
aimg{border:none;}
h1a,h1a:hover{color:rgb(51,51,51);text-decoration:none;}
hr{color:rgb(221,221,221);height:1px;margin:2em0px;border-top-style:solid;border-top-width:1px;border-top-color:rgb(221,221,221);border-bottom-style:none;border-left-width:0px;border-right-width:0px;}
.md-table-edit{background-color:rgb(237,237,237);padding-top:4px;background-position:initialinitial;background-repeat:initialinitial;}
table{margin-bottom:1.333333rem;}
tableth,tabletd{padding:8px;line-height:1.333333rem;vertical-align:top;border-top-width:1px;border-top-style:solid;border-top-color:rgb(221,221,221);}
tableth{font-weight:bold;}
tabletheadth{vertical-align:bottom;}
tablecaption+theadtr:first-childth,tablecaption+theadtr:first-childtd,tablecolgroup+theadtr:first-childth,tablecolgroup+theadtr:first-childtd,tablethead:first-childtr:first-childth,tablethead:first-childtr:first-childtd{border-top-width:0px;}
tabletbody+tbody{border-top-width:2px;border-top-style:solid;border-top-color:rgb(221,221,221);}
.task-list{padding:0px;}
.task-list-item{padding-left:1.6rem;}
.task-list-iteminput::before{content:'√';display:inline-block;width:1.33333333rem;height:1.6rem;vertical-align:middle;text-align:center;color:rgb(221,221,221);background-color:rgb(254,254,254);}
.task-list-iteminput:checked::before,.task-list-iteminput[checked]::before{color:inherit;}
.md-tag{color:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-variant:inherit;font-weight:inherit;line-height:inherit;}
#writepre.md-meta-block{min-height:35px;padding:0.5em1em;}
#writepre.md-meta-block{white-space:pre;background-color:rgb(248,248,248);border-width:0px1000px;color:rgb(153,153,153);border-left-color:rgb(248,248,248);border-left-style:solid;margin:-1.3333333333333rem-1000px2em;border-right-color:rgb(248,248,248);border-right-style:solid;padding-top:26px;padding-bottom:10px;line-height:1.8em;font-size:0.76em;padding-left:0px;background-position:initialinitial;background-repeat:initialinitial;}
.md-img-error.md-image>.md-meta{vertical-align:bottom;}
#write>h5.md-focus::before{top:2px;}
.md-toc{margin-top:40px;}
.md-toc-content{padding-bottom:20px;}
.outline-expander::before{color:inherit;font-size:14px;top:auto;content:'';font-family:FontAwesome;}
.outline-expander:hover::before,.outline-item-open>.outline-item>.outline-expander::before{content:'';}
#typora-source{font-family:Courier,monospace;color:rgb(106,106,106);}
.cm-s-typora-default.cm-header,.cm-s-typora-default.cm-property,.CodeMirror.cm-s-typora-defaultdiv.CodeMirror-cursor{color:rgb(66,139,202);}
.cm-s-typora-default.cm-atom,.cm-s-typora-default.cm-number{color:rgb(119,119,119);}

第一部分:SwiftyAPIs:NSUserDefaults01传统弊端(方法太长)Verbosity02使用下边改进:Thesubscript03类型的问题Types04可能为空Optionals05Existence:key是否存在?06Optionalassignment可选赋值07算法方面Arithmetic08简单的封装WrappingupResult最后的效果下一步Nextsteps第二部分:Statically-typedNSUserDefaults01不足之处Shortcomings02静态类型Thepowerofstatictyping03下标访问的进阶Subscriptsareawesome04走捷径Takingshortcuts05Theoptionalityconundrum缺省值的问题06更多的类型Moretypes07序列化的问题Archiving08结论Resultandconclusions

第一部分:SwiftyAPIs:NSUserDefaults

http://radex.io/swift/nsuserdefaults/
https://github.com/radex/SwiftyUserDefaultshttps://github.com/radex/SwiftyUserDefaults
InSwiftymethods,IwasarguingthatweshouldresistthetemptationtowriteSwiftthewayObjective-Cusedtobewritten.Iarguedthatweshoulddevelopnewapproachesandconventionstodesigninginterfaces,andthatweshould,onceandforall,abolishObjective-C’sexcessiveverbosity.

Althoughitwasverywellreceived,theissueI’vehadwiththeessayisthattheargumentsIwasmakingwereallintheabstract.Igavesomeexampleshereandthere,buttheywerelow-level,andmostlysilly.

AndsoIdecidedtostartaseriesofarticlescalledSwiftyAPIs,whereI’mgoingtotakesomeclassorcomponentfromanAppleframeworkandshowhowIwoulddesignitwithSwiftinmind.

Today,we’lldissectNSUserDefaultsandgiveitalittlemake-over.We’regoingtomakeitlessverbose,abitcleaner,andmoreconsistentwithotherclasses.We’llpolishitsroughedgesandwe’llmakeuseofSwift’scoolfeaturesalongtheway.

01传统弊端(方法太长)Verbosity

ItwouldbeexaggeratedtosaythatNSUserDefaults,asawhole,isparticularlyverbose.Butthere’sonebit,repeatedoverandover,that’sabitexcessive:NSUserDefaults.standardUserDefaults().Somanywordsandcharacters,beforeweevengettosaywhatwewanttofetchorsavethere.

NSUserDefaults.standardUserDefaults().stringForKey("color")
[code]​
NSUserDefaults.standardUserDefaults().setObject(NSDate(),forKey:"updatedAt")

WecouldaddanaliastostandardUserDefaults()—forexample,apropertynamedstandard,sharedorevenjusts.Theresultwouldbeabitbetter,butlet’strysomethingdifferent.

letDefaults=NSUserDefaults.standardUserDefaults()
[code]​
//Usage:
Defaults.integerForKey("launchCount")

Iadmit,it’salittleunconventionaltomakeaglobalvariablewithanupper-casename,butIthinkitlooksprettyneat.Itlooksasifitwasaclassname,notavariable,andwewereusingclassmethods.AndIthinkthat’sgreat,because99%ofthetime,wejustwanttousethestandarduserdefaults,andsowethinkofitasasingleglobalthing.(Anotherreasontomakethenameupper-caseisthatthelower-case“defaults”couldeasilyconflictwithlocalvariables.)

02使用下边改进:Thesubscript

Ithinkof NSUserDefaultssimilarlytoNSDictionary.It’ssomesortofakey-valuestructurethatservesasadatastore.Thereareobviousdifferences,likethefactthatyougenerallyuseonlyoneNSUserDefaultsobject,butstill,theprimarytwointeractionsarethesame:puttingstuffinunderakey;andfetchingstuffout.Andifthat’sthecase,itwouldmakesensetomodelthesyntaxsimilarly,makeitmoreconsistent.

Compare:

letdictionary=["launchCount":10]
[code]​
dictionary["launchCount"]
dictionary["color"]="red"
Defaults.integerForKey("launchCount")
Defaults.setObject("red",forKey:"color")

Wouldn’titbeniceifwecouldusethesamesquarebracketsubscriptingsyntaxinbothplaces?

Here’smyfirsttake:

extensionNSUserDefaults{
[code]subscript(key:String)->NSObject?{
get{
returnobjectForKey(key)
}
set{
setObject(newValue,forKey:key)
}
}
}

Theresult,atfirstglance,isquitenice:

Defaults["color"]
[code]​
Defaults["color"]="red"

(IhopeyoucanseethethirdreasonwhyImadeDefaultsaglobalvariable—it’snotpossibletodefinea“classsubscript”inSwift)

这里说不能定义
classsubscript
 ,应该是可以的(swift2.1)

03类型的问题Types

Thereisaseriousproblemwiththisimplementation,though. ThegetteralwaysreturnsNSObject.Thatwon’tflyinastaticallytypedlanguage. Wereallyneedamechanismtotellcompilerthetypeofdatawewanttofetch. That’sthenecessaryevilwheninterfacingoutsideofthetype-inferredworldofSwift.(我们需要让编译器知道返回的数据的类型)

Wecould,ofcourse,justcastthevaluetodesiredtype(e.g.as?NSString),butthenagain,weweresupposedtomaketheAPInicerandlessverbose.Here’sanalternativeIcameupwith:

extensionNSUserDefaults{
[code]classProxy{
privateletdefaults:NSUserDefaults
privateletkey:String
privateinit(_defaults:NSUserDefaults,_key:String){
self.defaults=defaults
self.key=key
}
varstring:String?{
returndefaults.stringForKey(key)
}
varint:Int{
returndefaults.integerForKey(key)
}
}
subscript(key:String)->Proxy{
returnProxy(self,key)
}
}
//Usage:
Defaults["color"].string
Defaults["launchCount"].int
Soessentially,sincewecan’tmakethesubscriptconveytypeinformation,Imadeitreturnanobjectthatrepresentstheactualvalueinuserdefaults.Andthenyouusethepropertiesofthatobjecttodotheactualfetching.

Notethatwedon’thavetodothiswhengoingtheotherwayaround. Whenwewanttoputstuffin,objectsalreadycarrythetypeinformationweneed(存储数据的时候,就知道数据是什么类型了).Wedon’tneedtobeexplicitaboutit.

extensionNSUserDefaults{
[code]subscript(key:String)->Any?{
get{
returnself[key]
}
set{
ifletv=newValueas?String{
setObject(v,forKey:key)
}elseifletv=newValueas?Int{
setInteger(v,forKey:key)
}
}
}
}

YoumightwonderwhyImadeaString->Any?subscriptinsteadofaddingseparatesubscriptsforeachacceptedtype.Well,Itrieddoingitthatwayanditdidn’twork.I’mnotsureifit’sabugoraconsequenceofhowtypesystemworksinSwift.Eitherway,thisimplementationworksjustfine.Youcanwrite

Defaults["color"]="red"

AndtheString->Any?setterwillbeexecuted.Butyoucanstillwrite:

[code]Defaults["color"].string
Andthecompilerwilldotherightthing—runString->Proxygetter.So,whilewehavetodefinesomegetterthatwillreturnAny?(youcan’tdefineasetter-onlysubscript),itwon’tactuallybeusedinpractice.

04可能为空Optionals

MakingtheAPIconciseandniceisjustonepartoftheequation.Butifthereareinconsistenciesorotherissueswiththeactualbehavior,you’vegotarealproblem.

Considerwhathappensifyoutrytofetchavaluefromuserdefaultsthatdoesn’texist:

[code]Defaults.stringForKey("color")//=>nil[code]​
Defaults.arrayForKey("lastPaths")//=>nil
Defaults.integerForKey("launchCount")//=>0
Defaults.boolForKey("loggingEnabled")//=>false

Huh?You’llgetnilinsomecases,butnotifwhatyouwanttofetchisanumberoraboolean—thenyou’llget0andfalse,respectively.It’sunderstandablewhytheydidthat—inObjective-C,thosetypesaredumb“primitivetypes”,andnilonlymakessenseforpointers. ButinSwift,anythingcanbeoptional.Solet’sbringconsistency!(swift的思想是返回的一个optionalNil)

extensionNSUserDefaults{
[code]classProxy{
varobject:NSObject?{
returndefaults.objectForKey(key)as?NSObject
}
varnumber:NSNumber?{
returnobjectas?NSNumber
}
varint:Int?{
returnnumber?.integerValue
}
varbool:Bool?{
returnnumber?.boolValue
}
}
}

Ifkeydoesn’texist,objectForKey()willreturnnil.Andifitdoesexist,butisn’tanumber,theoptionalcasttoNSNumberwillfail,andyou’llalsogetnil.

Andincaseswhenyoudowantthestandardbehavior,nilcoalescingcomestorescue:

Defaults["launchCount"].int??0
[code]​
Defaults["loggingEnabled"].bool??false
Defaults["color"].string??""


05Existence:key是否存在?

Believeitornot,NSUserDefaultsdoesn’thaveamethodforcheckingifakeyexists.ItonlytakesaquickGooglesearchtofigureoutthatobjectForKey()willreturnnilifavaluedoesn’texist.Still,thisshouldbejustanimplementationdetail,andthereshouldbeaproperinterfaceforit.

extensionNSUserDefaults{
[code]funchasKey(key:String)->Bool{
returnobjectForKey(key)!=nil
}
}

Andwhilewe’reatit,let’smentionremovingthings.ThereisaremoveObjectForKey()methodforit,butIdecidedtoshortenittoremove().Also,it’spossibletoremoveobjectsbysettingkey’svaluetonil,soIalsoaddedthatfeaturetoourString->Any?subscriptincasesomeonetrieddoingitthisway.

IwasalsoplayingwiththeideaofaddingthosetwofeaturestoourNSUserDefaults.Proxyobject.

extensionNSUserDefaults{
[code]classProxy{
varexists:Bool{
returndefaults.hasKey(key)
}
funcremove(){
defaults.removeObjectForKey(key)
}
}
}
//Usage:
Defaults["color"].exists
Defaults["color"].remove()

Iwasquitetornonthis.Ononehand,itplayswellwiththeideathatNSUserDefaults’ssubscriptreturnsanobjectrepresentingavalue.Ontheotherhand,theProxyclasswasjustanecessaryevil;checkingforexistenceandremovingobjectsisanoperationontheentiredatastructure,notanelementofit.Intheend,Isidedwiththelatterargument.

06Optionalassignment可选赋值

是什么意思呢?

就是这个符号:??

也就是如果前面有值,返回就是前面的值. 如果前面是值,就返回后面的值.

(当然后面的值被当成了一个闭包.)

InRuby,there’sausefuloperator,||=,forconditionalassignment.It’susedlikethis:

a=nil#=>nil
[code]​
a||="foo"#=>"foo"
a||="bar"#=>"foo"

Essentially,theright-handvalueisassignedtotheleft-handvariable,butonlyifit’sundefined,nilorfalse.

IthinkSwiftshouldalsohavethisoperator,onlythatI’dcallit?=,theoptionalassignmentoperator.Itwouldsetthevalueofavariable(anoptional)ifit’snil.

ThemagicofSwiftisthatyoucandefineitonyourown:

infixoperator?={
[code]associativityright
precedence90
}
func?=<T>(inoutvariable:T?,@autoclosureexpr:()->T){
ifvariable==nil{
variable=expr()
}
}

WhatdoesithavetodowithNSUserDefaults?Well,ifwecanoptionallyassignvaluestovariables,whynotoptionallyassignvaluestouserdefaultskeys?

func?=(proxy:NSUserDefaults.Proxy,@autoclosureexpr:()->Any){
[code]if!proxy.defaults.hasKey(proxy.key){
proxy.defaults[proxy.key]=expr()
}
}
//Usage:
Defaults["color"]//=>nil
Defaults["color"]?="white"//=>"white"
Defaults["color"]?="red"//=>"white"


很明显,这借鉴了自定义运算符.

NotethatthisisdifferentfromusingregisterDefaults.Theoptionalassignmentoperatorchangestheactualuserdefaultsdataandsavesthenewvaluestodisk.registerDefaultshassimilarbehavior,butitonlymodifiesthedefaultsinmemory.

07算法方面Arithmetic

Considerwhatyouhavetodoifyouwanttoincrementthevalueofauserdefault:

Defaults["launchCount"].int=(Defaults["launchCount"]??0)+1

Ifitwasavariable,youcouldmakeitshorterandclearerbyusingthe+=operator.Well,whosayswecan’tdothataswell?

[code]func+=(proxy:NSUserDefaults.Proxy,b:Int){[code]​
leta=proxy.defaults[proxy.key].int??0
proxy.defaults[proxy.key]=a+b
}
//Usage:
Defaults["launchCount"]+=1
Nice!Butheck,let’smakeitevenshorter:
postfixfunc++(proxy:NSUserDefaults.Proxy){
proxy+=1
}
//Usage:
Defaults["launchCount"]++
Voilà!

可以看到,值的改变,也是将实现封装到了一个自定义运算符中去了.



08简单的封装Wrappingup

Let’sfillintheblanks.IaddeddoublepropertytoNSUserDefaults.Proxythatworksjustlikeintandbool(usingconversionfromNSNumber,notthedoubleForKeymethod).Ididnotaddafloatproperty,becauseyou’resupposedtojustuseDoubleinSwift.

Iadded arraydictionary and data propertiesthatmirror arrayForKeydictionaryForKey and dataForKey.Ialsoaddeddateproperty,because NSDate isoneofthetypessupportedbyNSUserDefaults,andyetitdoesn’thaveabuilt-ingetter.

Andfinally,Iupdatedoursetter:

subscript(key:String)->Any?{
[code]​
get{
returnself[key]
}
set{
ifletv=newValueas?Int{
setInteger(v,forKey:key)
}elseifletv=newValueas?Double{
setDouble(v,forKey:key)
}elseifletv=newValueas?Bool{
setBool(v,forKey:key)
}elseifletv=newValueas?NSObject{
setObject(v,forKey:key)
}elseifnewValue==nil{
removeObjectForKey(key)
}else{
assertionFailure("Invalidvaluetype")
}
}
}

Onmyfirsttry,IdefinedthesubscripttoreturnAnyObject?,butwhenIaddedDoubleandBoolafterInt,Iwassurprisedthatitdidn’tworkproperly.Youcouldsetandfetchbackdoublesandbooleans,buttheywouldbeencodedasintegers.Turnsout,whenyoutrytopassnumbersorbooleansasAnyObject,theygetautomaticallymappedtoNSNumberunderthehood,whichcausedthisoddbehavior.SimplechangetoAnyfixedtheissue.

Result最后的效果

Let’slookatourbrandnewNSUserDefaultsAPIinitsfullglory:

//Fetchingdata
[code]​
Defaults["color"].string
Defaults["launchCount"].int??0
Defaults["lastPaths"].array?.firstObject
//Settingdata
Defaults["color"]="red"
Defaults["firstLaunchAt"]?=NSDate()
Defaults["launchCount"]++
Defaults["totalTime"]+=3600
//Checking&removing
if!Defaults.hasKey("hotkey"){
Defaults.remove("hotkeyOptions")
}


下一步Nextsteps

Now,thejourneydoesn’tstophere.Althoughwemadegreatprogressonsyntaxandnoisereduction,thisisn’tthebestwecando.CheckoutStatically-typedNSUserDefaultstoseehowwecantakeUserDefaultstothenextlevel,simplifytheiruse,andgetcompilerchecksforfreebyadoptingtyped,statically-definedkeys.
https://github.com/radex/SwiftyUserDefaults

第二部分:Statically-typedNSUserDefaults

Ayearago,notlongafterSwiftbecameathing,Inoticedatendencyamongprogrammerstowriteitthewayyou’dwriteObjective-C.IthoughtthatSwiftwasadifferentlanguage,withdifferentsyntax,philosophyandcapabilities,andsoweshoulddevelopnewconventionsandapproachestoit.IrespondedwithSwiftymethods,whereIarguedforabetter,clearerwayofnamingthings.Then,sometimelater,IstartedtheSwiftyAPIsseriestoputthoseideasinpracticeandexplorehowtodesigneasy-to-useinterfaces.

Inthefirstarticleinsaidseries,wetooktheNSUserDefaultsAPI:

NSUserDefaults.standardUserDefaults().stringForKey("color")
[code]​
NSUserDefaults.standardUserDefaults().setObject("red",forKey:"color")

…andwemadeitlooklikethis:

Defaults["color"].string
[code]​
Defaults["color"]="red"

Theresultwasclearer,lessverbose,andnicerlookingthantheoriginal.WefixedsomeconsistencyissuesandmadeitallfitbetterwithSwift.Thisfeltlikeasignificantimprovement.

Andyet,asI’vebeenactuallyusingthenewAPI,andlearningSwiftalongtheway,Irealizedthatitwasn’tactuallyverySwiftyatall. IdrewinspirationfromRuby’sandSwift’ssyntaxindesigningit,andthatmatters,butwedidn’timproveitonasemanticlevelatall. WeonlyputaSwiftycoatofpaintonafundamentallyObjective-C-likemechanism.(在OC的机制上披上了一层swift的外衣)

语法层次上的提示:来自Ruby的灵感

01不足之处Shortcomings

Ofcourse,“notSwifty”isn’tagoodreasontostartfromscratch.FamiliaritymakesanAPIeasiertolearn,butwedon’twanttobedogmaticaboutit.Wedon’tjustwantaSwift-likedesign,wewantwhatworksbestinSwift,period.Sohere’safewissueswithNSUserDefaults:

Supposeyouhaveapreferenceforuser’sfavoritecolor:

Defaults["color"]="red"
[code]​
//elsewhereintheapp:
Defaults["colour"].string//=>nil

Ooops,butyou’vemadeatypointhekeyname.Boom,that’sabug.

 将key拼写错误了.

Let’ssayyoukeepadateobjectinthedefaults:

Defaults["deadline"]=NSDate.distantFuture()
[code]​
Defaults["deadline"].data//=>nil
Thistimeyoumistypedthedategetter,andonceagain,youhaveabug.Unlikelytohappen?Probably.Butwhydowehavetospecifythereturntypeeverysingletimewewanttofetch,anyway?It’skindofannoying.

将getter方法拼错了.

Here’sonemore:

Defaults["deadline"]=NSData()
[code]​
Defaults["deadline"].date//=>nil

Clearlywemeant“rightnow”,not“emptydata”.Ohwell.

Whataboutthis:

Defaults["magic"]=3.14
[code]​
Defaults["magic"]+=10
Defaults["magic"]//=>13
Theonlyreasonwhy+=worksatallisbecausewedefineditasamagicoperator.Butitcanonlyinfertypes(IntorDouble)fromtheargumentpassed.Soifyoupassaninteger10,theresultwillbestoredasaninteger,cuttingoffthefractionalpart.Andwehaveabug.

Youmightbethinkingthatthesearepurelytheoreticalproblemsthatwouldn’tariseintherealworld.Butthinktwice. Thesearethesamekindsofbugsasmistypingavariableormethodname,orpassingaparameterofthewrongtype. Thesethingshappenallthetime. 

key值写错
方法名写错
传入了错误的参数类型
Ifyouworkwithacompiled,statically-typedlanguage,yougrowtoappreciategettingfeedbackfromthecompilermorequicklythanbytestingit. Moreimportantly,investmentincompile-timecheckspaysdividendsovertime. (在编译期间做更多的事情比在测试时候发现问题更好.)Thisisn’tjustforyouwritingthecodeforthefirsttime,thisisforpreventingbugsasyouchangeandrefactoritlater.Thisisaboutprotectingthefuture-youfrompast-you.

02静态类型Thepowerofstatictyping

Therootofallevilhereisthatthereisnostatically-definedstructureoftheuserdefaults.

Withthepreviousredesign,IrecognizedthisproblemandtriedtoslaptypesonthesubscriptbyhavingitreturnaProxyobjectprovidingtypedaccessors.This,inmyestimation,wasbetterthanmanuallycastingAnyObjectorhavinggettermethodsinsteadofasubscript.

Butitwasahack,notasolution.TomakeameaningfulimprovementtotheAPI, weneedtocentralizeinformationaboutuserdefaultkeysandmakeitavailabletothecompiler.

现在的问题在于怎么把keys集中化,暴露给编译器

How?Considerhowpeoplesometimesdefinestringkeysaheadoftimetoavoidtyposandgetnameauto-completionforfree:

[code]x
letcolorKey="color"
借鉴定义常量字符串:

1.避免了拼写错误

2.编译器自动补全

Whatifwedojustthat,butwithtypeinformation?

[code]x
classDefaultsKey<ValueType>{
letkey:String
init(_key:String){
self.key=key
}
}
letcolorKey=DefaultsKey<String?>("color")

Wewrappedthekeynameinanobject,andembeddedthevalue’stypeinagenerictypeparameter.

上面的代码做了什么:

将一个key封装到一个类对象中了.
key对应的值呢?被嵌入到了类需要使用的泛型参数.
 NowwecandefineanewsubscriptonNSUserDefaultsthatacceptsthesekeys:

[code]x
extensionNSUserDefaults{
subscript(key:DefaultsKey<String?>)->String?{
get{returnstringForKey(key.key)}
set{setObject(newValue,forKey:key.key)}
}
}

Andhere’stheresult:

[code]x
letkey=DefaultsKey<String?>("color")
Defaults[key]="green"
Defaults[key]//=>"green",typedasString?

Boom,assimpleasthat.Morerefinementstothesyntaxandfunctionalityarepossible(aswe’llsoonexplore),butwithThisOneSimpleTrick™,wefixedmanyofourproblems.Wecan’teasilymakeakeynametypo,becauseit’sdefinedonlyonce.Wecan’tassignavalueofthewrongtype,becausethecompilerwon’tletus.Andwedon’tneedtowrite.string,becausethecompileralreadyknowswhatwewant.

解决的问题:

1)只定义了一次,减少拼写错误

2)赋值时候编译器做类型检查

Bytheway.WeshouldprobablyjustusegenericstodefinetheNSUserDefaultssubscriptsinsteadoftypingalloftheneededvariantsbyhand.Well,thatwouldbewonderful,butsadly,theSwiftcompilerdoesn’tsupportgenericsubscriptsrightnow.Sigh.

上面的下标访问只支持字符串,如果我们不想全部手动写,使用泛型,但是问题是:

swift编译器不支持下标访问的时候使用泛型.

 Thesquarebracketsmightbenice,butlet’snotbestupidaboutthesyntax,andjustmakegenericmethodsforsettingandgetting…Right?

Ahh,butyouhaven’tseenwhatsubscriptscandoyet!

03下标访问的进阶Subscriptsareawesome

Considerthis:

[code]x
vararray=[1,2,3]
array.first!+=10
编译不通过,原因:值类型的数据,传递的只是拷贝,离开表达式后就不存在了==>对比变量

Itwon’tcompile.We’retryingtoincrementanintegerinsideanarray,butthismakesnosenseinSwift.Integershavevaluesemantics.They’reimmutable.Youcan’tjustchangethemwhenthey’rereturnedfromsomeplaceelse,becausetheydon’treallyexistoutsideofthatexpression.They’remerelytransientcopies.

It’svariablesthathavetheconceptofmutability.Whenyoudo:

varnumber=1
[code]​
number+=10

You’renotactuallychangingthevalue,you’rechangingthevariable—assigninganewvaluetoit.

Buttakealookatthis:

vararray=[1,2,3]

array[0]+=10

array//=>[11,2,3]

Thisjustworks.Itdoesexactlywhatyou’dexpect,butit’sactuallynotobviouswhy.Huh!

奇怪了,上面的array.first就不能编译通过,但是array[0]却能达到预期的效果,这是为什么?

See,subscriptshavesemanticsthatplayreallywellwithvaluetypesinSwift.Thereasonwhyyoucanchangevaluesinanarraythroughthesubscriptisbecauseitdefinesbothagetterandasetter. Thisallowsthecompilertorewritearray[0]+=10toarray[0]=array[0]+10.Ifyoumakeagetter-onlysubscript,itwon’twork.

原因在于下标访问方法定义了getter和setter两个方法,而不仅仅是一个只读的getter方法.

Again,thisisn’tsomemagiconArray’spart.Thisisaconsequenceofcarefullydesignedsemanticsofsubscripts.Wegettheexactsamebehaviorforfree,anditallowsustodothingslike:

[code]x
Defaults[launchCountKey]++
Defaults[volumeKey]-=0.1
Defaults[favoriteColorsKey].append("green")
Defaults[stringKey]+="…caneasilybeextended!"

Youknow,it’sfunny.In1.0,weusedsubscriptspurelyfortheirsyntacticvalue(becauseitmadeuserdefaultslooklikeadictionary),andmissedoutcompletelyonsemanticbenefits.

语法值

语义

Weaddedafewoperatorslike+=and++,buttheirbehaviorwasdangerousanditreliedonmagicimplementations.Here,byencapsulatingtypeinformationinthekey,andbydefiningboththesubscriptgetterandsetter,allofthisactuallyjustworks.

04走捷径Takingshortcuts

Onenicethingaboutusingplainoldstringkeysisthatyoucouldjustusetheminplace,withouthavingtocreateanyintermediateobjects.

Obviously,creatingthekeyobjecteverytimewewanttouseitdoesn’tmakemuchsense.Thiswouldbeawfullyrepetitiveandwouldeliminatethebenefitsofstatictyping.Solet’sseehowwecanorganizeourdefaultskeysbetter.

Onewayistodefinekeysattheclasslevelwhereweusethem:

[code]x
classFoo{
structKeys{
staticletcolor=DefaultsKey<String>("color")
staticletcounter=DefaultsKey<Int>("counter")
}
funcfooify(){
letcolor=Defaults[Keys.color]
letcounter=Defaults[Keys.counter]
}
}

ThisseemstoalreadybeastandardSwiftpracticewithstringkeys.

AnotherwayistotakeadvantageofSwift’s implicitmemberexpressions.(暗含的成员表达式)Themostcommonuseofthisfeatureiswithenumcases.(最常见的是枚举)

WhencallingamethodthattakesaparameterofenumtypeDirection,youcanpass.Right,andthecompilerwillinferthatyoumeantDirection.Right.Lessknownisthefactthatthisalsoworkswithanystaticmembersofargument’stype.SoyoucancallamethodtakingaCGRectwith.zeroRectinsteadoftypingCGRect.zeroRect.

举个例子,有个方法带一个参数:Direction枚举类型,

如果直接传入
.Righ
,那么编译器认为你传入的是
Direction.Right


其实,这种情况也适用于静态成员变量作为函数参数的情况.

例如,带一个CGRect的参数的时候,可以传入
.zeroRect
,而不用传入
CGRect.zeroRect




enumDirection{
[code]caseLeft,Right,Up,Down
}
classPerson{
classfuncwalkTo(direction:Direction){
print("nowwalkto\(direction)")
}
}
Person.walkTo(.Right)
//nowwalktoRight


Infact,wecandothesamethingherebydefiningourkeysasstaticconstantsonDefaultsKey.Well,almost.Weneedtodefineitslightlydifferentlytoworkaroundacompilerlimitationatthemoment:

[code]x
classDefaultsKeys{}
classDefaultsKey<ValueType>:DefaultsKeys{...}
extensionDefaultsKeys{
staticletcolor=DefaultsKey<String>("color")
}

Andnow,ohwow!

[code]x
Defaults[.color]="red"
Isn’tthiscool?Atcallersite,verbosityisnowlowerthanwiththetraditionalstringly-typedapproach.Wedolessworkwritingandreadingthecode,andgetallthebenefitsalmostforfree.

(Oneshortcomingofthistechniqueisthatthereisnomechanismfornamespacing.Inalargeproject,itmightbebettertogowiththeKeysstructapproach.)

不足之处:工程变大的时候,由于没有命名空间,更好的解决方法是使用结构体

05Theoptionalityconundrum缺省值的问题

IntheoriginalAPIredesign,wemadeallgettersreturnoptionals.IdislikedthelackofconsistencyinhowNSUserDefaultstreatsdifferenttypes.Forstrings,amissingvaluewouldgetyounil,butfornumbersandbooleans,you’dget0andfalse.

Thedownsideofthisapproach,asIquicklyrealized,wasverbosity.Muchofthetime,wedon’treallycareaboutthenilcase—wejustwanttogetsomedefaultvalue.Whensubscriptsreturnanoptional,wehavetocoalesceiteverytimewefetch.

默认值返回的问题:

string类型,返回nil

数值类型,返回0

布尔类型,返回false

AsolutiontothisproblemwasproposedbyOlegKokhtenko.Inadditiontothestandard,Optional-returninggetters,anothersetofgetterswasadded.Thosehavenamesendingwith-Valueandreplacenilwithadefaultvaluethatmakessenseforthetype:

Defaults["color"].stringValue//defaultsto""
[code]​
Defaults["launchCount"].intValue//defaultsto0
Defaults["loggingEnabled"].boolValue//defaultstofalse
Defaults["lastPaths"].arrayValue//defaultsto[]
Defaults["credentials"].dictionaryValue//defaultsto[:]
Defaults["hotkey"].dataValue//defaultstoNSData()
通过对可能为空的值,再次进行封装,给出一个默认值.

Wecandothesamethingunderthestatictypingregimebyprovidingsubscriptvariantsforoptionalandnon-optionaltypes,likeso:

[code]x
extensionNSUserDefaults{
subscript(key:DefaultsKey<NSData?>)->NSData?{
get{returndataForKey(key.key)}
set{setObject(newValue,forKey:key.key)}
}
subscript(key:DefaultsKey<NSData>)->NSData{
get{returndataForKey(key.key)??NSData()}
set{setObject(newValue,forKey:key.key)}
}
}
在get方法的时候,给出了默认值.

Ilikeit,becausewe’renotrelyingonconvention(typeandtypeValue)toconveynullability.We’reusingtheactualtypewhendefiningauserdefaultskey,andthenlettingthecompilerhandletherest.

06更多的类型Moretypes

Fillingintheblanks,Iextendedthescopeofsupportedtypesbyaddingsubscriptsforallofthese:String,Int,Double,Bool,NSData,[AnyObject],[String:AnyObject],NSString,NSArray,NSDictionary,andtheiroptionalvariants(plusNSDate?,NSURL?,andAnyObject?,whichdon’thaveanon-optionalcounterpart,becausethereisn’tareasonabledefaultvalue).

Andyes,strings,dictionariesandarrayshavebothSwiftandFoundationvariants.NativeSwifttypesarepreferrable,buttheydon’thaveallofthecapabilitiesoftheirNScounterparts.Ifsomeonewantsthose,Iwanttomakethateasy.

Andspeakingofarrays,whylimitourselvestountypedarraysonly?Inmostcases,arraysstoredinuserdefaultsaregoingtohavehomogenouselementsofasimpletype,likeString,Int,orNSData.

Sincewecan’tdefinegenericsubscripts,we’llcreateapairofgenerichelpermethods:

extensionNSUserDefaults{
[code]​
funcgetArray<T>(key:DefaultsKey<[T]>)->[T]{
returnarrayForKey(key.key)as?[T]??[]
}
funcgetArray<T>(key:DefaultsKey<[T]?>)->[T]?{
returnarrayForKey(key.key)as?[T]
}
}

…andthencopy&pastethisstubforalltypeswe’reinterestedin:

extensionNSUserDefaults{
[code]​
subscript(key:DefaultsKey<[String]?>)->[String]?{
get{returngetArray(key)}
set{set(key,newValue)}
}
}

Andnowwecandothis:

letkey=DefaultsKey<[String]>("colors")
[code]Defaults[key].append("red")
letred=Defaults[key][0]

SubscriptingthearrayreturnsString,andappendingtoitverifiesatcompiletimethatyouarepassingastring.Winforconvenience,winforsafety.

07序列化的问题Archiving

AlimitationofNSUserDefaultsisthatitonlysupportsahandfuloftypes.AcommonworkaroundforstoringcustomtypesistoserializethemwithNSKeyedArchiver.

Let’smakethiseasytodo.SimilarlytothegetArrayhelper,Idefinedgenericarchive()andunarchive()methods.Withthose,youcaneasilydefineastubsubscriptforwhateverNSCoder-complianttypeyouwant:

extensionNSUserDefaults{
[code]​
subscript(key:DefaultsKey<NSColor?>)->NSColor?{
get{returnunarchive(key)}
set{archive(key,newValue)}
}
}
extensionDefaultsKeys{
staticletcolor=DefaultsKey<NSColor?>("color")
}
Defaults[.color]//=>nil
Defaults[.color]=NSColor.whiteColor()
Defaults[.color]//=>w1.0,a1.0
Defaults[.color]?.whiteComponent//=>1.0

Clearlynotperfect,butit’snicethatwithjustafewlinesofboilerplate,wecanmakeanycustomtypeworknaturallywithNSUserDefaults.

08结论Resultandconclusions

Andthat’sit!Here’showournewAPIpresentsitself:

//Definekeysaheadoftime
[code]​
extensionDefaultsKeys{
staticletusername=DefaultsKey<String?>("username")
staticletlaunchCount=DefaultsKey<Int>("launchCount")
staticletlibraries=DefaultsKey<[String]>("libraries")
staticletcolor=DefaultsKey<NSColor?>("color")
}
//Usethedotsyntaxtoaccessuserdefaults
Defaults[.username]
//Definekey’stypewithout?tousedefaultvaluesinsteadofoptionals
Defaults[.launchCount]//Int,defaultsto0
//Modifyvaluetypesinplace
Defaults[.launchCount]++
Defaults[.volume]+=0.1
Defaults[.strings]+="…caneasilybeextended!"
//Useandmodifytypedarrays
Defaults[.libraries].append("SwiftyUserDefaults")
Defaults.libraries+="2.0"
//Easilyworkwithcustomserializedtypes
Defaults[.color]=NSColor.whiteColor()
Defaults[.color]?.whiteComponent//=>1.0
Statictypingdoesn’thurtinSwift


Ihopeyoucanseethesignificantbenefitsofthisstatically-typedapproach.Yes,there’sasmallpricetooptin.WeneedtopayourrespectstothetypesystemanddefineaDefaultsKeyaheadoftime.Butinreturn,thecompilerbringsusasackfullofpresents:

compile-timechecks(keyname,read/writetypesafety)编译器类型检测
autocompletionofkeynames自动补全
inferredtypes—noneedtotype.stringormanuallycastAnyObject.类型推断
wecaneasilymanipulatevaluetypesstoredinuserdefaultswithoutintermediatestepsormagicoperators
consistency—weirdkeysaside,Defaultslooksandactslikeatypeddictionary一致性
AndmorebenefitsmightbepossibleasSwiftadvances.

TrulySwiftyAPIstakeadvantageofstatictyping.Idon’tmeantobedogmaticaboutit—therearesurelyproblemsthatarebestsolvedindifferentways.Butdoconsiderthebenefitsbeforeyoufallbacktothewayyou’ddothingsinObjective-CorJavaScript.Remember,thisisn’tyourgrandfather’sstatictypingwe’retalkingabout.ArichtypesystemSwifthasusuallyallowsforreallyexpressiveandnice-to-useAPIswithlittleoverhead.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: