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:NSUserDefaults
http://radex.io/swift/nsuserdefaults/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.[code]NSUserDefaults.standardUserDefaults().stringForKey("color")
NSUserDefaults.standardUserDefaults().setObject(NSDate(),forKey:"updatedAt")
WecouldaddanaliastostandardUserDefaults()—forexample,apropertynamedstandard,sharedorevenjusts.Theresultwouldbeabitbetter,butlet’strysomethingdifferent.
[code]letDefaults=NSUserDefaults.standardUserDefaults()
//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:
[code]letdictionary=["launchCount":10]
dictionary["launchCount"]
dictionary["color"]="red"
Defaults.integerForKey("launchCount")
Defaults.setObject("red",forKey:"color")
Wouldn’titbeniceifwecouldusethesamesquarebracketsubscriptingsyntaxinbothplaces?
Here’smyfirsttake:
[code]subscript(key:String)->NSObject?{extensionNSUserDefaults{
get{
returnobjectForKey(key)
}
set{
setObject(newValue,forKey:key)
}
}
}
Theresult,atfirstglance,isquitenice:
[code]Defaults["color"]
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:
[code]classProxy{extensionNSUserDefaults{
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"].intSoessentially,sincewecan’tmakethesubscriptconveytypeinformation,Imadeitreturnanobjectthatrepresentstheactualvalueinuserdefaults.Andthenyouusethepropertiesofthatobjecttodotheactualfetching.
Notethatwedon’thavetodothiswhengoingtheotherwayaround. Whenwewanttoputstuffin,objectsalreadycarrythetypeinformationweneed(存储数据的时候,就知道数据是什么类型了).Wedon’tneedtobeexplicitaboutit.
[code]subscript(key:String)->Any?{extensionNSUserDefaults{
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]
Andthecompilerwilldotherightthing—runString->Proxygetter.So,whilewehavetodefinesomegetterthatwillreturnAny?(youcan’tdefineasetter-onlysubscript),itwon’tactuallybeusedinpractice.
04可能为空Optionals
MakingtheAPIconciseandniceisjustonepartoftheequation.Butifthereareinconsistenciesorotherissueswiththeactualbehavior,you’vegotarealproblem.Considerwhathappensifyoutrytofetchavaluefromuserdefaultsthatdoesn’texist:
[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)
[code]classProxy{extensionNSUserDefaults{
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:
[code]Defaults["launchCount"].int??0
Defaults["loggingEnabled"].bool??false
Defaults["color"].string??""
05Existence:key是否存在?
Believeitornot,NSUserDefaultsdoesn’thaveamethodforcheckingifakeyexists.ItonlytakesaquickGooglesearchtofigureoutthatobjectForKey()willreturnnilifavaluedoesn’texist.Still,thisshouldbejustanimplementationdetail,andthereshouldbeaproperinterfaceforit.[code]funchasKey(key:String)->Bool{extensionNSUserDefaults{
returnobjectForKey(key)!=nil
}
}
Andwhilewe’reatit,let’smentionremovingthings.ThereisaremoveObjectForKey()methodforit,butIdecidedtoshortenittoremove().Also,it’spossibletoremoveobjectsbysettingkey’svaluetonil,soIalsoaddedthatfeaturetoourString->Any?subscriptincasesomeonetrieddoingitthisway.
IwasalsoplayingwiththeideaofaddingthosetwofeaturestoourNSUserDefaults.Proxyobject.
[code]classProxy{extensionNSUserDefaults{
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:
[code]a=nil#=>nil
a||="foo"#=>"foo"
a||="bar"#=>"foo"
Essentially,theright-handvalueisassignedtotheleft-handvariable,butonlyifit’sundefined,nilorfalse.
IthinkSwiftshouldalsohavethisoperator,onlythatI’dcallit?=,theoptionalassignmentoperator.Itwouldsetthevalueofavariable(anoptional)ifit’snil.
ThemagicofSwiftisthatyoucandefineitonyourown:
[code]associativityrightinfixoperator?={
precedence90
}
func?=<T>(inoutvariable:T?,@autoclosureexpr:()->T){
ifvariable==nil{
variable=expr()
}
}
WhatdoesithavetodowithNSUserDefaults?Well,ifwecanoptionallyassignvaluestovariables,whynotoptionallyassignvaluestouserdefaultskeys?
[code]if!proxy.defaults.hasKey(proxy.key){func?=(proxy:NSUserDefaults.Proxy,@autoclosureexpr:()->Any){
proxy.defaults[proxy.key]=expr()
}
}
//Usage:
Defaults["color"]//=>nil
Defaults["color"]?="white"//=>"white"
Defaults["color"]?="red"//=>"white"
07算法方面Arithmetic
Considerwhatyouhavetodoifyouwanttoincrementthevalueofauserdefault:Defaults["launchCount"].int=(Defaults["launchCount"]??0)+1
Ifitwasavariable,youcouldmakeitshorterandclearerbyusingthe+=operator.Well,whosayswecan’tdothataswell?
[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 array, dictionary and data propertiesthatmirror arrayForKey, dictionaryForKey and dataForKey.Ialsoaddeddateproperty,because NSDate isoneofthetypessupportedbyNSUserDefaults,andyetitdoesn’thaveabuilt-ingetter.
Andfinally,Iupdatedoursetter:
[code]subscript(key:String)->Any?{
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:[code]//Fetchingdata
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.第二部分:Statically-typedNSUserDefaults
Ayearago,notlongafterSwiftbecameathing,Inoticedatendencyamongprogrammerstowriteitthewayyou’dwriteObjective-C.IthoughtthatSwiftwasadifferentlanguage,withdifferentsyntax,philosophyandcapabilities,andsoweshoulddevelopnewconventionsandapproachestoit.IrespondedwithSwiftymethods,whereIarguedforabetter,clearerwayofnamingthings.Then,sometimelater,IstartedtheSwiftyAPIsseriestoputthoseideasinpracticeandexplorehowtodesigneasy-to-useinterfaces.Inthefirstarticleinsaidseries,wetooktheNSUserDefaultsAPI:
[code]NSUserDefaults.standardUserDefaults().stringForKey("color")
NSUserDefaults.standardUserDefaults().setObject("red",forKey:"color")
…andwemadeitlooklikethis:
[code]Defaults["color"].string
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的外衣)
01不足之处Shortcomings
Ofcourse,“notSwifty”isn’tagoodreasontostartfromscratch.FamiliaritymakesanAPIeasiertolearn,butwedon’twanttobedogmaticaboutit.Wedon’tjustwantaSwift-likedesign,wewantwhatworksbestinSwift,period.Sohere’safewissueswithNSUserDefaults:Supposeyouhaveapreferenceforuser’sfavoritecolor:
[code]Defaults["color"]="red"
//elsewhereintheapp:
Defaults["colour"].string//=>nil
Ooops,butyou’vemadeatypointhekeyname.Boom,that’sabug.
[code]Defaults["deadline"]=NSDate.distantFuture()
Defaults["deadline"].data//=>nilThistimeyoumistypedthedategetter,andonceagain,youhaveabug.Unlikelytohappen?Probably.Butwhydowehavetospecifythereturntypeeverysingletimewewanttofetch,anyway?It’skindofannoying.