CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares
DesenvolvendoaplicaçõesparaiOSusandoObjective-C
DESENVOLVIMENTO DE APLICAÇÕES PARA IOS USANDO OBJECTIVE-C INTRODUÇÃO C APÍTULO1 - CONHECENDOOBJECTIVE-C IMAGEM1 – SELEÇÃODEPROJETOSNO XCODE IMAGEM2 – CRIANDOOPROJETONO XCODE IMAGEM3 – VISUALIZAÇÃOINICIALDOPROJETO IMAGEM4 – CRIANDOOTARGETQUEVAIRODAROSNOSSOSTESTES IMAGEM5 – ADICIONANDOOEXECUTÁVELDAAPLICAÇÃOCOMODEPENDÊNCIAAOTARGET TEST IMAGEM6 – CRIANDOANOSSAPRIMEIRACLASSEEM OBJECTIVE-C IMAGEM7 – CRIANDOANOSSAPRIMEIRACLASSEEM OBJECTIVE-C IMAGEM8 –VISUALIZAÇÃODONAVEGADORDEARQUIVOSCOMACLASSE CONTACRIADA. LISTAGEM1 – CONTA.H LISTAGEM2 – CONTA.M LISTAGEM3 – CONTA.H LISTAGEM4 – CONTA.M CRIANDOONOSSOPRIMEIROTESTEUNITÁRIO IMAGEM9 – CRIANDOOGRUPO “TEST” IMAGEM10 – CRIANDOACLASSEDETESTES IMAGEM11 – SELECIONANDOOSTARGETSDACLASSEDETESTES IMPLEMENTANDOONOSSOPRIMEIROTESTE LISTAGEM5 – CONTATEST.M IMAGEM12 – ALTERANDOOTARGETPADRÃOPARA TEST IMAGEM13 – DRAGEDROPDOARQUIVO “CONTA.M”EMTEST IMAGEM14 – INDICAÇÃODEERROSDEBUILDDO XCODE IMAGEM15 –TELADEERROSNOBUILDDO XCODE LISTAGEM6 – CONTA.H LISTAGEM7 – CONTA.M LISTAGEM8 – CONTATEST.H LISTAGEM9 – CONTATEST.M CRIANDOUM “CONSTRUTOR”PARAONOSSOOBJETOCONTA LISTAGEM10 – CONTA.H–DECLARANDOOMÉTODOINIT WITHSALDO LISTAGEM11 – CONTA.M DEFININDOPROPRIEDADESAUTOMATICAMENTENOSOBJETOS LISTAGEM12 – CONTA.HCOMASNOVASPROPRIEDADESDEFINIDAS LISTAGEM13 – CONTA.MCOMADEFINIÇÃODASPROPRIEDADESEGERENCIAMENTODEMEMÓRIA LISTAGEM14 – CONTATEST.MCOMOCÓDIGODEGERENCIAMENTODEMEMÓRIA
1 2 3 3 4 5 6 7 8 9 9 9 11 11 12 12 13 13 14 14 14 16 16 17 18 18 19 20 20 22 22 23 23 24 25 26
1
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Introdução
Estematerialtemcomoobjetivoservirdereferênciaparaocursode desenvolvimentodeaplicaçõesusandoObjective-CeXCodeparaiOS,osistema operacionalparadispositivosmóveisdaApple,comoiPhones,iPodseiPads.Ele fazpartedomaterialcomplementarparaasaulasexpositivasdocursode desenvolvimentoparaiOS. ParaseguiressematerialvocêprecisadeumcomputadorcomMacOSeXCode instalados,alémdoSDKparadesenvolvimentodeaplicaçõesparaiOS.Omaterial tambémassumequevocêjátemexperiênciacomodesenvolvimentode softwareemaomenosumalinguagemorientadaaobjetos. Conceitosbásicosdeprogramaçãoorientadaaobjetoscomovariáveisde instância,métodos,construtores,herança,encapsulamentonãovãoser explicados,assume-sequequemestálendoomaterialjátemconhecimentode todosessesconceitosquesãolugarcomumemqualquerlinguagemde programaçãoorientadaaobjetos. Emváriaspartesdomaterialvocêvaiencontrarafala“Abraomenucontextual doitemX”ou“cliquecomobotãodireitoemX”,issoquerdizerusarobotão direitodomouse(emummousecomom)fazer“Control+Click”noitem selecionadoou,sevocêestiverusandoumtrackpadmulti-touch,clicarcomos doisdedosaomesmotempo.
2
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Capítulo1-ConhecendoObjective-C
Ocódigofontedesteprimeirocapítuloestádisponívelem- https://github.com/mauricio/capitulo-1-curso-ios Objective-Céumalinguagemdeprogramaçãoorientadaaobjetosdeusogerale éalínguapadrãoparaodesenvolvimentodeaplicaçõesparaoMacOSehoje tambémparaoiOS,ambossistemasoperacionaisdesenvolvidospelaApple.A linguageméderivadadiretamentedoC,comalgumascaracterísticasde Smalltalk,comoousodeparâmetrosdentrodonomedométodoemvezdeem umaseçãodeparâmetrosnomesmo. VamosfazeragoradarosnossosprimeirospassoscomoXCode,criandoum projeto.AbraoXCode,essadeveseraprimeirajanelaquevocêvaiver: Imagem1–SeleçãodeprojetosnoXCode
Nessapágina,selecione“CreateanewXcodeproject”,aquiestáapróximajanela quevocêvaiver:
3
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Imagem2–CriandooprojetonoXcode
Nesseprimeiromomento,vamosiniciarcomumaaplicaçãoparaMacOS,pra entenderofuncionamentodalinguagemenosacostumarmoscomoXcodecomo ferramenta.Apósselecionar“MacOSX”->“Application”->“CommandLine Tool”,alémdeselecionar“Foundation”nocampodeseleção.Dêonome “AprendendoObjectivec”aoprojeto. Comoprojetocriado,vocêdeveverumajanelacomoessaagora:
4
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Imagem3–Visualizaçãoinicialdoprojeto
UmfatorinteressanteaoseiniciarodesenvolvimentousandoXcodeéque mesmoseparecendocompastas,“Source”,“Documentation”,“External frameworksandlibraries”e“Products”nãosãopastas,mas“agrupamentos”de conteúdo.Sevocêforatéapastaqueestáoprojeto,vaiperceberquenão existemdiretóriosequivalentesaeles,issoaconteceporqueoXcodeorganizaos arquivosapenaslogicamenteenãofisicamentedentrodoprojeto. Comonossoprojetocriado,vamoscriarumtargetparaostestesunitáriosque vamosescreverduranteoexemplo.Prafazerisso,cliquecomobotãodireitoou “Control+Click”nomarcador“Targets”,sigapara“Add”,depois“NewTarget”. Vocêdeveveratelaabaixo:
5
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Imagem4–Criandootargetquevairodarosnossostestes
Aopartirprapróximatelavocêvaidefinironomedotarget,coloqueonome Test.Assimqueotargetforcriado,elevaiabrirajaneladeopçõesdomesmo, nela,selecioneaaba“General”,cliquenobotão“+”,vocêdeveverentãouma janelacomoaimagemlogoabaixo,selecione“AprendendoObjectivec”eclique em“AddTarget”.
6
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Imagem5–Adicionandooexecutáveldaaplicaçãocomodependênciaaotarget Test
Estamosagorafinalmenteprontospracomeçaraescreverocódigodoprojeto. Paraentenderasconstruçõesbásicasdalinguagem,vamoscriarumaclasse Contaqueguardeosdadosdeagência,númerodeconta,bancoesaldo.Além dissoaclassetambémvaiconterosmétodosparasacar,depositaretransferir dinheiroentrecontas. Selecioneogrupo“Source”eabraomenucontextual.Váem“Add”->“NewFile”. Selecione“CocoaClass”edepois“Objective-Cclass”,comonaimagemabaixo:
7
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Imagem6–CriandoanossaprimeiraclasseemObjective-C
Napróximajanela,coloqueonomedaclassecomosendo“Conta.m”emarqueo checkbox“Alsocreate‘Conta.h’”:
8
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Imagem7–CriandoanossaprimeiraclasseemObjective-C
Comaclassecriada,vocêdeveverosarquivosnoXcodecomonaimagemabaixo: Imagem8–visualizaçãodonavegadordearquivoscomaclasseContacriada.
Olhandopraessaimagempodemosverqueexisteumarquivo“Conta.m”eum arquivo“Conta.h”,vejamosoquehádecódigoemcadaumdessesarquivos: Listagem1–Conta.h
#import @interface Conta : NSObject { } @end
9
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares SevocênuncaprogramouemCnavida,deveestarseperguntandoporquetemos doisarquivosparaumaúnicaclasse,um“.h”eoutro“.m”.Oarquivo“.h”, funcionacomo“cabeçalho”(daío“.h”,de“header”),nelevocêdefineainterface públicadaclasse,comoosatributosqueelatemeosmétodosqueela implementa(equedevemficarvisíveisparaterceiros). EmObjective-C,diferentementedeJavaeoutraslinguagens,nãoexistem modificadoresdeníveldevisibilidadeparaasclassesouosseusmembros(como métodoseatributos),tudoé“visível”,masapenasoqueestiverdefinidono arquivo“.h”ficarealmentedisponívelparaoutrosobjetosquequeiramusara suaclasse. Olhandoagoradiretamenteparaocódigofonte,vemoso“ #import ”,issoquerdizerqueanossaclasseestádeclarandoquevai utilizarfuncionalidadesdoframework“ Cocoa”(naverdadenóssóprecisamos doframework“Foundation”,masvamosdeixarassimporenquanto).Logoapós issovemososeguintecódigo: @interfaceConta:NSObject
Semprequevocêvirumcaractere“@”(arroba)emcódigoescritoemObjectiveC,querdizerqueoquevemlogoapóseleéumaextensãodalinguagemaoC. Opa,peraí,comoassimuma“extensãoalinguagemC”? Objective-C,assimcomoC++,existecomoumaextensãoalinguagemC.Você podeescrevercódigoCdentrodeprogramasescritosemObjective-Ceoseu código(teoricamente)vaicompilarefuncionarnormalmente.Osdesignersda linguagemresolveramentãodefinirumaformadedeixarclarooquenãoé“C puro”nalinguagemusandoocaracter“@”.Entãosemprequevocêviro“@”já sabequeissoéumaextensãodoObjective-Cparaadicionarnovos comportamentosaonossoqueridoeamadoC. Aextensão@interfacedizqueestamosdefinindoumanovaclassenalinguagem eoquesegueessadeclaraçãoéonomedaclasse,nonossocaso“ Conta”.Logo apósadeclaraçãodonomedaclasseo“:NSObject ”dizqueanossaclasse“Conta” herdade“NSObject ”.Diferentementedeoutraslinguagensondeexisteumaúnica classeraizemãedetodososobjetos(pensenoObjectdeJava,Ruby,C#etantas outras),emObjective-Cvocêmesmopodedefinirumaclasseraiz,mas normalmentevocêvaiherdarde“NSObject ”queéaclasseraizdoframework basedeObjective-CutilizadonodesenvolvimentodeaplicaçõesparaoMacOSe iOS.Obviamente,sevocênãodisserdequalclassevocêherda,asuaclassese tornaautomaticamenteumaclasseraiz,entãolembre-sesemprededefinira superclassedasuaclasseousimplesmentecoloquequeelaherdade“ NSObject ”. Opardechaves“{}”quevemlogoapósadeclaraçãodaclasseéolugaronde vocêdefineasvariáveisdeinstânciadasuaclasseesomenteelas(não,nãoé aquiquevocêcolocaosmétodos).Todasasvariáveisdeinstânciaprecisamestar definidasaquinoarquivo“.h”,mesmoaquelasquevocêqueiradeixarcomo “privadas”. Apósopardechavesvemocorpodaclasse,queéolugarondevocêdefineos métodosqueessaclasseimplementa(masvocênãoosimplementaaqui,você
10
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares apenasdefinequaissãoeles).Nãodefinirummétodoaquinormalmentefazcom quenãosejapossívelpraquealguémoinvoque,éumadasformasdesecriar métodos“privados”emObjective-C,jáquenãoexisteesseconceitodentroda linguagememsi. Listagem2–Conta.m
#import "Conta.h" @implementation Conta @end
Noarquivo“.m”vocêencontraagoraocódigorealdaclasse(mesmoquenão tenhamoscolocadonadaaindanele.Enquantoquenoarquivo“.h”nóshavíamos definidoa@interfacedocódigo,agoraestamosdefinindoa @implementation. Vejaqueaquinãoémaisnecessáriodefinirdequalclasseanossaclasseherda,a definiçãoficaapenasnainterface. Vejaqueocódigotambémfazum“#import”paraoarquivo“.h”,issoéparaqueo arquivodeimplementaçãopossaverasinformaçõesdefinidasnainterface,como variáveisdeinstânciaetambémreceberautomaticamenteasdependênciasque jáforamimportadasnomesmo.EmCvocêfariaomesmocom#include,maso #importvaiumpoucomaislongeeevitaqueomesmoarquivoseja“incluído” duasvezes,umproblemabemcomumpraquemtrabalhacomC. Vamosagoracomeçararealmenteescrevercódigo: Listagem3–Conta.h
#import @interface Conta : NSObject { float saldo; } - (BOOL) depositar:(float)valor; - (float) saldo; @end
Agoraanossaclassecontatemumavariáveldeinstânciadefinida(dotipo “ float ”),declaramosaexistênciadométodo depositar querecebeumparâmetro dotipo float eretornaumBOOL,obooleandalinguagem.Tambémdeclaramoso método“saldo”quevaiseraformadeacessaravariáveldeinstância“saldo”.É possívelacessarumavariáveldeinstânciadeumaclasseemObjective-C diretamentede“fora”daclasse,masomelhoréfazeroacessosemprevia métodos(ouaspropriedadesqueveremosmaisafrente). Osinalde“-“(subtração)antesdadefiniçãodométodoavisaqueesseéum métododeinstância(métodosdeclassesãodefinidoscomumsinaldeadição,o “+”).Otipoderetornoeotipodosparâmetrosrecebidosficamsempreentre parênteses. UmdetalheimportantenadeclaraçãodemétodosemObjective-Céqueo parâmetrofica“dentro”donomedométodoenãoapósadefiniçãodonome, 11
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares comoemJavaouRuby.Comoométododepositar recebeumparâmetroé necessáriocolocaros“:”parasepararonomedoparâmetroqueestásendo passado,depoisvamosentenderumpoucomaiscomosenomeiammétodosem Objective-C. Vejamosentãocomovaificaronossoarquivo“.m”: Listagem4–Conta.m
#import "Conta.h" @implementation Conta - (BOOL) depositar: (float) valor { if ( valor > 0 ) { saldo += valor; return YES; } else { return NO; } } - (float) saldo { return saldo; } @end
Olhandoprocódigofontevocêjádeveterentendidoexatamenteoqueelefaz,se ovalorpassadocomoparâmetroformaiordoquezero,elevaisomarcomo valoratualdavariáveldeinstância“saldo”eretornarYES ,queéumatalhoparao valorBOOLquerepresentaverdadeiro,seovalorpassadocomoparâmetrofor menorouigualazeroelesimplesmenteretorna NO. AssimcomoemC,blocosdecódigoemObjective-Cficamsempredentrodepares dechaves(“{}”)etodasasestruturasdecontrolequevocêconhecedoC(ouJava eC#)existemexatamentedamesmaformaemObjective-C. Aimplementaçãodométodo“saldo”éaindamaistrivial,elasimplesmente retornaovalordavariáveldeinstânciadiretamente. Criandoonossoprimeirotesteunitário
Comanossaclasseimplementada,agoraéahoradeescreverumtesteparaesse primeirométodoimplementado.Aprimeiracoisaasefazerécriarumnovo grupodentrodoprojetopramanterasclassesdeteste,assimpodemos facilmentesepararasclassesdosseustestesnahoradegeraraaplicaçãofinal,já queelanãoprecisalevarostestesconsigo.Cliquecomobotãodireitoem “AprendendoObjectivec”,comonaimagemabaixo:
12
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Imagem9–Criandoogrupo“Test”
Comogrupocriado,cliquecomobotãodireitoem“Test”,selecione“Add”e “NewFile”.VocêvaicriarumaclassedetestepadrãodoCocoa,comonaimagem abaixo: Imagem10–Criandoaclassedetestes
Dêonome“ContaTest”aclasseeselecioneoTarget“Test”apenas,desmarcando oprimeiroTargetqueaparece,comonaimagemabaixo:
13
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Imagem11–Selecionandoostargetsdaclassedetestes
Nocasodostestes,vocênormalmentenãoprecisadefinirnadanoarquivo“.h”,já queosmétodossãochamadosautomaticamente,entãovamosnosfocarapenas noarquivo“.m”,queéondenósvamosrealmenteestartrabalhando. Implementandoonossoprimeiroteste Listagem5–ContaTest.m
#import "ContaTest.h" #import "Conta.h" @implementation ContaTest - (void) testDepositarComSucesso { Conta * conta = [[Conta alloc] init]; [conta depositar:200]; STAssertTrue( [conta saldo] == 300, @"O saldo deve ser de 300 para que o teste falhe" ); } @end
Assimcomoemoutrosframeworksdeteste,ométodoquedefinea implementaçãodotesteprecisateroseunomeiniciandocom“test”enão retornarnada(porissoovoid ).Naprimeiralinhadotestejátemosvários detalhesdalinguagempraentender. Aprimeiracoisaaserpercebidaéqueadeclaraçãodavariávelcontémum“*”,se vocêvemdoC,sabequeissoquerdizerqueessavariávelénaverdadeuma referencia(ouponteiro)praumobjetoqueestáemmemória.Semprequevocê defineumareferenciapraumavariávelemObjective-Cénecessáriocolocaro
14
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares “*”,sevocênãoofizervaireceberwarningsdocompiladoreoseuprogramanão vaifuncionarcorretamente. Aindanessalinhatemosoidiomadeinicializaçãodeobjetos.Oqueemoutras linguagensdeprogramaçãoOOseriafeitoatravésdeumconstrutor,em Objective-Csefazatravésdaschamadasa“ alloc”,ummétododeclasseque organizaumanovainstânciadoobjetoemmemóriaeretornaoobjetopronto prauso,e“init”,queexecutafinalmenteainicializaçãodoobjeto.Épossívelusar tambémométododeclasse“new ”emvezdo“par”alloc/initparasecriarum objeto,masépreferívelusaralloc/init,especialmentesevocêpretendetervárias formasdeinicializaroseuobjeto. Ótimo,vocêdiz,masoquediabossãoaquelesparesdecolchetes(“[]”)? SeguindoatradiçãodeSmalltalk,emObjective-Caideianãoéquevocêestá chamandoummétodoemumobjeto,massimenviandoumamensagemaele. Inicialmente,asintaxepoderealmenteparecerumpoucoestranha,masnofim dascontaselaébemsimples: [objetoDestino mensagem] Doladoesquerdo,vocêsempretemoobjetoparaoqualvocêquerenviara mensagem,doladodireitovocêtemamensagemqueestásendoenviada(o métodoqueestásendochamado),tudoissodentrodecolchetes.Nonossocaso, ondefazemos“[[Conta alloc] init]”,estamosenviandoamensagem“ alloc” paraoobjetoquerepresentaaclasse“Conta”enovalorqueéretornadoporesse método(umobjetoConta)fazemosachamadadométodo“ init ”.Oidioma alloc/initécomumepervasivoemtodaalinguagemeexemplosdecódigoque vocêvaiencontrar,masevitefazerchamadasdemétododentrodechamadasde métodonoseucódigo,anãoserquesejaumcasomuitosimplescomoesseque nósestamosvendoaqui. Nasegundalinhadotestevemososeguinte: [conta depositar:200];
Nósenviamosamensagem“depositar”paraoobjetorepresentadopelavariável “conta”comoparâmetro200.Os“:”quenósusamosnadefiniçãodométodo “depositar”fazemrealmentepartedonomedométodo,sendoobrigatórioasua adiçãoachamada(osmétodosquenãoadicionam“:”sãoosquenãorecebem parâmetros). Nofimtemosocódigoquefazaasserçãodoteste: STAssertTrue( [conta saldo] == 300, @"O saldo deve ser de 300 para que o teste falhe" );
Comparamosentãoovalordosaldocom300exatamenteporquequeremos, nesseprimeiromomento,verotestefalhar.STAssertTruenãoéummétodo,mas umafunçãocomumquevocêdefiniriacomoumafunçãoemC,elafazpartedo frameworkdetestesunitáriosquevemporpadrãodentrodoCocoa. Agoraumdetalheimportantequepodepassardesapercebidoéadefiniçãodo textousadocomomensagemparaesseteste,emvezdesersomenteumconjunto decaracteresentreaspas,háum“@”antesdasaspas.Issoquerdizerquevocê
15
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares nãoestáusandoumastringcomumdoC,queéumarraydecaracteres,esimum objetodotipoNSStringqueadicionaváriasfuncionalidadesasstringscomunsdo C.AmaiorpartedocódigoemObjective-CquelidacomStringsvaiesperarque vocêenvieobjetosdotipoNSString,entãoébomseacostumaraescrever primeiroa“@”antesdedeclararumstringnoseucódigo. VoltemosagoraparaoXcode,ondevocêvaimudaroTargetpadrãoparaTest, vejacomofazerissonaimagemabaixo: Imagem12–AlterandooTargetpadrãoparaTest
AgoraestamosentrandonomododetestesdoXcodevamospodercomeçara executarostestes,masantesdefazerissoprecisamosdizerparaoTarget“ Test ” ondeelevaiacharaclasseConta,poiselanãofoiadicionadaaele.Prafazerisso, vocêdeveselecionaroarquivo“Conta.m”earrastá-loparadentrodapasta “CompileSources”doTarget“Test”,comonaimagemabaixo: Imagem13–Dragedropdoarquivo“Conta.m”emTest
16
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Comissofeito,podemosfinalmenteexecutarobuilddoprojeto,prafazerisso digite“Command+B”.ApósalgunsinstantesoXcodedeveterminardefazero buildevocêdeveverumavisonocantoinferiordireitodaferramentaindicando queexistemdoiserros: Imagem14–IndicaçãodeerrosdebuilddoXcode
Aoclicarnoserrosvocêdeveveraseguintetela:
17
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Imagem15–teladeerrosnobuilddoXcode
Comovocêpodeperceber,oerronaverdadenãoéexatamentenobuild,massim nonossotesteunitárioquefalhou,jáqueovalordavariávelnãoé300esim200. Paraveressatelasemterqueusaromousepraclicarnoerrobastafazer“Shift+ Command+B”.Agoraquevocêjáviuatela,troqueo300por200eexecuteo buildmaisumavezcom“Command+B”,oseubuilddeveexecutarsemerros, agoraqueotestejáestáimplementadocorretamente. Vamosagoradefinirosmétodossacaretransferirnaclasseconta: Listagem6–Conta.h
#import @interface Conta : NSObject { float saldo; } -
(BOOL) depositar: (float) valor; (BOOL) sacar: (float) valor; (BOOL) transferir: (float) valor para: (Conta *) destino; (float) saldo;
@end
Estamosquasechegandolá,ométodosacartemadefiniçãoigualadepositar, masométodo“transferir:para:”(vejasócomoelesechama)deveestardando umnónoseucérebronessemomento.Vejamos: - (BOOL) transferir: (float) valor para: (Conta *) destino;
EmObjective-C,quandovocêtemummétodoquerecebeváriosparâmetros, vocêprecisa“dividir”onomedométodoempedaçosparareceberos
18
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares parâmetros.Entãoemvezdesimplesmentefazerum“transferir(doublevalor, Contadestino)”,comovocêfariaemJavaouC#,vocêquebraonomedométodo etransformareleem“transferir:valorpara:destino”. Inicialmenteasintaxepareceestranha,masapróprialeituradachamadado métodoficamaissimplesesevocêtiverummétodoquerecebevários parâmetroselecomcertezavaificarbemmaislegível,jáquecadaparâmetrovai teroseuidentificadorantes. Vejamosagoracomoficaaimplementaçãodessesdoismétodos: Listagem7–Conta.m
#import "Conta.h" @implementation Conta - (BOOL) depositar: (float) valor { if ( valor > 0 ) { saldo += valor; return YES; } else { return NO; } } - (BOOL) sacar:(float)valor { if ( valor > 0 && valor <= saldo) { saldo -= valor; return YES; } else { return NO: } } - (BOOL) transferir:(float) valor para:(Conta *) destino { if ( [self sacar: valor] && [ destino depositar: valor ] ){ return YES; } else { return NO; } } - (float) saldo { return saldo; } @end
Aessaalturadocampeonato,vocêjásabeexatamenteoqueessecódigotodo estáfazendo,entãovamospassardiretamenteprostestes,praveressesnovos 19
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares métodossendoexercitados.Emtodososnossostestesvamos,utilizarumobjeto conta,entãoemvezderecriaresseobjetoacadateste,vamosserinteligentes definirumavariáveldeinstância Contanonossotesteeimplementarométodo “setUp”quecriaacontaparanãorepetirmosessaoperação: Listagem8–ContaTest.h
#import @class Conta; @interface ContaTest : SenTestCase { Conta * conta; } @end
Adicionamosadefiniçãodavariáveldeinstâncianaclasse,comoesperado,maso queéesse“@class”quetambémestáaí?O@classemObjective-Céuma “forwardreference”enormalmenteéutilizadoemarquivos“.h”paraquevocê digaqueoseuarquivodependedeumaclasseemespecífico,masnãovaifazero “#import”dessaclasseaqui,vaideixarpraimportaroarquivodaclassesomente noseu“.m”.Sevocênãocolocaro@classnemo“#import”praoarquivoda classenãovaiserpossívelcompilarocódigo. Umdetalheimportantedo“#import”équequandootextoquevemapóseleestá entre“<>”(comoem#import ),isso indicaaocompiladorqueeledeveprocuraressearquivono“loadpath”do sistemaoperacional,oslugaresondeficamosarquivos“.h”domesmo.Quandoo “#import”apareceusandoaspasnoconteúdoaserimportado,querdizerqueele deveprocurardentrodosarquivoslocaisdoprojeto(comoem #import "Conta.h"). Vejamosagoraaimplementaçãoatualdostestes: Listagem9–ContaTest.m
#import "ContaTest.h" #import "Conta.h" @implementation ContaTest - (void) setUp { conta = [[Conta alloc] init]; [conta depositar: 200]; } - (void) testDepositarComSucesso { [conta depositar:150]; STAssertTrue( conta.saldo == 350, @"Saldo final deve ser 350" );
20
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares } - (void) testDepositarComFalha { [conta depositar:-150]; STAssertTrue( conta.saldo == 200, @"Valor do saldo não deve ter se modificado" ); } - (void) testSacarComSucesso { [conta sacar:150]; STAssertTrue( conta.saldo == 50, @"O saldo atual deve ser 50" ); } - (void) testSacarComValorMaior { [conta sacar: 250]; STAssertTrue( conta.saldo == 200, @"O saldo atual não deve ter se modificado" ); } - (void) testSacarComValorNegativo { [conta sacar: -100]; STAssertTrue( conta.saldo == 200, @"O saldo atual não deve ter se modificado" ); } - (void) testTransferirComSucesso { Conta * destino = [[Conta alloc] init]; [conta transferir:150 para: destino]; STAssertTrue( conta.saldo == 50, @"O saldo da conta origem deve ser 50" ); STAssertTrue( destino.saldo == 150, @"O saldo da conta destino deve ser 250" ); } - (void) testTransferirComFalha { Conta * destino = [[Conta alloc] init]; [ conta transferir:250 para: destino ]; STAssertTrue( conta.saldo == 200, @"O saldo da conta origem deve ser 50" ); STAssertTrue( destino.saldo == 0, @"O saldo da conta destino deve ser 250" );
21
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares } @end
Temosentãováriostestesparaaimplementaçãodasfuncionalidadesdanossa classeconta,elessãobemsimples,mastemduascoisasimportantesquenão discutimosainda,aprimeiraéessa: conta.saldo == 200
Antes,nonossoteste,fazíamosachamadaassim: [conta saldo] == 200
EmObjective-C,sevocêtemummétodoquenãorecebeparâmetroseretorna umvalor,essemétodopodeserchamadocomoseelefosseumapropriedadedo seuobjeto,semquevocêtenhaquefazerumainvocaçãoexplícitadomesmo, então“conta.saldo”éamesmacoisaqueescrever“[contasaldo]”,ocompilador vaifazeramágicadetransformaroprimeironosegundopravocê. Jáosegundocaso: [ conta transferir:250 para: destino ]
Aquinósvemosumexemplodachamadadométodo“transferir:para:”,junto comotransferirnóstemosovalorquevaisertransferidoelogodepoisdepara temosoobjetoquevaireceberatransferência,vejaquenãoexistemvírgulas separandoosparâmetros,elessãoseparadosnormalmentepelosespaçosentre osnomesqueformamométodoeosparâmetrospassados. Criandoum“construtor”paraonossoobjetoconta
Comojácomentamosantes,opar“[[Contaalloc]init]”servepracriaroobjetoe inicializá-lodentrodoprojeto,masesenósquisermosdefinirumaforma personalizada?Simples,criamosummétododeinicialização.Nonossocaso queremospodercriarcontascomumsaldoquesejadiferentedezero. Começamospordefinirométodo“initWithSaldo”em“Conta.h”: Listagem10–Conta.h–declarandoométodoinitWithSaldo
#import @interface Conta : NSObject { float saldo; } -
(Conta *) initWithSaldo: (float) valor; (BOOL) depositar: (float) valor; (BOOL) sacar: (float) valor; (BOOL) transferir: (float) valor para: (Conta *) destino; (float) saldo;
@end
22
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Comonãoexistemconstrutoresrealmentedentrodalinguagem,oquenós fazemosédefinirmétodosqueinicializemoobjetocomovalorquenos interessa,vejamosaimplementaçãoagorapraentenderumpoucomaisoque estáacontecendoeporqueénecessáriocriaressesmétodos: Listagem11–Conta.m
#import "Conta.h" @implementation Conta - (Conta *) initWithSaldo:(float)valor { if ( self = [ self init]) { saldo = valor; } return self; } // todo aquele código que você já viu @end
Maisumcasodeidiomadalinguagemsurgeentãopranósnessemomento: if ( self = [ self init]) {
Pormaisestranhoquepareça,essecódigoestárealmenteatribuindoumvalora variável“self”(“self”éequivalenteao“this”emJavaeC#,éumareferênciapara oobjetoondeométodoemexecuçãoatualfoichamado)dentrodoseuobjeto. Masporquealguémiriaquererfazerisso? NaimplementaçãodaAppledoObjective-Cexisteumconceitochamadode “class-clusters”.QuandovocêcriaumobjetodotipoNSString,oquevocêrecebe podenãoserexatamenteumNSString,masumasubclassedele.Asclassesque nósvemos“doladodefora”funcionamapenascomoummeiopraseacessaras classesquefazemrealmenteotrabalho,masessesdetalhesdeimplementação ficamescondidosgraçasaessapequenamágicadométodoinit(atribuirumnovo valoraselferetornaressevalor). Nocasodanossaclassenãoserianecessáriofazeressamágica,jáqueestamos realmenteretornandoumaconta,masoidealéquevocêconstruatodososseus inicializadoresdessaformaparaquequandoforcriarumasubclassedeuma classepadrãodalinguagemnãoseesquecereterminarcombugsestranhosno seucódigo. Outracoisaimportantetambémélembrar-sedechamarométodo“ init ”dasua classe,sevocêestiverimplementandoumnovoinicializador(comonósfazemos nessecódigo)ouchamarométodoinit dasuasuperclasse(com[superinit])se vocêestiversobrescrevendoométodo“init ”,assimvocênãoperdeocódigode inicializaçãoquejátenhasidoimplementadonaclasseatualenassuas superclasses. Definindopropriedadesautomaticamentenosobjetos
Nósvimosqueocompiladoréinteligenteosuficientepraaceitarque“[conta saldo]”sejachamadocomo“conta.saldo”,masalémdissonóstambémpodemos 23
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares instruirocompiladorparaqueelegereosmétodosdeacessoparaas propriedadesdosnossosobjetosautomaticamente(penseno“generategetters andsetters”doEclipseouattr_accessoremRuby).Praisso,vamosdefinirnovas propriedadesnanossaclasse,“agencia”e“conta”.Vejamoscomoficaonosso “Conta.h”agora: Listagem12–Conta.hcomasnovaspropriedadesdefinidas #import @interface Conta : NSObject { float saldo; NSString * conta; NSString * agencia; } @property (copy, nonatomic) NSString * conta; @property (copy, nonatomic) NSString * agencia; @property (readonly) float saldo; -
(Conta (BOOL) (BOOL) (BOOL)
*) initWithSaldo: (float) valor; depositar: (float) valor; sacar: (float) valor; transferir: (float) valor para: (Conta *) destino;
@end
Nósdefinimosasduasvariáveisdeinstância,“agencia”e“conta”,elogodepois temosasdeclaraçõesdaspropriedades,com“@property”: @property (copy, nonatomic) NSString * conta;
Issoindicaaocompiladorquenósvamosterosmétodosabaixodefinidos: -
(NSString*)conta; (void)setConta:(NSString*);
Asinstruções“copy”e“nonatomic”sãoatributosquevãoserutilizadosna geraçãodaimplementaçãodosmétodos. -
-
-
“copy”indicaquequandoumobjetoforrecebidocomoparâmetro,deve sercriadaumacópiadesseobjetoeessacópiaéquemvaiseratribuídaa variáveldeinstância,issoénecessárioespecialmentesevocêestá recebendodadosdainterface,poisosstringsquevemdelapodemser recolhidosdamemóriaaqualquermomentonosambientesondenãohá coletordelixo,comoiPadseiPhones. “nonatomic”indicaqueosmétodosgeradosnãovãofazernenhum controlesobreoacessodeformaconcorrente.Essenormalmenteéocaso pramaiorpartedasaplicações,massevocêvaiutilizaresseobjetoemum ambientecomconcorrência,ondeváriasthreadsvãoacessaromesmo objetoechamarseusmétodos,deveremoverisso. “readonly”indicaqueapenasométodo“getter”,quelêovalordavariável, vaisergerado,ométodoset,quealteraovalordavariávelnãovaiser gerado. 24
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Comosmétodosdefinidos,agoravoltamospra“Conta.m”pradeclararocódigo quevaifazercomqueosmétodossejamrealmentegerados,vejamoscomoficao códigoagora: Listagem13–Conta.mcomadefiniçãodaspropriedadesegerenciamentode memória
#import "Conta.h" @implementation Conta @synthesize agencia, conta, saldo; - (void) dealloc { [ self.agencia release ]; [ self.conta release ]; [ super dealloc ]; } // todo o resto do código que você já conhece aqui @end
Amágicadeverdadeviveno“@synthesize”,eleinstruiocompiladoraleras informaçõesdaspropriedadesdefinidasatravésde“@property”egeraros métodos.Juntocomissochegamosaumdetalheimportantedaprogramação comObjective-C,especialmentesevocêestiverplanejandoprogramarparaiOS,o gerenciamentodememória. Comadefiniçãodaspropriedadesnósdefinimostambémométodo“dealloc”que échamadoquandoumobjetovaiserremovidodamemória,paraqueelepossa limpardamemóriatambémosobjetosparaosquaiseleaponta.Quandovocê estáprogramandoemObjective-CparaiOS,nãoexisteumcoletordelixo automático,liberaramemóriaéresponsabilidadedoprogramador,entãoé necessárioquevocêtomecuidadoparanãovazarmemórianoseucódigoe estouraramemóriadodispositivo. EmObjective-Cocontroledememória,quandonãoéfeitoatravésdocoletorde lixo,aconteceatravésdacontagemdereferencias.Semprequevocêcriaum objetousando“alloc”ou“new”esseobjetoficacomocontadordereferenciasem 1(um),cadavezquevocêchama“retain”noobjeto(comoem“[objetoretain]”) essecontadoraumentaemumecadavêsquevocêchama“release”(comoem “[objetorelease]”)ocontadordiminuiem1.Quandoocontadordereferencias atingir0,oobjetoéremovidodamemória. Nonossocaso,aspropriedades“conta”e“agencia”sãodefinidascomo“copy”, issoquerdizerqueoobjetoquevaisercolocadonavariáveldeinstânciavaiser clonadoeocloneéoobjetoquevaifinalmenteficardisponívelparaanossa classe.Comoocloneéumobjetorecém-criadocombaseemoutroobjetoasua contagemdereferenciasé1(um),oquequerdizerquequandochamarmos “release”neles,elesvãoserremovidosdamemória. Pralimparamemóriaqueestamosusando,precisamosajustaronossoteste parafazero“release”dosobjetoscontaqueestãosendocriados.Vamosfazero
25
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares releasedavariáveldeinstânciacontanométodotearDownedacontadestino emcadaumdostestes,vejamosoquemudananossaclasseteste: Listagem14–ContaTest.mcomocódigodegerenciamentodememória
@implementation ContaTest //métodos não mostrados aqui não foram alterados - (void) tearDown { [ conta release ]; } - (void) testTransferirComSucesso { Conta * destino = [[Conta alloc] initWithSaldo: 100 ]; [conta transferir:150 para: destino]; STAssertTrue( conta.saldo == 50, @"O saldo da conta origem deve ser 50" ); STAssertTrue( destino.saldo == 250, @"O saldo da conta destino deve ser 250" ); [ destino release ]; } - (void) testTransferirComFalha { Conta * destino = [[Conta alloc] init]; [ conta transferir:250 para: destino ]; STAssertTrue( conta.saldo == 200, @"O saldo da conta origem deve ser 50" ); STAssertTrue( destino.saldo == 0, @"O saldo da conta destino deve ser 250" ); [ destino release ]; } - (void) testSetContaEAgencia { conta.agencia = @"1111-0"; conta.conta = @"10.000-9"; STAssertEqualObjects( conta.agencia, @"1111-0", @"O valor deve ser igual" ); STAssertEqualObjects( conta.conta, @"10.000-9", @"O valor deve ser igual" ); } @end
26
CursodedesenvolvimentodeaplicaçõesparaiOSusandoObjective-C MaurícioLinhares Adicionamosanossaimplementaçãoométodo“tearDown”queenviaum “release”paraoobjetocontaetambémfazemosoreleasedosobjetosdestino criadosnostestesdetransferência.Vocênuncadevechamarométododealloc diretamentenosseusobjetos,semprechamereleaseedeixequeopróprio runtimedoObjective-Cvaifazerachamadaadeallocquandoforahoracorreta. Épossíveldefiniralgumasregrinhasbásicasnahoradelidarcomagerênciade memóriaemaplicaçõesescritasemObjective-C: •
•
•
•
Sevocêpegouumobjetoatravésde“alloc/new/copy”,esseobjetotem umcontadorde1evocêdeveselembrardeliberaresseobjetoquando elenãoformaisnecessário; Sevocêpegouumobjetodeoutrolugar,assumaqueeletemumcontador de1,sevocêsóvaiusá-loedeixarelepralá,nãofaçanadacomele,quem passouelepravocêprovavelmentevailimpá-loquandofornecessário. Sevocêprecisamanterumobjetorecebidodeoutrolugarparausá-loem outromomento,chame“retain”neleparaqueocontadoraumentepara “2”,assimquandoquemlhepassouesseobjetochamar“release”neleo contadorvaibaixarpra“1”eoobjetoaindanãovaiserliberadoda memória. Semprequevocêdá“retain”emumobjeto,devegarantirquevaidarum “release”neleemalgummomento,sevocênãodero“release”,com certezavaiestarvazandomemórianasuaaplicação;
Gerenciamentodememóriaéumtópicolongoevamosnosaprofundarmaisnele conformeavançamosparaaconstruçãodasnossasaplicaçõesparaiOS,essa introduçãoéapenasparaquevocêentendaobásicodecomoesseconceito funcionadentrodalinguagem.
27