ΑΝΑΠΤΥΞΗ ΕΦΑΡΜΟΓΗΣ ΣΕ ΠΕΡΙΒΑΛΛΟΝ iphone ΓΙΑ ΤΗΝ ΔΙΕΥΚΟΛΥΝΣΗ ΤΗΣ ΠΡΟΣΒΑΣΗΣ ΤΩΝ ΦΟΙΤΗΤΩΝ ΣΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ ΔΙΠΛΩΜΑΤΙΚΗ ΕΡΓΑΣΙΑ

Μέγεθος: px
Εμφάνιση ξεκινά από τη σελίδα:

Download "ΑΝΑΠΤΥΞΗ ΕΦΑΡΜΟΓΗΣ ΣΕ ΠΕΡΙΒΑΛΛΟΝ iphone ΓΙΑ ΤΗΝ ΔΙΕΥΚΟΛΥΝΣΗ ΤΗΣ ΠΡΟΣΒΑΣΗΣ ΤΩΝ ΦΟΙΤΗΤΩΝ ΣΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ ΔΙΠΛΩΜΑΤΙΚΗ ΕΡΓΑΣΙΑ"

Transcript

1 ΑΝΑΠΤΥΞΗ ΕΦΑΡΜΟΓΗΣ ΣΕ ΠΕΡΙΒΑΛΛΟΝ iphone ΓΙΑ ΤΗΝ ΔΙΕΥΚΟΛΥΝΣΗ ΤΗΣ ΠΡΟΣΒΑΣΗΣ ΤΩΝ ΦΟΙΤΗΤΩΝ ΣΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ ΔΙΠΛΩΜΑΤΙΚΗ ΕΡΓΑΣΙΑ ΓΕΩΡΓΙΟΥ ΧΡΙΣΤΟΥΛΑΚΗ ΕΠΙΒΛΕΠΩΝ: Κ. ΣΓΑΡΜΠΑΣ ΠΑΤΡΑ - ΙΟΥΝΙΟΣ 2012

2 ΠΙΣΤΟΠΟΙΗΣΗ Πιστοποιείται ότι η διπλωματική εργασία με θέμα: ΑΝΑΠΤΥΞΗ ΕΦΑΡΜΟΓΗΣ ΣΕ ΠΕΡΙΒΑΛΛΟΝ iphone ΓΙΑ ΤΗΝ ΔΙΕΥΚΟΛΥΝΣΗ ΤΗΣ ΠΡΟΣΒΑΣΗΣ ΤΩΝ ΦΟΙΤΗΤΩΝ ΣΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ Του φοιτητή του τμήματος Ηλεκτρολόγων Μηχανικών & Τεχνολογίας Υπολογιστών Χριστουλάκη Γεωργίου (Α.Μ. 6889) παρουσιάστηκε δημόσια και εξετάστηκε στο Τμήμα Ηλεκτρολόγων Μηχανικών & Τεχνολογίας Υπολογιστών στις 13/06/12 Ο Επιβλέπων Ο Συνεξεταστής Κ. Σγάρμπας Ε. Δερματάς Επ.Καθηγητής Επ.Καθηγητής

3 ΤΙΤΛΟΣ: Ανάπτυξη Εφαρμογής σε Περιβάλλον iphone για την Διευκόλυνση της Πρόσβασης των Φοιτητών στο Πανεπιστήμιο Πατρών ΣΥΓΓΡΑΦΕΑΣ: Χριστουλάκης Γιώργος (Α.Μ. 6889) ΠΕΡΙΛΗΨΗ: Στην παρούσα διπλωματική εργασία παρουσιάζεται η ανάπτυξη μιας εφαρμογής σε περιβάλλον iphone, η οποία έχει ως σκοπό την διευκόλυνση της πρόσβασης των φοιτητών στο χώρο του Πανεπιστημίου Πατρών. Πιο συγκεκριμένα, προσφέρει δεδομένα σχετικά με τον χώρο του Πανεπιστημίου και την αστική συγκοινωνία της Πάτρας και προσφέρει στον χρήστη την δυνατότητα της καθοδήγησης από ένα σημείο της πόλης σε άλλο. ABSTRACT: This diploma dissertation concerns the development of an iphone application, aiming to facilitate the access of the students to the University of Patras. It provides information over the University of Patras grounds and the traffic network of Patras, as well as the ability for the user to be navigated from one part of the city to another.

4 ΠΡΟΛΟΓΟΣ Η εργασία αυτή εκπονήθηκε κατά το χρονικό διάστημα Εκτελέστηκε σε λειτουργικό σύστημα υπολογιστών MacOS, με επισκέψεις στον χώρο του Πανεπιστημίου. Ξεκινώντας από τον Σεπτέμβρη του 2011, ο πρώτος μήνας αποτέλεσε γνωριμία με το περιβάλλον των MacOS και του προγραμματισμού για περιβάλλον iphone. Στη συνέχεια ξεκίνησε η υλοποίηση της εφαρμογής, που ολοκληρώθηκε ουσιαστικά αμέσως μετά το Πάσχα του Συνολικά έγιναν 42 επισκέψεις στον χώρο του Πανεπιστημίου προκειμένου να είναι δυνατόν να χρησιμοποιηθεί υπολογιστής με MacOS, και αρκετές μέρες εργασίας στο σπίτι για να συγκεντρωθούν οι απαραίτητες πληροφορίες. Η ιδέα της διπλωματικής εργασίας, ξεκίνησε από προηγούμενη διπλωματική εργασία, με τίτλο «ΑΥΤΟΜΑΤΗ ΔΡΟΜΟΛΟΓΗΣΗ ΣΕ ΔΙΚΤΥΟ ΑΣΤΙΚΩΝ ΣΥΓΚΟΙΝΩΝΙΩΝ». Δυστυχώς δεν ήταν δυνατόν να βρεθούν τα δεδομένα της προηγούμενης αυτής εργασίας, οπότε η παρούσα διπλωματική εργασία ξεκίνησε από το μηδέν. Επιβλέπων καθηγητής της διπλωματικής αυτής εργασίας ήταν ο κ. Κυριάκος Σγάρμπας και μεγάλη βοήθεια πρόσφερε και ο διδακτορικός ερευνητής Χαράλαμπος Τσιμπούρης, τους οποίους και ευχαριστώ.

5 ΠΙΝΑΚΑΣ ΠΕΡΙΕΧΟΜΕΝΩΝ 1. ΕΙΣΑΓΩΓΗ 1.1 Στόχος και χρησιμότητα Πρακτικές δυσκολίες στην υλοποίηση ΓΕΝΙΚΕΣ ΓΝΩΣΕΙΣ 2.1 Η γλώσσα προγραμματισμού Objective C Αντικειμενοσταφή και διαδικαστικός προγραμματισμός Η Objective C ως επέκταση της C Διαχείρηση Μνήμης Alloc, Retain, Copy και Release Setter μέθοδοι, πιο αναλυτικά Autorelease και Autorelease pool Προγραμματισμός για iphone και το περιβάλλον του Xcode Model, View, Controller (MVC) Delegation Πρότυπα αρχεία Core Data Υπόλοιπες Γνώσεις HTML, XML και Javascript Λοιπές γλώσσες προγραμματισμού Αλγόριθμος Levenshtein Η ΕΦΑΡΜΟΓΗ 3.1 Αρχεία στο XCode Τα views της εφαρμογής Τα αρχικά Views Τα κύρια Views Modal View Controllers και υπόλοιπα views CoreData Views για φιλτράρισμα Application Delegate ΠΑΡΑΔΕΙΓΜΑΤΑ ΧΡΗΣΗΣ ΜΕΤΡΗΣΕΙΣ ΣΥΜΠΕΡΑΣΜΑΤΑ & ΜΕΛΛΟΝΤΙΚΕΣ ΒΕΛΤΙΩΣΕΙΣ 6.1 Συμπεράσματα Μελλοντικές Βελτιώσεις ΠΑΡΑΡΤΗΜΑ Α Κώδικας Objective C..93 Β Αναδρομικός Αλγόριθμος Αναζήτησης.145 Γ Βιβλιογραφία.147

6 ΚΕΦΑΛΑΙΟ 1 ΕΙΣΑΓΩΓΗ 1.1 Στόχος και χρησιμότητα Θέμα και στόχος της παρούσας διπλωματικής εργασίας είναι η ανάπτυξη μιας εφαρμογής σε περιβάλλον iphone για τη διευκόλυνση της πρόσβασης των φοιτητών στο χώρο του Πανεπιστημίου. Η γενικότερη ιδέα είναι ότι κάποιος φοιτητής που έχει περάσει σε κάποια σχολή της Πάτρας από άλλη πόλη, θα είχε την ανάγκη της καθοδήγησης, τόσο στον χώρο του Πανεπιστημίου, όσο και στην πόλη της Πάτρας. Παρέχεται λοιπόν λεπτομερής χάρτης του Πανεπιστημίου, στον οποίο ο χρήστης της εφαρμογής μπορεί να εντοπίσει την σχολή του, ή και γενικότερα οποιονδήποτε χώρο του Πανεπιστημίου, καθώς δεν είναι αδύνατο να αναζητήσει κανείς τον χώρο της Φοιτητικής Εστίας, του Γυμναστηρίου ή απλά και οποιασδήποτε άλλης σχολής. Σε δεύτερο στάδιο, πάνω στον χάρτη της Πάτρας που παρέχεται, μπορεί ο χρήστης να ψάξει οποιαδήποτε περιοχή, και προσθέσει δικές του σημειώσεις πάνω στο χάρτη. Ένα επιπλέον κομμάτι που κάθε πρωτοετής (και όχι μόνο) φοιτητής έρχεται να αντιμετωπίσει, είναι η χρήση της αστικής συγκοινωνίας, για να μεταφερθεί είτε στον χώρο του Πανεπιστημίου, είτε προς το κέντρο της Πάτρας. Υπάρχει λοιπόν πληροφορία πάνω στα λεωφορεία της Πάτρας και στο τρένο που εξυπηρετεί και το Πανεπιστήμιο, την οποία μπορεί ο χρήστης να δει με διαφορετικούς τρόπους: - Όλες τις στάσεις που υπάρχουν - Όλες τις στάσεις που κάνει μια συγκεκριμένη γραμμή λεωφορείου - Αναζήτηση συγκεκριμένης στάσης με το όνομά της - Δρομολόγηση από συγκεκριμένο αρχικό σημείο σε άλλο τελικό - Δρομολόγια λεωφορείων και τρένου 1.2 Πρακτικές δυσκολίες στην υλοποίηση Η βασικότερη δυσκολία της υλοποίησης της εφαρμογής, είναι το γεγονός ότι η πληροφορία γύρω από το αντικείμενο, τόσο του Πανεπιστημίου, όσο και των συγκοινωνιών της Πάτρας, δεν βρίσκεται συγκεντρωμένη, αλλά διασπάρτη στο διαδίκτυο. Επίσης καθώς όσοι παρείχαν την πληροφορία δεν είναι δυνατόν να είχαν υπόψιν οποιαδήποτε εφαρμογή που θα ακολουθούσε, υπήρχε το πρόβλημα της μορφής της πληροφορίας, που είναι διαφορετική από την ιδανικά επιθυμητή, και έπρεπε να γίνει κάποια προεπεξεργασία πριν δοθεί σαν είσοδος στην εφαρμογή που αναπτύσσεται στην παρούσα εργασία. Σε επόμενο κεφάλαιο θα αναλυθεί συνοπτικά η διαδικάσια εύρεσης, επεξεργασίας και αποθήκευσης των δεδομένων που χρειάστηκε η εφαρμογή. Εξίσου σημαντική δυσκολία είναι η ανάπτυξη της εφαρμογής σε συγκεκριμένη γλώσσα προγραμματισμού, σε συγκεκριμένη πλατφόρμα που (τουλάχιστον στην 1

7 Ελλάδα) δεν είναι τόσο διαδεδομένη. Τα «έξυπνα» τηλέφωνα (κοινώς smart phones) της κατασκευάστριας εταιρείας Apple, iphone, υποστηρίζουν εφαρμογές που αναπτύσσονται σε Objective-C, και για την παρούσα εργασία η ανάπτυξη έγινε μέσω της πλατφόρμας Xcode, που είναι αποκλειστικότητα του λειτουργικού συστήματος της Apple, Mac OS. 2

8 ΚΕΦΑΛΑΙΟ 2 ΓΕΝΙΚΕΣ ΓΝΩΣΕΙΣ 2.1 Η Γλώσσα Προγραμματισμού Objective-C Ξεκινώντας την περιγραφή της γλώσσας προγραμματισμού που χρησιμοποιήθηκε, αρκεί να δούμε το όνομά της και να πάρουμε δύο πολύ σημαντικά δεδομένα: - Objective - C Πρώτον, πρόκειται για μια αντικειμενοστραφή γλώσσα, όπως οι περισσότερες νέες γλώσσες υψηλού επιπέδου που έχουν αναπτυχθεί. Ως αντικειμενοστραφής, βασίζεται στην έννοια της κλάσης (class) και του στιγμιοτύπου (instance). Δεύτερον, πρόκειται για μια γλώσσα που δημιουργήθηκε ως επέκταση της γλώσσας προγραμματισμού C, μια από τις πιο βασικές γλώσσες προγραμματισμού. Περιμένουμε να δούμε λοιπόν πολλές ομοιότητες, συντακτικού κυρίως χαρακτήρα με την γλώσσα C, αλλά πολύ διαφορετικές δυνατότητες, καθώς η C είναι διαδικαστική γλώσσα Αντικειμενοστραφής και Διαδικαστικός Προγραμματισμός Ο αντικειμενοστραφής προγραμματισμός (Object-Oriented Programming) διαφέρει από τον διαδικαστικό προγραμματισμό (Procedural Programming) κυρίως στον βαθμό της αφαιρετικότητας που προσφέρει. Ο διαδικαστικός προγραμματισμός προσφέρει αφαιρετικότητα μέσω των συναρτήσεων, και συνδυάζει τις δυνατότητες τους για να εκτελέσει το κυρίως πρόγραμμα. Τα δεδομένα που απαιτούνται περνιούνται ως παράμετροι στις συναρτήσεις, ή υπάρχουν ως καθολικά δεδομένα. Όταν όμως τα κύρια προγράμματα μεγαλώνουν, όπως μεγαλώνουν κι οι απαιτήσεις, δεν είναι δυνατόν και συνήθως ούτε ασφαλές να βλέπουν όλοι όλα τα δεδομένα. Επίσης τα πράγματα δυσκολεύουν όταν η συμπεριφορά που επιθυμούμε δεν είναι μια απλή κλήση σε μια συνάρτηση, αλλά συμπεριφορά μιας συνάρτησης που διαφέρει ανάλογα με τα δεδομένα. Προχωρώντας λοιπόν στον αντικειμενοστραφή προγραμματισμό, ορίζουμε την έννοια του αντικειμένου. Κάθε αντικείμενο έχει τα δικά του δεδομένα, τα οποία μπορεί να μοιράζεται με άλλους, μπορεί και όχι, και την δική του συμπεριφορά που μπορεί να καθορίζεται από τον τύπο του αντικειμένου, από τα δεδομένα του και τα λοιπά. Έτσι μια φαινομενικά απλή πράξη, όπως η πρόσθεση, μπορεί να έχει εντελώς διαφορετική εκτέλεση αναλόγα με το αντικείμενο που την εκτελεί: - Ένας ακεραίος θα μας έδινε την γνωστή μαθηματική πρόσθεση 3

9 - Η πρόσθεση όμως δύο συμβολοσειρών είναι κάτι εντελώς διαφορετικό, κι όμως υπακούει στην ίδια εντολή (+) Η Objective C ως επέκταση της C Η Objective C είναι ένα υπερσύνολο της C με την αυστηρή έννοια. Είναι δυνατόν να συνθέσουμε (compile) οποιοδήποτε πρόγραμμα γραμμένο σε C σε έναν οποιονδήποτε συνθέτη γλώσσας Objective C (compiler). Έτσι προφανώς μπορούμε να συμπεριλάβουμε κώδικα C μέσα ένα πρόγραμμα γραμμένο σε Objective C, χωρίς καμία απολύτως επιπλοκή. Μια τέτοια επιλογή θα είχε λογική για εξοικονόμηση πόρων του συστήματος, όπως μνήμη, χρόνο και υπολογιστική ισχύ. Ως γλώσσα αρκετά υψηλού επιπέδου, η Objective C είναι αρκετά πιο «ακριβή» σε ισχύ από μια γλώσσα όπως η C. Ως επέκταση της C, όλο το συντακτικό που αφορά μη αντικειμενοστραφείς λειτουργίες είναι αυτό της C, ενώ για το υπόλοιπο κομμάτι, η Objective C ακολουθεί το συντατικό όπως ακολουθεί το στυλ των μηνυμάτων Smalltalk. Η Smalltalk είναι μια άλλη γλώσσα προγραμματισμού, που αναπτύχθηκε τη δεκατία του 80, και έχει τον εξής ορισμό για την έννοια του αντικειμένου: Ένα αντικείμενο μπορεί να κάνει ακριβώς τρία πράγματα: 1. Να έχει μια εσωτερική κατάσταση (συνήθως και με αναφορές σε άλλα αντικείμενα) 2. Να λαμβάνει μηνύματα από τον εαυτό του ή από κάποιο άλλο αντικείμενο 3. Καθώς επεξεργάζεται ένα μήνυμα, μπορεί να στείλει μηνύματα στον εαυτό του ή σε άλλο αντικείμενο Έτσι μια «κλήση» σε μια μέθοδο, ή γενικότερα όπως αναφέρεται, η αποστολή ενός μηνύματος, ακολουθεί το παρακάτω συντακτικό: [obj method:argument]; Όπου obj το αντικείμενο, method η μέθοδός του που καλούμε και argument η πρώτη από τις παραμέτρους που απαιτεί η μέθοδος. Μια λίγο πιο γενική μορφή είναι η: [obj method:argument1 moremethodtext:argument2 methodfinaltext:argumentn]; Η χρήση των τετράγωνων παρενθέσεων (square brackets [] ) λοιπόν σημαίνει την κλήση σε μια μέθοδο, την αποστολή μηνύματος σε ένα αντικείμενο, και επίσης όπως κάθε εντολή της C, τελειώνει με το ελληνικό ερωτηματικό (semicolon ; ). 4

10 Αρχεία Objective C Ας δούμε την μορφή των αρχείων ενός προγράμματος γραμμένου σε Objective C. Γνωστά και από την C, έχουμε τα αρχεία κεφαλίδων (header files), με δηλώσεις ονομάτων συναρτήσεων ή και μεταβλητών, και τα αρχεία κώδικα (code files), που παρέχουν την υλοποίηση. Στην Objective C, τα header files, έχουν κατάληξη.h, και περιέχουν την «διεπαφή» μιας κλάσης (interface). Δηλώνονται όσες μεταβλητές χρησιμοποιούνται από τις περισσότερες μεθόδους (όπως θα δούμε παρακάτω, η Objective C είναι δυναμική γλώσσα και επιτρέπει την υλοποίηση μεταβλητών και δέσμευση μνήμης κατά τη διάρκεια εκτέλεσης (run time), όχι μόνο κατά τη διάρκεια σύνθεσης (compilation time)) και οι μορφές των μεθόδων της classname : superclassname // instance variables + classmethod1; + (return_type)classmethod2; + (return_type)classmethod3:(param1_type)param1_varname; - (return_type)instancemethod1:(param1_type)param1_varname : (param2_type)param2_varname; - (return_type)instancemethod2withparameter : (param1_type)param1_varname Όπου με άνω κάτω τελεία (colon) μετά το όνομα της κλάσης (classname) δηλώνουμε την κληρονομικότητα (inheritance), δίνοντας το όνομα της άλλης κλάσης από την οποία λάμβανει τα κληρονομικά χαρακτηριστικά, γνωστή και ως υπερ-κλάση (superclass). Αν δεν έχουμε κάποια κλάση που επιθυμούμε να κάνουμε subclass, τότε πάντα πρέπει να κάνουμε subclass την NSObject, η οποία είναι η βασική κλάση (root class). Οι μέθοδοι με το σύμβολο συν ( + ) αριστερά αναγνωρίζονται ως μέθοδοι κλάσης (class methods), ενώ αυτές με το σύμβολο πλην ( - ) είναι μέθοδοι στιγμιοτύπου (instance methods). Ο τύπος του αντικειμένου που επιστρέφουν αναγράφεται αμέσως μετά, μέσα σε παρενθέσεις, και μπορεί να είναι void (κοινώς να μην επιστρέφει κάτι), κάποιος πρωτογενής τύπος της C (int, float, κλπ), δείκτης σε ένα αντικείμενο συγκεκριμένου τύπου, από αυτά που ορίζουν οι βιβλιοθήκες της Objective C, ή τέλος, δείκτης σε αντικείμενο του οποίου τον τύπο έχει ορίσει ο προγραμματιστής. 5

11 Τα αρχεία κώδικα, περιέχουν την υλοποίηση των μεθόδων που αναφέρονται στα αρχεία κεφαλίδων. Αρχικά σημαίνοντας μηνύματα (messages), τα αρχεία αυτά έχουν την κατάληξη.m, και έχουν την ακόλουθη classname + (return_type)classmethod // implementation - (return_type)instancemethod // Η εσωτερική αναπαράσταση της Objective C για τις μεθόδους διαφέρει από υλοποίηση σε υλοποίησή της. Για παράδειγμα, μια μέθοδος: - (int)changecolortored:(float)red green:(float)green blue:(float)blue; Μπορεί να αναπαρίσταται εσωτερικά ως: _i_color_changecolortored_green_blue όπου το i δηλώνει μέθοδο στιγμιότυπου, το Color είναι το όνομα της κλάσης, και ακολουθεί το όνομα της μεθόδου. Ωστόσο δεν έχει νόημα η χρήση των εσωτερικών αυτών αναπαραστάσεων καθώς η Objective-C είναι δυναμική και συνήθως ο παραλήπτης του μηνύματος δεν είναι γνωστός παρά κατά την ώρα εκτέλεσης (run time). Πραγμάτωση στιγμιοτύπων Αφού μια κλάση έχει γραφεί σε κώδικα, για να πραγματοποιηθεί χρειάζονται δύο βήματα: - Το πρώτο δεσμεύει χώρο στην μνήμη για ένα μη αρχικοποιημένο στιγμιότυπο της κλάσης - Το δεύτερο το αρχικοποιεί Μόνο άν γίνουν και τα δύο βήματα μπορεί να χρησιμοποιηθεί το αντικείμενο. Συνήθως αυτά τα δύο βήματα γίνονται σε μια γραμμή, για να είναι σίγουρο ότι ένα αντικείμενο έχει δεσμευτεί και αρχικοποιηθεί, καθώς επίσης επειδή το ενδιάμεσο αντικείμενο δεν είναι κάτι το οποίο θα χρησιμοποιηθεί, και μερικές φορές δεν συμπίπτει καν με το αντικείμενο που επιστρέφει η αρχικοποίηση. MyObject *o = [[MyObject alloc] init]; 6

12 Ο παραπάνω τρόπος είναι ο δεδομένος για όλες τις κλάσεις. Πέρα από αυτόν μπορούν να γραφούν διαφορετικοί προσαρμοσμένοι τρόποι (στο κομμάτι της αρχικοποίησης, όχι στο κομμάτι της δέσμευσης της μνήμης). Για παράδειγμα: MyObject *o = [[MyObject alloc] initwithstring:mystring]; Στην πρώτη περίπτωση, που δεν έχουμε διαφορετική αρχικοποίηση, μπορεί να χρησιμοποιηθεί και η μέθοδος new: MyObject *o = [MyObject new]; Το μήνυμα alloc δεσμεύει αρκετό χώρο για όλες τις μεταβλητές που δηλώνει το στιγμιότυπο, δίνει σε όλες την τιμή μηδέν και μετατρέπει τη μνήμη σε ένα στιγμιότυπο της κλάσης. Το μήνυμα init αρχικοποιεί το στιγμιότυπο αφού δημιουργηθεί. Η υλοποίηση του μηνύματος έχει την ακόλουθη μορφή: - (id)init self = [super init]; if (self) // perform initialization of object here return self; Το id είναι ένας τύπος αντικειμένου που σημαίνει «δείκτης σε οτίδηποτε αντικείμενο». Μετατρέπεται σε οποιοδήποτε αντικείμενο κατά την ώρα εκτέλεσης (ξανά ένα από τα στοιχεία που καθιστούν την Objective-C δυναμική γλώσσα προγραμματισμού). Γίνεται πρώτα μια κλήση στην υπερκλάση και αναθέτει το αποτέλεσμα στο ίδιο το αντικείμενο (self). Αν είναι ένα έγκυρο αντικείμενο γίνονται οι αρχικοποιήσεις. Ένα μη έγκυρο αντικείμενο (ή καλύτερα ένας μη έγκυρος δείκτης σε αντικείμενο), έχει την τιμή nil (γνωστό ως Null σε άλλες γλώσσες). Έτσι σε περίπτωση σφάλματος επιστρέφεται η τιμή nil. Πρωτόκολλα Ένα επίσης νέο στοιχείο της Objective-C είναι τα πρωτόκολλα (protocols). Επειδή η Objective-C δεν υποστηρίζει πολλαπλή κληρονομικότητα (σε άμεση υλοποίηση), την υποστηρίζει έμμεσα μέσω των πρωτοκόλλων. 7

13 Ξεχωρίζουμε τα πρωτόκολλα σε επίσημα και ανεπίσημα (formal & informal). Τα επίσημα πρωτόκολλα ορίζουν μεθόδους που η κλάση που το εφαρμόζει υποχρεούται να υλοποιήσει, ενώ τα ανεπίσημα ορίζουν προαιρετικές μεθόδους. Για να δηλώσει μια κλάση ότι υποστηρίζει ένα πρωτόκολλο, το αναφέρει στο Interface της, ως SomeClass : SomeSuperClass ενώ το ίδιο το πρωτοκόλλο ορίζεται ως Locking - (void)lock; - Η ντιρεκτίβα #import Αντίστοιχα με το #include της C, η Objective C έχει την ντιρεκτίβα #import, που συμπεριλαμβάνει τα αρχεία που την ακολουθούν στον κώδικα κατά τη διάρκεια της σύνθεσης (compilation). Το πλεονέκτημά της σε σχέση με την #include, είναι ότι συμπεριλαμβάνει τα αρχεία μόνο μια φορά, καθιστώντας αχρείαστη την προφύλαξη σε περιπτώσεις διπλής εισαγωγής. Ένα αρχείο κώδικα (.m) συμπεριλαμβάνει πάντα το αρχείο κεφαλίδας (header file) για την διεπαφή του (interface), καθώς και οποιαδήποτε πρωτόκολλα στα οποία συμορφώνεται, ή άλλα αρχεία κεφαλίδων άλλων κλάσεων των οποίων στιγμιότυπα χρησιμοποιεί. Τύποι Δεδομένων Όπως έχει ήδη αναφερθεί πιο πάνω, η Objective C υποστηρίζει τους γνωστούς πρωτογενείς τύπους δεδομένων της C, όπως int, float, double, char και το id, το οποίο όπως είπαμε είναι ένας δείκτης σε αντικείμενο οποιασδήποτε κλάσης, το οποίο μπορεί να αλλάξει δυναμικά τύπο στην εκτέλεση του προγράμματος (ή καλύτερα, απλά δεν είναι γνωστός ο τύπος του κατά την σύνθεση του κώδικα). Υπάρχει επίσης ο ψευδο-τύπος BOOL για τις δυαδικές μεταβλητές (boolean) οι οποίες ουσιαστικά εκφυλίζονται από YES/NO σε 1/0. Πέρα από αυτούς τους τύπους, ανάλογα με το framework που συμπεριλαμβάνει η εφαρμογή μας, υπάρχουν έτοιμες κλάσεις των οποίων μπορούμε να δημιουργήσουμε αντικείμενα. 8

14 Για παράδειγμα, για το Foundation Framework (NSFoundation), έχουμε εξ αρχής πρόσβαση σε χρήσιμες κλάσεις όπως την NSString, NSArray, NSNumber και τα λοιπά. Καθώς το string (αλφαριθμητικό) είναι από τους πιο συχνά και ευρέως χρησιμοποιούμενους τύπους δεδομένων, υπάρχει η δυνατότητα να κατασκευαστεί στιγμιότυπο, χωρίς την διαδικασία που αναφέραμε πιο πάνω. Απλά Οποιοδήποτε κείμενο εδώ έχουμε δημιουργήσει ένα στιγμιότυπο της κλάσης NSString (το οποίο όμως δεν γίνεται deallocate ποτέ, το σύστημα το βλέπει και το αποθηκεύει ως δεδομένο και καταλήγει στο κομμάτι δεδομένων της εφαρμογής). Σχετικά κοντά στην ιδέα των τύπων δεδομένων είναι οι επιλογείς (selectors), οι οποίοι κατά μια έννοια είναι δείκτες σε συναρτήσεις. Βέβαια, λόγω του δυναμικού χαρακτήρα της Objective C, δεν είναι ακριβώς δείκτες σε μια συγκεκριμένη συνάρτηση, αλλά σε οποιαδήποτε συναρτήση έχει το δεδομένο όνομα του selector. Έστω μια κλήση σε μια μέθοδο ενός αντικειμένου: [δέκτης μέθοδος: παράμετρος]; Ολόκληρη η γραμμή αυτή, δηλώνει το πέρασμα ενός μηνύματος σε έναν δέκτη. Το μήνυμα είναι το κομμάτι μέθοδος: παράμετρος, ο δέκτης είναι ένα στιγμιότυπο μιας κλάσης (ή η ίδια η κλάσης σε περιπτώσεις στατικών μεθόδων) και ο επιλογέας θα είναι το κομμάτι μέθοδος:. Η υλοποίηση της μεθόδου γίνεται κάπου αλλού, αλλά μέσω του selector μπορεί να γίνει αναφορά σε αυτήν τη μέθοδο. Μέθοδοι Πρόσβασης (Accessor Methods) Στην Objective C τα δεδομένα και οι μεταβλητές μιας κλάσης είναι προσβάσιμα μόνο από την ίδια. Αν όμως θέλουμε να περάσουμε ή να χρησιμοποιήσουμε την τιμή μιας μεταβλητής σε κάποια άλλη κλάση, πρέπει να έχουμε τρόπο να αναφερθούμε σε αυτήν. Μπορούμε να γράψουμε δικές μας συναρτήσεις που θα υλοποιούν αυτό το κομμάτι. Αυτές αναφέρονται γενικά ως μέθοδοι τοποθέτησης και ανάκτησης (setter & getter methods). Η σύμβαση που ακολουθείται είναι η εξής: variable = [object varname]; 9

15 Και [object setvarname: value]; με προσοχή πάντα το πρώτο γράμμα στο όνομα της μεταβλητής μετά το set να είναι κεφαλαίο. Στην δήλωση των μεθόδων αυτών έχουμε προνοήσει να έχουμε δηλώσει σωστά τους τύπους, δηλαδή: //Δήλωση μεταβλητών int age; //Δήλωση μεθόδων - (int) age; - (void) setage: (int) newage; Ακριβώς την ίδια λειτουργικότητα προσφέρουμε και σε οποιοδήποτε αντικείμενο αν χρησιμοποιήσουμε //Δήλωση μεταβλητών int age; NSString *name; //Δήλωση μεθόδων - (int) age; - (void) setage: (int) newage; - (NSString *) name; - (void) setname: (NSString *) newname; Οι τελευταίες γραμμές μπορούν να συμπτιχθούν σε δύο, αν χρησιμοποιήσουμε τα int (copy) NSString *name; Μέσω των properties μπορούμε να συμπιέσουμε τον κώδικα, αλλά μας δίνει επίσης την δυνατότητα της διαχείρισης μνήμης (εκεί αναφέρεται το (copy) στην μεταβλητή name), και σε περίπτωση που δεν θέλουμε να υπάρχει setter, υπάρχει η δυνατότητα δημιουργίας read-only property με την προσθήκη του (readonly) μετά Αυτά για το κομμάτι δηλώσεων, που είναι στο.h αρχείο, δηλαδή στο interface της κλάσης. Στο.m αρχείο που θα γράφαμε την υλοποίηση της μεθόδου γράφουμε name; 10

16 και δεν θα υπήρχε ανάγκη να συμπληρώσουμε κάτι παραπάνω. Στην περίπτωση που θέλαμε να έχουμε μια πιο σύνθετη ανάθεση ή ανάγνωση τιμών (π.χ. να αυξάνουμε και ένα μετρητή ή οποιαδήποτε άλλη λειτουργία), θα θέλαμε να γράψουμε δική μας μέθοδο. Μπορούμε και πάλι να χρησιμοποιήσουμε τα properties και απλά γράφοντας τον κώδικα για μια από τις μεθόδους, αγνοείται ο αρχικός κώδικας και εκτελείται ο δικός μας (method override). Υπάρχει επίσης η δυνατότητα, η μεταβλητή για την οποία κάνουμε το synthesize, να έχει διαφορετικό όνομα από την μεταβλήτη του στιγμιοτύπου. Για ClassName :NSObject int age = Έτσι καλώντας την [obj setage: someintvalue], αυτό που θα έπαιρνε την τιμή someintvalue θα ήταν η μεταβλητή στιγμιότυπου numberofyearsold. Σύνταξη με τελεία (Dot syntax) Μετά από την έκδοση της Objective C 2.0, υποστηρίζεται και η σύνταξη με την τελεία για τις κλήσεις μεθόδων. Κοινώς η εντολή: self.height = 50; Είναι ισοδύναμη με την εντολή: [self setheight:50]; Και αξίζει να σημειωθεί ότι η σύνταξη με την τελεία πάντα θα καλεί τις μεθόδους τοποθέτησης και ανάκτησης (setter & getter). Αυτό έχει σημασία γιατί δεν γίνεται απλή ανάθεση τιμών, αλλά ακολουθείται η διαδικασία διαχείρησης μνήμης που έχει δηλωθεί στην ιδιότητα (property). 11

17 2.2 Διαχείρηση Μνήμης Ένα πάρα πολύ σημαντικό κομμάτι στην Objective C είναι αυτό της διαχείρησης μνήμης. Ειδικά στην περίπτωση των iphone, που οι πόροι του συστήματος είναι θεωρητικά πολύ πιο περιορισμένοι από ότι σε ένα σταθερό υπολογιστή, είναι πάρα πολύ σημαντικό να γίνεται πολύ καλή διαχείρηση μνήμης, αλλιώς είναι πολύ εύκολο το σύστημα να τερματίσει μια εφαρμογή όταν αρχίσει η μνήμη να γεμίζει. Επίσης ως δυναμική γλώσσα προγραμματισμού, επιτρέπει την δέσμευση και αποδέσμευση χώρου για μεταβλητές και αντικείμενα δυναμικά, χωρίς δηλαδή να είναι γνωστό το μέγεθος κατά την σύνθεση του προγράμματος, παρά μόνο κατά την εκτέλεση. Κατ αντιστοιχία με την C, η οποία υποστήριζε τις εντολές malloc και free, για δέσμευση και απελευθέρωση χώρου στη μνήμη, η Objective C υποστηρίζει τις alloc και dealloc Alloc, Retain, Copy και Release Έχουμε ήδη αναφέρει την διαδικασία δημιουργίας ενός στιγμιοτύπου, και ότι είναι απαραίτητο ένα αντικείμενο να δεσμεύεται και να αρχικοποιείται στην ίδια γραμμή. Αυτό διότι σε κάποιες σπάνιες περιπτώσεις, η init που κάνει την αρχικοποίηση επιστρέφει (δείκτη πάντα, σε ) διαφορετικό αντικείμενο από αυτό που έκανε την κλήση της. Επίσης, καθώς ένα αντικείμενο ουσιαστικά δεν είναι χρήσιμο αναρχικοποίητο, δεν έχει νόημα να κρατάμε δείκτη σε αυτό. Έτσι έχουμε ήδη κάνει το πρώτο κομμάτι, αυτό της δέσμευσης της μνήμης. Μένει μόνο όταν τελειώσουμε με την εργασία που χρειαζόμαστε το αντικείμενο, να αποδεσμεύσουμε αυτήν τη μνήμη. Πρέπει να λοιπόν να κληθεί η dealloc στο αντικείμενο, και έχει επέλθει ισορροπία. Αν δεν το κάνουμε, και απλά «ξεχάσουμε» τον δείκτη που έχουμε, έχουμε τις λεγόμενες διαρροές μνήμης (memory leaks), οι οποίες αν ξεφύγουν σε πλήθος, μπορεί να αναγκάσουν το σύστημα να τερματίσει την εφαρμογή μας. Επίσης η εφαρμογή μπορεί να τερματίσει απρόσμενα και λανθασμένα, αν υπάρξει κλήση σε ένα δείκτη που δείχνει σε μνήμη που έχει αποδεσμευτεί (bad memory access), ή αν προσπαθήσουμε να αποδεσμεύσουμε μνήμη που έχει ήδη αποδεσμευτεί (βασικά επειδή πρόκειται για την προηγούμενη περίπτωση, στέλνουμε ένα μήνυμα σε μη έγκυρη περιοχή μνήμης). 12

18 Συνήθως βέβαια δεν καλείται απ ευθείας μες στον κώδικα η εντολή dealloc. Αυτό που χρησιμοποιείται, είναι ένας αριθμός που δείχνει πότε ένα αντικείμενο πρέπει να καταστραφεί. Αυτό είναι το retain count. Κάθε αντικείμενο, όταν δημιουργείται από εντολές alloc ή copy, ξεκινά με retain count ίσο με 1. Μπορούμε να αυξήσουμε αυτόν τον αριθμό κατά ένα καλώντας την εντολή retain, και να τον μειώσουμε κατά ένα καλώντας την εντολή release. Με το που φτάσει το retain count στο 0, το αντικείμενο καταστρέφεται, καλείται η dealloc, και δεν υπάρχει κανένας απολύτως τρόπος να το ανακτήσουμε. Και γιατί να θέλουμε να αυξήσουμε αυτόν τον αριθμό? Ας σκεφτούμε την παρακάτω περίπτωση: Έστω ένας αριθμός, στιγμιότυπο της κλάσης NSNumber για να μιλάμε για αντικείμενο, και δύο ξεχωριστές μέθοδοι, η μια θέλει να τον προσθέσει με κάτι και η άλλη να τον εκτυπώσει 50 φορές. Αν η πρώτη μέθοδος με το που τελειώσει αποδεσμεύσει τη μνήμη του αριθμού, η δεύτερη θα συναντήσει κακή μνήμη. Θα μπορούσαμε να βάλουμε την δεύτερη μέθοδο να κάνει την αποδέσμευση. Αν όμως αντίθετα για κάποιο λόγο τερματίσει η δεύτερη μέθοδος πρώτη (όχι απαραίτητα λόγο φόρτου εργασίας, αλλά για παράδειγμα λόγω χρονοπρογραμματισμού διεργασιών του συστήματος), θα έχουμε το ίδιο πρόβλημα. Η λύση: Όταν η πρώτη μέθοδος πάρει το αντικείμενο, το κάνει retain. Συνεπώς το retain count αυξάνεται κατά ένα. Αντίστοιχα και η δεύτερη. Όταν τελειώσουν, η κάθε μία κάνει από ένα release στο αντικείμενο. Συνεπώς το retain count είναι στο ίδιο νούμερο που ήταν πριν αρχίσει η διαδικασία, και πλέον ουσιαστικά είναι ευθύνη του δημιουργού του να το αποδεσμεύσει. Αν ήταν αυτή η πρώτη διαδικασία που το δημιούργησε (ξεκίνησε δηλαδή το retain count από 0 και έγινε 1 με ένα alloc), το αντικείμενο απελευθερώνεται με το που γίνει το δεύτερο release (αφού το count έχει επανέλθει στο 0). Τέλος να αναφέρουμε ότι η dealloc καλείται σε μια και μόνο περίπτωση ευθέως. Αφού έχουμε κάνει release όλα τα αντικείμενα για τα οποία είμαστε υπεύθυνοι, καλούμε την dealloc στην υπερκλάση της κλάσης μας, ως εξής: [var1 release]; [var2 release]; [super dealloc]; 13

19 Όπου το super υποδεικνύει πέρασμα μηνύματος σε υπερκλάση. Με άλλα λόγια, αφού έχουμε «ξεκαθαρίσει» όση μνήμη είμαστε εμείς υπεύθυνοι, περνάμε τον έλεγχο προς τα πάνω για να «καθαριστούν» και τα υπόλοιπα. Μην ξεχνάμε ότι υπερκλάση όλων, κλάση-ρίζα είναι το NSObject, και όλες οι κλήσεις εν τέλει θα καταλήξουν εκεί. Αυτή η διαδικασία είναι πολύ εμφανής και στην ανάπτυξη εφαρμογών, καθώς τα View Controllers όπως θα δούμε, καθορίζουν την δική τους dealloc, όπου εμείς πρέπει να γράψουμε τι διαχειρήσεις γίνονται, και είναι ευθύνη του συστήματος να την καλέσει Setter μέθοδοι, πιο αναλυτικά Έχοντας μια πρώτη απόψη για την διαχείρηση της μνήμης, μπορούμε να δούμε λίγο καλύτερα τι κάνει μια setter μέθοδος, όταν μιλάμε για αντικείμενα (δεν μιλάμε για τους πρωτογενείς τύπους της C, καθώς σε αυτούς δεν αναφερόμαστε συνήθως με δείκτες, οπότε οι περιπτώσεις που περιγράφονται παρακάτω συνήθως δεν εφαρμόζονται): Υπάρχουν 3 διαφορετικοί τρόποι που θα μπορούσαμε να κάνουμε μια τοποθέτηση «τιμής» (ουσιαστικά γίνεται αντιστοίχιση δεικτών - αντικειμένων). - (void)setname:(nsstring *)newname name = newname; Αυτός ο τρόπος, παίρνει τον δείκτη της τοπικής μεταβλητής name, και τον βάζει να δείχνει όπου δείχνει ο newname. Το κακό με αυτόν τον τρόπο είναι ότι δεν είμαστε ιδιοκτήτες αυτού του αντικειμένου. Ανά πάσα στιγμή, όποιοσδήποτε έχει ένα δείκτη στο αντικείμενό μας, μπορεί να το αποδεσμεύσει και να μείνουμε με ένα δείκτη σε κακή μνήμη. - (void)setname:(nsstring *)newname if (name!= newname) [name release]; name = [newname retain]; Αυτός ο τρόπος είναι καλύτερος από την άποψη του ότι, ότι και να γίνει, εμείς θα έχουμε πάντα έναν έγκυρο δείκτη στο αντικείμενο, αφού του αυξήσαμε το retain count κατά 1. Εφόσον δεν χρειαζόμαστε το παλιό αντικείμενο, το κάνουμε release, για να έρθει το retain count σε ισορροπία. Ο έλεγχος στην αρχή έχει το εξής νόημα. Αν για κάποιο λόγο περαστεί ως παράμετρος το ίδιο το αντικείμενο, αν το κάνουμε release και είμαστε οι μόνοι που έχουμε δείκτη σ αυτό, η αμέσως επόμενη εντολή 14

20 θα καταλήξει σε πρόσβαση σε κακή μνήμη. Με αυτό τον έλεγχο προλαβαίνουμε αυτή την περίπτωση. - (void)setname:(nsstring *)newname if (name!= newname) [name release]; name = [newname copy]; Αυτός ο τρόπος είναι ίδιος με τον δεύτερο, με τη μόνη διαφορά ότι αντί να αυξάνουμε το retain count, δημιουργούμε ένα νέο αντικείμενο, αντίγραφο του αρχικού και βάζουμε τον δείκτη να δείχνει εκεί. Το retain count μετά από το copy είναι 1. Αυτοί ακριβώς οι 3 τρόποι είναι αυτοί που υποστηρίζονται από με (assign) ClassName (retain) ClassName (copy) ClassName *varname Συχνά παρεμβάλεται και το πρόθεμα non-atomic, ως (nonatomic, copy) ClassName *varname Με την έννοια ότι οι μέθοδοι setter και getter δεν είναι ατομικές. Αν παραλείπεται, θα θεωρούνται ατομικές. Ατομικότητα μιας μεθόδου ορίζεται ως η ιδιότητα ότι δεν μπορεί να τμηθεί. Με άλλα λόγια, αυτή η μέθοδος είτε εκτελείται είτε όχι. Δεν υπάρχει η περίπτωση να διακοπεί στην μέση μιας ανάθεσης. Η ατομικότητα παίζει πολύ σημαντικό ρόλο στον συγχρονισμό διεργασιών και νημάτων, αλλά κάνει το σύστημα πιο «δυσκίνητο», καθώς όταν εκτελείται μια τέτοια διεργασία, δεν επιτρέπεται να εκτελεστεί παράλληλα κάποια άλλη η οποία πιθανόν να χρειαστεί την μεταβλητή (πιθανόν και όχι). Γενικότερα χρησιμοποιείται αρκετά αυτό το πρόθεμα, καθώς οι περισσότερες εφαρμογές είναι ενός νήματος (single threaded) και δεν απαιτούνται πολύπλοκες διαδικασίες συγχρονισμού Autorelease και Autorelease pool Ως τώρα έχουμε δει πως μπορούμε να δημιουργήσουμε ένα αντικείμενο και να κρατήσουμε ένα δείκτη σε αυτό, και πως αν πάρουμε ένα αντικείμενο, να κρατήσουμε ένα δείκτη σε αυτό και να είμαστε σίγουροι πως όσο τον κρατάμε, το αντικείμενο δεν πρόκειται να αποδεσμευτεί. 15

21 Αν όμως θέλουμε να δημιουργήσουμε ένα αντικείμενο με τον σκοπό του να το περάσουμε ή να το δώσουμε σε κάποιον άλλο, πρέπει να δώσουμε σε αυτόν τον άλλον να καταλάβει και πως πρέπει να το διαχειριστεί. Το αν θα πρέπει να το αποδεσμεύσει η άλλη κλάση ή αυτή που το έδωσε, πρέπει να φαίνεται στο όνομα της μεθόδου, και αυτό είναι μια σύμβαση που πρέπει να ακολουθείται, προκειμένου να μην υπάρχουν μπερδέματα και κακή διαχείρηση μνήμης. Αν το όνομα της μεθόδου περιέχει κάποια από τις λέξεις alloc, copy, ή new, τότε ο παραλήπτης πρέπει να κάνει το release. Διαφορετικά πρέπει να το κάνει η δημιουργός μέθοδος. Πως όμως θα ξέρει η μέθοδος που το δημιούργησε, πότε πρέπει να το αποδεσμεύσει? Για παράδειγμα: - (NSString *)fullname NSString *result; result = [[NSString alloc] initwithformat:@ %@ %@, firstname, lastname]; [result release]; return result; Με αυτή την μέθοδο, έχουμε ήδη αποδεσμεύσει το αντικείμενο, πριν καν το επιστρέψουμε σε αυτόν που κάλεσε τη μέθοδο. Η λύση: - (NSString *)fullname NSString *result; result = [[NSString alloc] initwithformat:@ %@ %@, firstname, lastname]; [result autorelease]; return result; Με την λέξη-κλειδί autorelease, μαρκάρουμε το αντικείμενο για να γίνει release σε κάποια μελλοντική στιγμή, και όχι αμέσως. Έτσι όποιος κάνει την κλήση μπορεί ελεύθερα να το κάνει retain και να κρατήσει ένα δείκτη στο αντικείμενό του. Αντίστοιχα από την μεριά του δημιουργού, ξέρει ότι έχει εξισορροπήσει τις κλήσεις του σε retain και release, αφού το αντικείμενο θα γίνει release αργότερα. Για να δούμε πότε θα γίνει αυτό, στρεφόμαστε στην ιδέα του autorelease pool. Ένα τέτοιο pool δημιουργείται σε κομβικά θα λέγαμε σημεία, και κάθε αντικείμενο που δημιουργείται και μαρκάρεται ως autoreleased μπαίνει εκεί μέσα. Όταν τελειώσει η όποια διεργασία, το pool γίνεται release, και αυτό αυτόματα καλεί το release σε όλα 16

22 τα αντικείμενα που είχε. Είναι ένας πολύ κομψός και βολικός τρόπος διαχείρησης μνήμης, και προσθέτει πολύ μεγάλη ευελιξία στον κώδικα. Μπορούμε επίσης να έχουμε εμφωλευμένα pools, χωρίς κανένα πρόβλημα απολύτως. Επίσης το πρώτο πράγμα που κάνει η εφαρμογή που φτιάχνουμε, είναι να δημιουργήσει ένα τέτοιο pool, και το τελευταίο που κάνει είναι το κάνει release. Αυτό φαίνεται ξεκάθαρα στο main.m αρχείο που δημιουργείται για μας αυτόματα, και περιέχει απλά τον παρακάτω κώδικα: int main(int argc, char *argv[]) NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int retval = UIApplicationMain(argc, argv, nil, nil); [pool release]; return retval; Αυτή είναι η main συνάρτηση που καλείται πριν καν ξεκινήσουν οι διαδικασίες αρχικοποίησης για την εφαρμογή μας, και όλη η εφαρμογή τρέχει από την UIApplicationMain(). Επίσης μπορούμε να ορίσουμε εμείς δικό μας autorelease pool σε σημείο που ξέρουμε ότι θα δεσμεύσουμε αρκετή μνήμη, την οποία δεν θα χρειαζόμαστε μετά. Δημιουργούμε όσα αντικείμενα χρειαζόμαστε, φροντίζουμε να τα μαρκάρουμε ως autoreleased, και στο τέλος απλά αδειάζουμε το pool, κάνοντάς το release. Είναι πολύ σημαντικό αυτό να εφαρμόζεται και σε περίπτωση που δημιουργούμε ξεχωριστά νήματα, καθώς κάθε νήμα πρέπει να βλέπει την δική του μνήμη και να την διαχειρίζεται κατάλληλα (πέρα φυσικά από όποια επικοινωνία μπορεί να υπάρχει μεταξύ των νημάτων). 17

23 2.3 Προγραμματισμός για iphone και το περιβάλλον του Xcode Για την ανάπτυξη της εφαρμογής χρησιμοποιήθηκε η πλατφόρμα του Xcode, που δίνει πολλά βασικά στοιχεία για να ξεκινήσει κανείς από το μηδέν να δημιουργήσει μια εφαρμογή Model, View, Controller (MVC) Ο προγραμματισμός στο περιβάλλον του iphone ακολουθεί το μοντέλο ανάπτυξης MVC (Model View Controller), κοινώς διαχωρίζει την εφαρμογή σε τρία κομμάτια. Το μοντέλο (model), που έχει τα δεδομένα που είναι απαραίτητα στην εφαρμογή, είτε για να τρέξει και να αρχίσει, είτε τα δεδομένα που έχει σκοπό η εφαρμογή να παρουσιάσει. Δεν θα πρέπει να έχει καμία εξάρτηση από τον τρόπο παρουσίασής τους. Ένα καλό μοντέλο μπορεί να απεικονιστεί από εντελώς διαφορετικούς τρόπους παρουσίασης ή διαφορετικές διεπαφές. Συνήθως είναι δεδομένα αποθηκευμένα σε κάποια βάση δεδομένων (SQLite, CoreData) ή κάπου στο διαδίκτυο. Η εικόνα της εφαρμογής (view), που είναι αυτό το οποίο ο χρήστης βλέπει. Αυτό το κομμάτι είναι υπεύθυνο για την παρουσίαση των δεδομένων στο χρήστη, χωρίς να τα αποθηκεύει πουθενά. Μέσω του View, μπορεί ο χρήστης να επηρεάσει τα δεδομένα, αλλά όχι άμεσα. Μια σωστή απεικόνιση πρέπει να μπορεί να επαναχρησιμοποιηθεί, και να μην εξαρτάται από τα δεδομένα που παρουσιάζει αλλά να προσαρμόζεται εύκολα και κατάλληλα. Τέλος, ο ελεγκτής (controller). Είναι το κομμάτι που μεσολαβεί ανάμεσα στο μοντέλο και την απεικόνιση. Είναι ευθύνη του ελεγκτή να ενημερώσει την απεικόνιση αν έχουν αλλάξει τα δεδομένα στο μοντέλο, και ευθύνη του να αλλάξει τα δεδομένα αν έρθει τέτοιο μήνυμα από την απεικόνιση. Είναι το βασικό κομμάτι της εφαρμογής που διαθέτει το μεγαλύτερο κομμάτι της λογικής του κώδικα που θα γράψουμε. Συνεπώς το κομμάτι του κώδικα που γράφουμε που καθορίζει την εφαρμογή μας είναι αυτό του ελεγκτή. Συγκεκριμένα το περιβάλλον του Xcode διευκολύνει την εφαρμογή αυτού του μοντέλου ανάπτυξης, διαχωρίζοντας αυτά τα τρία κομμάτια. Πέρα από το βασικό περιβάλλον που γράφουμε τον κώδικα, υπάρχει ένα ξεχωριστό κομμάτι, το Interface Builder, που απευθύνεται στην δημιουργία των views. Είναι μια γραφική διεπαφή που μας επιτρέπει να προσθέτουμε στοιχεία όπως κουμπιά, διακόπτες και άλλα, κάνοντας drag & drop, χωρίς να γράψουμε ούτε μια γραμμή 18

24 κώδικα. Σε εμάς μένει μόνο να κάνουμε τη διασύνδεση των στοιχείων του view με τις πράξεις στον ελεγκτή. Τα αρχεία που δημιουργούνται ονομάζονται nib files και έχουν την κατάληξη.xib. Όσο για το μοντέλο, υποστηρίζονται βάσεις δεδομένων SQLite (μια μορφή για σχετικά πιο μικρές βάσεις δεδομένων), αν και η βασική διεπαφή με μια βάση δεδομένων γίνεται μέσω CoreData Delegation Μια πάρα πολύ σημαντική έννοια προγραμματισμού που παίζει εξίσου κυρίαρχο ρόλο τόσο στην Objective C γενικά, όσο και στον προγραμματισμό για εφαρμογή σε περιβάλλον iphone, είναι αυτή της αρμοδιότητας (delegation). Είναι πολύ συνήθες μια κλάση να μην αντιμετωπίζει από μόνη της την εφαρμογή μιας μεθόδου, αλλά να προωθεί το μήνυμα σε κάποιον άλλον. Αυτό μπορεί να γίνει προς τα εμπρός με πέρασμα παραμέτρων σε μια άλλη μέθοδο γνωστής κλάσης, ή προς τα πίσω, έχοντας ορίσει ένα αρμόδιο για την συγκεκριμένη εργασία. Στην δεύτερη περίπτωση, δεν ξέρουμε καν τι κλάση θα είναι αυτή που θα εκτελέσει εν τέλει τον κώδικα, αρκεί να έχει δηλώσει ότι μπορεί να το κάνει. Η δήλωση του delegation γίνεται μέσω του μηχανισμού των πρωτοκόλλων της Objective C, και από σύμβαση το όνομα του πρωτοκόλλου περιέχει την λέξη delegate. Από τους πρώτους delegate που συναντάμε, είναι ο Application Delegate (AppDelegate), που είναι η πρώτη κλάση που δημιουργεί το XCode αυτόματα για εμάς, και υλοποιεί πολλές χρήσιμες μεθόδους, που απαντούν σε μηνύματα που μπορεί να πάρει η εφαρμογή μας από το σύστημα. Για παράδειγμα, αν μια εφαρμογή καταναλώνει πολύ μνήμη, αν μια εφαρμογή επρόκειται να διακοπεί προσωρινά (λόγω πχ τηλεφωνικής κλήσης) ή να επανέλθει, το σύστημα στέλνει αντίστοιχο μήνυμα στην εφαρμογή. Αρμόδιος για να απαντήσει και να πράξει σε αυτά τα μηνύματα είναι αυτή η κλάση. Ο μηχανισμός του delegation είναι πολύ συχνός όταν χρησιμοποιούμε έτοιμα αντικείμενα από μια βιβλιοθήκη και, είτε είμαστε υποχρεωμένοι να εφαρμόσουμε κάποιες από τις μεθόδους στην κλάση μας προκειμένου να λειτουργήσει το αντικείμενο, είτε τις εφαρμόζουμε για να προσδώσουμε τις δικές μας προτιμήσεις στην λειτουργικότητά του. Αν δεν προσθέσουμε εμείς τον κώδικα, υπάρχουν πολλές διαφορετικές περιπτώσεις απόκρισης. Πρώτον, μπορεί η εφαρμογή απλά να μην τρέξει, και συνήθως αυτό γίνεται όταν λείπουν προαπαιτούμενες μέθοδοι. Δεύτερον, μπορεί η εφαρμογή στην κλήση αυτή να μην κάνει τίποτα, κοινώς από μόνη της δεν έχει γραμμένη 19

25 κάποια συμπεριφορά. Τρίτον, μπορεί να αντιδράσει με κάποιον προκαθορισμένο τρόπο. Για την πρώτη περίπτωση, το αντικείμενο UITableView, έχει προαπαιτούμενο ένα UITableViewSource, που είναι ο delegate που δίνει τα δεδομένα στον πίνακα. Για την δεύτερη περίπτωση, πάλι το UITableView, έχει και το UITableViewDelegate, που προσδίδει την συμπεριφορά που πρέπει να έχει αν ο χρήστης επιλέξει κάποιο από τα στοιχεία του πίνακα. Μη υλοποίηση μιας τέτοιας μεθόδου, δεν δίνει κανένα αποτέλεσμα αν ο χρήστης επιλέξει κάτι. Τέλος για την τρίτη περίπτωση, το UIWebView, με το UIWebViewDelegate, δίνει για παράδειγμα την συμπεριφορά αν ο χρήστης πατήσει ένα σύνδεσμο σε μια ιστοσελίδα. Αν δεν υλοποιήσουμε κάτι, η προεπιλεγμένη αντίδραση είναι να ακολουθήσει το σύνδεσμο και να εμφανίσει τη νέα σελίδα Πρότυπα αρχεία Από το περιβάλλον του XCode έχουμε αρκετούς τρόπους να ξεκινήσουμε να χτίζουμε την εφαρμογή μας, δίνοντάς μας διαφορετικά πρότυπα αρχεία για να ξεκινήσουμε. 1) Window-based application 2) View-based application 3) Navigation-based application 4) TableView-based application 5) Και άλλα Όλες οι περιπτώσεις δημιουργούν το appdelegate για την εφαρμογή μας, και ένα.xib αρχείο με την γραφική εικόνα της εφαρμογής μας. Στην περίπτωση 1) το αρχείο αυτό είναι ένα κενό παράθυρο, στην 2) δημιουργείται αυτό το παράθυρο και ταυτόχρονα ένα δεύτερο.xib αρχείο για το view, και συνδέει τα δύο. Στις περιπτώσεις 3) και 4) συμπληρώνεται στο view και το αντίστοιχο στοιχείο που αναφέρει το όνομα, ήτοι ένα Navigation view ή ένα TableView. Στην περίπτωση 4), μας προτρέπει να δημιουργήσουμε και την αντίστοιχη βάση δεδομένων CoreData για τα περιεχόμενα του πίνακα. Δεν είναι υποχρεωτικό να επιλέξουμε κάτι από αυτά, υπάρχει και η επιλογή για κενή εφαρμογή, αλλά μας διευκολύνει διότι προσθέτει πάρα πολύ κώδικα που ούτως ή άλλως θα κληθούμε να γράψουμε. 20

26 2.3.4 Core Data Για την μόνιμη αποθήκευση δεδομένων στο δίσκο, ειδικά για περιπτώσεις που έχουμε μεγάλα σε όγκο δεδομένα, στρεφόμαστε στις βάσεις δεδομένων. Η χρήση βάσεων δεδομένων μας δίνει το πλεονέκτημα του να μην φορτώνουμε στην κύρια μνήμη της συσκευής όλα τα δεδομένα ταυτόχρονα, αλλά όσα χρειαζόμαστε και όταν τα χρειαζόμαστε. Το CoreData δεν πρόκειται για ένα σύστημα διαχείρισης σχεσιακών βάσεων δεδομένων (RDBMS Relational DataBase Managing System). Δίνει απλά τους μηχανισμούς για να ανακτούμε και να αποθηκεύουμε αντικείμενα. Μπορεί να χρησιμοποιεί SQLite για την αποθήκευση, αλλά δεν είναι το ίδιο μια βάση δεδομένων. Δημιουργούμε οντότητες που είναι τα βασικά στοιχεία της βάσης δεδομένων, στις οποίες αποδίδουμε γνωρίσματα, και μπορούμε να συνδέσουμε μεταξύ τους με σχέσεις. Στις σχεσιακές βάσεις δεδομένων, θα δίναμε κάποια τιμή κλειδί στην κάθε οντότητα, ώστε να μπορούμε να αναφερόμαστε σε κάθε στοιχείο της μοναδικά, και για τις υλοποιήσεις των σχέσεων, θα περνούσαμε αυτή την τιμή στην άλλη οντότητα ως «ξένο κλειδί» (foreign key). Εδώ το CoreData διαφέρει μόνο στο ότι αντί να δώσουμε εμείς κάποια τιμή σαν κλειδί, ορίζει από μόνο του κάποιους δείκτες, μοναδικούς σε κάθε αντικείμενο. Στο κομμάτι της υλοποίησης των σχέσεων, το CoreData δίνει αυτούς τους δείκτες στα αντικείμενα, είτε μόνους τους (αν οι σχέση είναι 1 προς 1) είτε ως set (αν είναι 1 προς Ν ή Ν προς Ν). Εδώ το πρόβλημα που παρουσιάστηκε ήταν ότι δεν υπήρχε η δυνατότητα να δώσουμε γνωρίσματα στην σχέση. Στην SQL, για μια σχέση Ν προς Ν, θα δημιουργούσαμε ένα πίνακα με δύο ξένα κλειδιά, ένα για κάθε οντότητα που συμμετέχει στη σχέση, και θα μπορούσαμε να προσθέσουμε περιταίρω πεδία ως επιπρόσθετη πληροφορία ή γνωρίσματα για την σχέση. Όπως θα δούμε για να υπερπηδήσουμε αυτό το πρόβλημα, δημιουργήσαμε μια νέα οντότητα για να αντιπροσωπεύσει την σχέση και να της δώσουμε γνωρίσματα, και ορίσαμε δύο 1 προς Ν σχέσεις προς την κάθε οντότητα αντίστοιχα. Ωστόσο το μεγάλο πλεονέκτημα του CoreData, είναι ότι όταν ανακτάς ένα αντικείμενο, δεν φέρνει μαζί και τα αντικείμενα με τα οποία έχει σχέση. Επιστρέφει την σχέση ως σφάλμα, το οποίο ενημερώνει, μόνο αν ζητηθεί από το πρόγραμμα. Μειώνει έτσι σημαντικά το κόστος σε μνήμη. Το XCode υποστηρίζει γραφικό τρόπο σχεδίασης της βάσης δεδομένων, αφήνοντας τον χειρισμό της να είναι ο μόνος (σχεδόν) κώδικας που θα πρέπει να γράψουμε. 21

27 Επίσης αφού ολοκληρωθεί η σχεδίαση γραφικά, μπορούμε να δημιουργήσουμε αυτόματα κλάσεις για τις οντότητες που δημιουργήσαμε. Στην υλοποίηση του ελέγχου μιας βάσης σε CoreData πρέπει να έχουμε υπόψιν τρεις βασικές έννοιες: Managed Object Model Είναι ουσιαστικά το σχήμα της βάσης δεδομένων, το μοντέλο της εφαρμογής. Περιέχει όλη την πληροφορία για τις οντότητες, αυτό που δημιουργούμε στο γραφικό κομμάτι που αναφέραμε πιο πάνω. Managed Object Context Είναι το κομμάτι που χρησιμοποιούμε πιο πολύ. Είναι σαν ένα ενδιάμεσο στάδιο, στο οποίο μπορούμε να δημιουργούμε, να διαγράφουμε, να ενημερώνουμε αντικείμενα, χωρίς να δρούμε απ ευθείας πάνω στη βάση. Αν θέλουμε να αποθηκεύσουμε τις αλλαγές μας, πρέπει να περάσουμε από τον store coordinator. Persistent Store Coordinator Είναι το κομμάτι διασύνδεσης με τη βάση δεδομένων. Κάθε φορά που το Managed Object Context θέλει να επικυρώσει μια πράξη, συναλλαγή, περνάει από το Persistent Store Coordinator. Για να επιβάλλουμε ερωτήματα πάνω στη βάση, υπάρχει η κλάση NSFetchRequest, μέσω της οποίας με κατάλληλους περιορισμούς μέσω της κλάσης NSPredicate, παίρνουμε τα δεδομένα που θέλουμε. Κάθε αντικείμενο που παίρνουμε ως απάντηση από ένα NSFetchRequest, είναι αντικείμενο της κλάσης NSManagedObject, ή κάποιας υποκλάσης του. Όταν εξάγουμε αυτόματα τις κλάσεις για τις οντότητες που έχουμε δημιουργήσει, αυτόματα είναι υποκλάσεις της κλάσης αυτής. 22

28 2.4 Υπόλοιπες Γνώσεις Πέρα από το κομμάτι του προγραμματισμού στο iphone, το περιβάλλον του XCode και την Objective C, χρειάστηκαν και χρησιμοποιήθηκαν και άλλες γνώσεις για την παρούσα διπλωματική εργασία HTML, XML και Javascript Η HTML (HyperText Markup Language) είναι η γλώσσα μορφοποίησης κειμένων που έχει κυριαρχήσει στο διαδίκτυο και δίνει στις ιστοσελίδες την εμφάνιση που έχουν. Η HTML προσφέρει γνωστά στοιχεία μορφοποίησης κειμένου, τα οποία σε συνδυασμό με ένα συγκεκριμένο view του framework του UIKit, το UIWebView, που είναι ουσιαστικά λόγος ύπαρξής του να εμφανίζει κατάλληλα τέτοιο περιεχόμενο, γράφτηκε το κομμάτι της εφαρμογής που δίνει τις οδηγίες χρήσης της. Προκειμένου να υπάρχει και λειτουργικότητα πάνω στο κείμενο, όπως το να είναι δυνατόν να συρθεί σε συγκεκριμένα σημεία, ή να πηγαίνει απ ευθείας στο τέλος ή την αρχή του, χρησιμοποιήθηκε και σχετικά λίγη και απλή Javascript. Τέλος, η XML (Extensible Markup Language), που είναι επίσης γλώσσα μορφοποίησης, είναι η μορφή με την οποία πήραμε τα δεδομένα για τις στάσεις των λεωφορείων από το διαδίκτυο. Σε σύνταξη μοιάζει αρκετά με την HTML, αν και είναι αρκετά και πιο αυστηρή, και, αν και υποστηρίζει όπως η HTML τις ετικέτες (tags), δεν έχει κανένα περιορισμό (σχεδόν) στις ονομασίες τους Λοιπές γλώσσες προγραμματισμού Προκειμένου να αποκτήσουμε όλη την απαραίτητη πληροφορία, χρησιμοποιήθηκαν διάφορα μέσα, όπως για παράδειγμα ένα απλό VBScript (Visual Basic Script) για να ανοίξουμε όλες τις διαφορετικές σελίδες που είχαν τα αρχεία που θέλαμε, και καθώς δεν ήταν δυνατόν να δουλεύω σε Mac υπολογιστή από το σπίτι, χρησιμοποίησα την Java ως γλώσσα προγραμματισμού. Την Java χρησιμοποίησα για δύο διαφορετικές διαδικασίες: - Πρώτον, να μαζέψω, να ξεχωρίσω και να ταξινομήσω όλη την πληροφορία που είχαν τα xml αρχεία για τις στάσεις των λεωφορείων και τις γραμμές των λεωφορείων, πετώντας όση ήταν περιττή ή πολλαπλή. - Δεύτερον, για το κομμάτι των δρομολογίων των λεωφορείων, που δεν ήταν διαθέσιμο σε ξεχωριστό αρχείο, παρά παρουσιαζόταν απ ευθείας μέσα στην HTML 23

29 της σελίδας, με τη βοήθεια regular expressions, απομόνωσα το κομμάτι που αναφερόταν στην κάθε γραμμή Αλγόριθμος Levenshtein Στο κομμάτι της εύρεσης συγκεκριμένης στάσης μέσω του ονόματός της, δεν είναι βολικό για τον χρήστη να εισάγει ολόκληρο το όνομα της στάσης, και επίσης καλό είναι να προβλέπεται η πιθανότητα της λάθους πληκτρολόγησης. Στα iphone υπάρχει βεβαίως η δυνατότητα της αυτόματης διόρθωσης, αλλά υπόκειται στους εξής περιορισμούς: - Είναι περιορισμένη, ειδικά σε ότι αφορά το ελληνικό λεξιλόγιο. - Τα ονόματα στάσεων λεωφορείων σπανίως βρίσκονται σε κάποιο λεξικό ως λέξεις, ώστε να μπορέσει η αυτόματη διόρθωση να βρει την λέξη για μας. Συνεπώς, για διευκόλυνση προς τον χρήστη, πρόσθεσα ένα κομμάτι κώδικα να συγκρίνει το τι έχει γράψει με όλα τα ονόματα των στάσεων στην βάση δεδομένων. Εδώ έρχεται ο αλγόριθμος του Levenshtein. Ο αλγόριθμος Levenshtein είναι ένας αλγόριθμος υπολογισμού αποστάσεων λέξεων, όπου ορίζεται ως απόσταση η εξής έννοια. Θεωρούμε τα δυνατά λάθη ή τις δυνατές διαφορές ανάμεσα σε δύο λέξεις τις εξής: - Προσθήκη ενός γράμματος σε μία θέση - Αφαίρεση ενός γράμματος από μια θέση - Αντικατάσταση ενός γράμματος με ένα άλλο σε μια θέση Ως απόσταση Levenshtein ορίζεται ο ελάχιστος αριθμός τέτοιων αλλαγών που μπορούμε να κάνουμε σε μία λέξη ώστε να γίνει ίδια με μια άλλη. Ο αλγόριθμος Levenshtein είναι η διαδικασία με την οποία υπολογίζεται αυτή η απόσταση. Είναι αλγόριθμος δυναμικού προγραμματισμού και περιλαμβάνει την συμπλήρωση ενός πίνακα Ν x M με θετικές τιμές. Εδώ Ν είναι το μήκος της πρώτης λέξης και Μ το μήκος της δεύτερης. Ακολουθώντας την διαδικασία που περιγράφεται παρακάτω, στο κελί με συντεταγμένες (Ν,Μ) θα εμφανιστεί η τιμή της απόστασης. Ως απόσταση, ισχύει ότι η απόσταση της πρώτης λέξης από την δεύτερη είναι ίση με την απόσταση της δεύτερης λέξης από την πρώτη. Επίσης, ισχύει και η τριγωνική ανισότητα, δηλαδή το άθροισμα της απόστασης μιας λέξης Α από μια Β συν την 24

30 απόσταση της Β από μια Γ είναι πάντα μικρότερο ή ίσο από την απόσταση της Α από την Γ. Ο αλγόριθμος έχει τα εξής βήματα: 1) Συμπληρώνουμε μπροστά από τις λέξεις το σύμβολο # που δηλώνει την αρχή της λέξης. 2) Οι πρώτη γραμμή και η πρώτη στήλη συμπληρώνονται με αύξοντες αριθμούς από το μηδέν (αφού ουσιαστικά η απόσταση μια λέξης που δεν έχει ξεκινήσει από μια άλλη είναι ο αριθμός των γραμμάτων της άλλης, αφού απαιτούνται ισάριθμες προσθήκες). 3) Για κάθε άλλο κελί, η τιμή του συμπληρώνεται ως εξής. Θεωρούμε μια οριζόντια μετακίνηση μέσα στον πίνακα μια προσθήκη, μια κάθετη μετακίνηση ως μια αφαίρεση και μια διαγώνια κίνηση ως μια αντικατάσταση. Ορίζουμε ως κόστος προσθήκης και αφαίρεσης το 1, και ως κόστος αντικατάστασης το 1 αν τα γράμματα στις λέξεις στις θέσεις n, m είναι διαφορετικά μεταξύ τους ή το μηδέν αν είναι διαφορετικά, όπου n και m οι συντεταγμένες του κελιού που εξετάζουμε. Από την διαδρομή που ήρθαμε, προσθέτουμε το αντίστοιχο κόστος στην αντίστοιχη τιμή (δηλαδή αν κάνουμε προσθήκη, προσθέτουμε ένα στο περιεχόμενο του αριστερού κελιού). 4) Από τις τρεις τιμές που προκύπτουν, κρατάμε την μικρότερη. 5) Επαναλαμβάνουμε την διαδικασία μέχρι να συμπληρωθεί ολόκληρος ο πίνακας. Στο τελευταίο κελί που συμπληρώνουμε θα είναι η τιμή της απόστασης των δύο λέξεων. Με τον αλγόριθμο Levenshtein, μπορούμε να βρούμε και ποιες ακριβώς είναι οι αλλαγές που πρέπει να κάνουμε για να φτάσουμε από μια λέξη σε μια άλλη, αλλά στην παρούσα εφαρμογή του, μας ενδιαφέρει απλά η απόσταση. Η υλοποίηση του αλγορίθμου έγινε αρχικά από εμένα, αλλά βρέθηκε στο διαδίκτυο υλοποίηση από τρίτον, που επειδή χρησιμοποιούσε C και όχι Objective C στον κώδικα, έδινε πολύ πιο γρήγορη και αποδοτική συμπεριφορά. Στο παράρτημα της βιβλιογραφίας δίνεται ο σύνδεσμος που είχε τον κώδικα. 25

31 ΚΕΦΑΛΑΙΟ 3 Η ΕΦΑΡΜΟΓΗ 3.1 Αρχεία στο XCode Η εφαρμογή ξεκίνησε ως ένα view-based application, με το ένα βασικό view και τον controller του που προσφέρει το XCode από μόνο του και την κλάση για το ApplicationDelegate. Σε αυτά προστέθηκαν τελικά: - 12 επιπλέον views με τα.xib αρχεία τους - 22.m αρχεία με κώδικα για διαχείρηση των views και κλάσεις για αφαιρετικότητα, ώστε να μην μαζεύεται ολόκληρος ο κώδικας σε μια κλάση h αρχεία, από τα οποία προφανώς τα 8 δεν αντιστοιχούν σε κάποιο.m αρχείο και πρόκειται για αρχεία πρωτοκόλλων. - 4 ακόμα.m και.h αρχεία που δημιούργησε το CoreData για τα Managed Objects. - 7 αρχεία κειμένου, με τα δεδομένα της εφαρμογής, των οποίων τη μορφή αναλύουμε παρακάτω. Μορφή Αρχείων Δεδομένων Ακολούθως, παρουσιάζεται η μορφή με την οποία έχουν αποθηκευτεί τα δεδομένα στα αρχεία κειμένου. lines_stopsutf8.txt : Οι στάσεις και τα λεωφορεία που εξυπηρετούν κάθε μία. κωδικός στάσης; γραμμή λεωφορείου 1; γραμμή λεωφορείου 2;... Τα ελληνικά ερωτηματικά ξεχωρίζουν την στάση από τα λεωφορεία που την εξυπηρετούν. Κάθε νέα γραμμή αναφέρεται σε νέα στάση. Στο τέλος της κάθε γραμμής υπάρχει ερωτηματικό. linesutf8.txt : Τα διαθέσιμα λεωφορεία. γραμμή λεωφορείου; όνομα γραμμής Όμοια ο διαχωρισμός γίνεται μέσω ελληνικών ερωτηματικών. 26

32 Routes_UTF8.txt : Η διαδρομή για κάθε λεωφορείο, με τη σωστή σειρά στάσεων. αριθμός γραμμής όνομα γραμμής 0 κωδικός στάσης 1! 1 κωδικός στάσης 2! 3 κωδικός στάσης 2!... Κάθε εγγραφή αποτελείται από δύο γραμμές κειμένου. Η πρώτη καθορίζει την γραμμή λεωφορείου για την οποία μιλάμε και η δεύτερη δίνει την σειρά τον στάσεων απαριθμώντας από το μηδέν και αυξάνοντας. Οι στάσεις διαχωρίζονται από την ακολουθία «!», η οποία πρέπει να είναι και στο τέλος της κάθε γραμμής. StartingPoints.txt : Οι στάσεις αφετηρίας για κάθε γραμμή. γραμμή λεωφορείου; κωδικός στάσης stops_linesutf8.txt : Τα λεωφορεία και οι στάσεις που εξυπηρετεί το κάθε ένα. γραμμή λεωφορείου; κωδικός στάσης; κωδικός στάσης;... StopsUTF8.txt : Οι υπάρχουσες στάσεις. όνομα στάσης; κωδικός στάσης; γεωγραφικό μήκος; γεωγραφικό πλάτος Timetables_UTF8.txt : Τα δρομολόγια των λεωφορείων. αριθμός γραμμής όνομα γραμμής Καθημερινές Από Εώς Συχνότητα ώρα 1 ώρα 2 συχνότητα σε λεπτά Κάθε εγγραφή αποτελείται από τουλάχιστον τρεις γραμμές που καθορίζουν το λεωφορείο για το οποίο μιλάμε, τις ημέρες για τις οποίες δίνουμε την πληροφορία του προγράμματος, μια γραμμή σαν σχόλιο με τις λέξεις που φαίνονται παραπάνω (από, εώς, συχνότητα) και ακολουθούν οι γραμμές δεδομένων, που μπορεί να είναι μία ή περισσότερες. Κάθε εγγραφή διαχωρίζεται από την επόμενη με μια κενή νέα γραμμή. Το αρχείο επίσης τερματίζει με κενή νέα γραμμή. 27

33 3.2 Τα views της εφαρμογής Τα αρχικά Views Τα αρχικά views της εφαρμογής: - MenuViewController - TutorialViewController - UniMapViewController - LoadingViewController - PreferencesViewController Αναλύονται συνοπτικά εδώ. LoadingViewController Στην πρώτη εκκίνηση του προγράμματος, αποθηκεύονται όλα τα δεδομένα για τις στάσεις και τα λεωφορεία στη βάση δεδομένων από τα αρχεία κειμένου που συμπεριλαμβάνονται στον κωδικα. Δεν υπάρχει ξεχωριστό.sql αρχείο που να περιέχει τα δεδομένα, κι αυτό γιατί το CoreData αναλαμβάνει πλήρως την αποθήκευση στην δευτερεύουσα μνήμη της συσκευής. Κατά την μεταφορά αυτή, είναι αρκετά μεγάλη η διαδικασία, γιατί εκτός από την δημιουργία Managed Object με τα δεδομένα από τα αρχεία κειμένου, γίνεται και η διασύνδεση τους για την υλοποίηση των σχέσεων στο CoreData. Κατά τη διάρκεια αυτής της μεταφοράς, η εφαρμογή δεν πρέπει να ξεκινήσει, γιατί είναι πιθανόν να διακόψει την διαδικασία με κάποιο ερώτημα και ως συνέπεια να δυσλειτουργεί. Από την άλλη μεριά, οι κανόνες της αλληλεπίδραση χρήστη μηχανής δεν επιτρέπουν ο χρήστης να μην έχει επίγνωση του τι συμβαίνει και να βλέπει μια μαύρη οθόνη μέχρι να τελειώσει η διαδικασία. Για αυτό τον λόγο, προσθέτουμε το LoadingViewController, που εμφανίζει ένα μήνυμα ότι η εφαρμογή έχει ξεκινήσει και αρχικοποιείται, καθώς επίσης ένα δείκτη δράσης (activity indicator). Με το που φορτωθεί αυτό το view στην οθόνη, δίνουμε εντολή στον δείκτη να αρχίσει να περιστρέφεται, έτσι ώστε να έχει ο χρήστης την αίσθηση ότι η εφαρμογή ανταποκρίνεται και δεν έχει κολλήσει. Σε κώδικα, θέτουμε τον ελεγκτή του βασικού view να είναι αυτός του LoadingViewController, εμφανίζοντας έτσι το view που ελέγχει. Για να μην επιβαρύνουμε το κύριο νήμα με την εμφάνιση του view και ταυτόχρονα με την αρχικοποίηση της βάσης δεδομένων, δημιουργούμε ένα νέο νήμα, το οποίο θα ολοκληρώσει αυτή την μεταφορά. Όταν ολοκληρωθεί, επιστρέφει και καλεί στην κλάση που το δημιούργησε ένα επιλογέα (selector). Με αυτό τον τρόπο, αν και δεν 28

34 ξέρουμε πότε ακριβώς θα τελειώσει η μεταφορά, μας ειδοποιεί το δευτερεύον νήμα ότι πρέπει να εμφανίσουμε το κύριο view. MenuViewController Το κύριο μενού της εφαρμογής, από το οποίο μπορούμε να πλοηγηθούμε στα διαφορετικά σημείο της εφαρμογής. Για κάθε επόμενο view, αυτός ο ελεγκτής είναι αυτός που θα δημιουργήσει και θα αρχικοποιήσει τα στιγμιότυπα των επόμενων ελεγκτών. Επειδή θέλουμε και τη δυνατότητα να επιστρέφουμε στο μενού από το κάθε view, έχω ορίσει το πρωτόκολλο ReturnToMenu.h και έχω τον ελεγκτή να το εφαρμόζει. Το πρωτόκολλο αυτό ορίζει μια μόνο μέθοδο, την (void) returntomenu, η οποία αλλάζει τον rootviewcontroller και τον θέτει πάλι στο MenuViewController. Για να επικοινωνούν τα υπόλοιπα views με το μενού και να το ειδοποιούν πότε ο χρήστης έχει ζητήσει να επιστρέψει εκεί, έχω ορίσει το menuviewcontroller ως delegate για εκείνα, και αντίστοιχα για τα views έχω ορίσει ότι έχουν ένα αντικείμενο delegate που εφαρμόζει το ReturnToMenu πρωτόκολλο. Εναλλακτική λύση θα ήταν η υλοποίηση μέσω Navigation Bar Views, αλλά έχει το μειονέκτημα ότι μειώνει την ελεύθερη επιφάνεια που απομένει στον χρήστη, και ειδικά στο κομμάτι του χάρτη ήθελα να είναι μέγιστη, για να έχει ο χρήστης την βέλτιστη εικόνα. UniMapViewController Ο ιστότοπος του Πανεπιστημίου Πατρών έχει διαθέσιμο και χάρτη για το Πανεπιστήμιο, καθώς και σημειωμένα διάφορα άλλα στοιχεία, όπως τις θέσεις των τμημάτων, των κοινόχρηστων χώρων του Πανεπιστημίου και τα λοιπά. Αυτό το κάνει έχοντας ενσωματωμένο ένα χάρτη στη σελίδα του, τον οποίο παρέχουν τα google-maps. Σαν παράμετρος περνιέται ένα kml αρχείο (παρεμφερές της xml), με τις συντεταγμένες που θέλουμε να σημειωθούν στο χάρτη και τα σχόλια. Δυστυχώς δεν μπόρεσα να αποκτήσω με κάποιο τρόπο το αρχείο kml με αυτή την πληροφορία, οπότε για τον χάρτη του Πανεπιστημίου χρησιμοποίησα την ίδια τη σελίδα, απεικονίζοντάς την μέσω ενός UIWebView. Το UiWebView έχει τη δυνατότητα να απεικονίζει html αρχεία, είτε αυτά είναι τοπικά είτε στο διαδίκτυο. Παράλληλα μπορεί να επεξεργάζεται και να εκτελεί και την javascript αυτών των κειμένων, επιτρέποντας την αλληλεπίδραση με τον χάρτη που είναι ενσωματωμένος. 29

35 Τέλος επειδή το συγκεκριμένο WebView ήθελα να χρησιμοποιείται αποκλειστικά για την απεικόνιση του χάρτη και όχι σαν κανονικός φυλλομετρητής, όρισα το UniMapViewController να είναι και UIWebViewDelegate, ώστε να διαχειρίζεται τις αιτήσεις που στέλνει το UIWebView. Έτσι υλοποίησα, σε κάθε αίτηση μετακίνησης σε διαφορετική σελίδα (διαφορετικό url), να επιστρέφει NO, δηλαδή να μην την επιτρέπει. PreferencesViewController Είναι απαραίτητο ο χρήστης να μπορεί να κάνει κάποιες αλλαγές στην εφαρμογή, και αυτές να είναι σε ένα γνωστό σημείο μαζεμένες. Για αυτό τον λόγο υπάρχει αυτό το view, όπου θα συγκεντρώνονται όλες οι δυνατές επιλογές. Στην παρούσα έκδοση της εφαρμογής, έχω συμπεριλάβει μόνο δύο στοιχεία: - Την εμφάνιση του χάρτη της Πάτρας, ανάμεσα σε Υβριδικό, Οδικό και Γεωγραφικό. - Τον αριθμό των αλλαγών μεταξύ γραμμών λεωφορείων που επιτρέπονται στο κομμάτι της δρομολόγησης. Μπορεί να μην είναι πολύ σημαντικές παράμετροι, αλλά υπάρχει έτσι τουλάχιστον η βάση για μελλοντικές εκδόσεις της εφαρμογής. Είναι επίσης σημαντικό να αποθηκεύονται οι αλλαγές, και να μην μένουν ενεργές μόνο για το διάστημα που τρέχει η εφαρμογή. Πρέπει λοιπόν να στραφούμε σε αποθήκευση στην δευτερεύουσα μνήμη («σκληρός δίσκος» - flash μνήμη για τα κινητά). Αυτό μπορεί να γίνει: - Μέσω βάσεων δεδομένων, που προσθέτει μεγάλο υπολογιστικό κόστος χωρίς να είναι απαραίτητο, καθώς η πληροφορία που θέλουμε να αποθηκεύσουμε είναι ελάχιστη. - Μέσω αρχείων κειμένου, καθώς για κάθε εφαρμογή το iphone δίνει και ένα χώρο, τον φάκελο εγγράφων (Documents Directory). Ωστόσο και πάλι, είναι ασύμφορο να αποθηκεύουμε ολόκληρο αρχείο για δύο τιμές που θα έχουν το μέγιστο τιμές ακεραίων (2 byte). - Μέσω της μόνιμης αποθήκευσης (persistent store) του NSUserDefaults, που μας επιτρέπει να αποθηκεύσουμε μια τιμή, και μια λέξη κλειδί με την οποία την συνδέουμε, και μέσω της οποίας αναφερόμαστε σε αυτήν. Για το παρών πρόβλημα επέλεξα την τρίτη επιλογή, καθώς είναι η πιο γρήγορη σε πρόσβαση, πιο εύκολη στην υλοποίηση και πιάνει σχετικά λίγο χώρο. Συνεπώς ορίζω δύο λέξεις κλειδιά, MapType και maxchanges, μία για την κάθε παράμετρο που καθορίζω. 30

36 Η αποθήκευση γίνεται με τον εξής κώδικα: [[NSUserDefaults standarduserdefaults] setvalue:[nsnumber numberwithint:2] Και η ανάκληση με τον εξής: NSUInteger t = [[[NSUserDefaults standarduserdefaults] valueforkey:@"maptype"] unsignedintvalue]; Να αναφέρω ότι οι σταθερές για το MapView, έχουν ονόματα του τύπου MKMapTypeHybrid, και αντιστοιχίζονται σε απλούς μη προσημασμένους ακέραιους αριθμούς. TutorialViewController Όταν ένας χρήστης πιάνει μια εφαρμογή για πρώτη φορά, πρέπει να έχει απ ευθείας μια αίσθηση του τι πρέπει να κάνει και πως λειτουργεί. Αν αυτό βέβαια δεν γίνει ξεκάθαρο, ή ο χρήστης θέλει κάποια επιπλέον καθοδήγηση, υπάρχει το κομμάτι των οδηγιών χρήσης που μπορεί να διαφωτίσει κάποιο πρόβλημα. Επειδή το iphone έχει περιορισμένο χώρο στην οθόνη του (320 * 460 pixels), δεν είναι δυνατόν οι οδηγίες να είναι ένα κατεβατό κείμενο, χωρίς ενδείξεις ή χρώματα. Αυτό θα το καθιστούσε τουλάχιστον δυσανάγνωστο. Επίσης θα ήταν πολύ βολικό να μπορεί ο χρήστης να πλοηγηθεί σε διαφορετικά σημεία των οδηγιών, και να μην χρειάζεται να κυλάει την οθόνη συνεχώς. Αυτές οι δύο απαιτήσεις, με οδήγησαν στο να επιλέξω να γράψω τις οδηγίες σε ένα html αρχείο, το οποίο ως γνωστόν μπορεί να έχει την μορφοποίηση που επιθυμεί ο δημιουργός, και την δυνατότητα να έχει συνδέσμους μέσα στο ίδιο το κείμενο. Από την μεριά του iphone, το view που μπορεί να επεξεργαστεί και να εμφανίσει κείμενο μορφοποιημένο μέσω html, είναι το UIWebView. Ένα επιπλέον πλεονέκτημα είναι ότι μπορεί να εκτελέσει και κώδικα javascript που δεν είναι ενσωματωμένος στην html, αλλά δίνεται σαν string μέσω της objective c. Συνεπώς, για να πλοηγηθούμε μέσα στο κείμενο των οδηγιών, υπάρχουν δύο τρόποι: - Έτοιμοι σύνδεσμοι που δημιουργήθηκαν μέσα στην html. - Τρία επιπλέον κουμπιά που πρόσθεσα στο view, πέραν του κουμπιού επιστροφής και του UIWebView. 31

37 Αυτά τα τρία κουμπιά είναι, ένα που οδηγεί στην κορυφή του εγγράφου, ένα που προχωράει το έγγραφο στην επόμενη «σελίδα» και ένα που το πηγαίνει στην προηγούμενη. Ως έννοια της σελίδας έχω εδώ κείμενο που αντιστοιχεί σε ύψος 416 pixel, που είναι το ύψος που απομένει, αν από τα 460 αφαιρέσουμε τα 44 της μπάρας που βρίσκεται στο κάτω μέρος του view. Και τα τρία αυτά κουμπιά, βάζουν το WebView να εκτελέσει κώδικα javascript για να κυλήσει η σελίδα, ως εξής: NSString *javascript = window.pageyoffset; window.scroll(0,y-416);"; [instructions stringbyevaluatingjavascriptfromstring:javascript]; Όπου όπως φαίνεται, για πολλές εντολές μας αρκεί το σύμβολο ; που έχει η Javascript για να τερματίζει τις εντολές της, και τις γράφουμε όλες μέσα στο ίδιο NSString Τα κύρια Views Τα κύρια views της εφαρμογής αναλύονται παρκάτω: - PatrasRoutesViewController - FilterAndSearch o BusLinePicker o DestinationPicker o RouteSelector - DetailedAnnotViewController - DetailedBusStopViewController - TimetableViewController Ξεκινώντας από το PatrasRoutesViewController, που είναι και το βασικό ολόκληρης της εφαρμογής. Το view αυτό, με την φόρτωση του κεντράρει τον χάρτη στο κέντρο της Πάτρας. Το βασικό στοιχείο είναι το MKMapView, που απεικονίζει τον χάρτη. Ο χάρτης αποκτά τα δεδομένα του μέσω του διαδικτύου, από τα google maps, αλλά αν φορτώσει μια φορά τα δεδομένα, τα κρατάει στην μνήμη του, εκτός αν παρουσιαστεί πρόβλημα μνήμης (διαρροές, κλπ) οπότε είναι από τα πρώτα πράγματα που αδειάζουν. 32

38 Η αλληλεπίδραση με τον χρήστη είναι συμπεριφορά που είναι έτοιμη και ενσωματωμένη στο MKMapView, και επιτρέπει στον χρήστη να μετακινηθεί στο χάρτη, να πλησιάσει ή να απομακρυνθεί σε υψόμετρο και άλλα. Στο κάτω μέρος του view, έχω προσθέσει μια NavigationBar όπως αυτές που έχουμε ήδη δει σε προηγούμενα views, με το ίδιο κουμπί Back που μας επιστρέφει στο μενού. Εντοπισμός Χρήστη Το αμέσως επόμενο κουμπί, με την ένδειξη «Εδώ», ενεργοποιεί τον μηχανισμό εντοπισμού του χρήστη στο χάρτη. Με το πρώτο πάτημα, αλλάζει το χρώμα του κουμπιού, αλλάζοντας την ιδιότητα style σε UIBarButtonItemStyleDone. Αυτή είναι μια από τις λύσεις για να επηρεάσεις κουμπιά σε ένα navigation bar, η οποία ήταν η μόνη που γινόταν μέχρι το ios 3.2. Μετά, με το ios 5.0, υποστηρίζονται και άλλες ιδιότητες που μπορούν να αλλάξουν πχ το χρώμα. Αλλά για την εφαρμογή είναι υπεραρκετό. Ο λόγος που μας ενδιαφέρει να δείχνουμε αν είναι ενεργός ο εντοπισμός χρήστη, είναι ότι πρώτον απαιτεί σύνδεση στο διαδίκτυο για να προσδιοριστεί, και δεύτερον είναι απαιτητικό από πλευρά πόρων και θεωρητικά προκαλεί μεγαλύτερη κατανάλωση της μπαταρίας. Με το που ενεργοποιήσουμε τον εντοπισμό χρήστη, δεν παίρνουμε απ ευθείας απάντηση, παρά αν προσπαθήσουμε να διαβάσουμε τις συντεταγμένες, θα πάρουμε nil αντικείμενο, το οποίο θα μεταφραστεί σε μηδενικά. Συνεπώς αν πάρουμε απ ευθείας την τιμή, θα κεντράρουμε τον χάρτη στο κέντρο του κόσμου. Για να το αποφύγουμε αυτό, βάζουμε έναν έλεγχο που όσο δεχόμαστε nil αντικείμενο για συντεταγμένες, δημιουργούμε ένα νέο νήμα, το βάζουμε να κάνει μια μικρή καθυστέρηση 0.4 δευτερολέπτων, και μετά να ξαναπροσπαθήσει. Για να αποφύγουμε ένα ατέρμων βρόχο, σε περίπτωση που δεν έχουμε απάντηση μέσα σε συνολικά 2 δευτερόλεπτα (δηλαδή με το που δημιουργηθούν 5 νήματα), τερματίζουμε την διαδικασία. Αν έχουμε απάντηση, παίρνουμε τις συντεταγμένες και φέρνουμε το κέντρο του χάρτη εκεί. Το MKMapView προσθέτει από μόνο του μια μπλε πινέζα και ένα κύκλο που υποδεικνύει την ακρίβεια της μέτρησης του εντοπισμού. Σε περίπτωση που αποτύχει, αν απενεργοποιήσουμε το κουμπί και το ξαναπατήσουμε, η διαδικασία επαναλαμβάνεται από την αρχή. 33

39 Κύλιση Χάρτη Το επόμενο κουμπί είναι αυτό που μας κεντράρει στον χώρο του Πανεπιστημίου. Γενικότερα η υλοποίηση της μετακίνησης του χάρτη σε ένα σημείο είναι πολύ απλή, αφού το ίδιο το MKMapView υποστηρίζει κατάλληλη μέθοδο, την [self.mymapview setregion:region animated:yes]; όπου περνάμε σαν παράμετρο μια δομή (struct, με την γνωστή έννοια από την C) που έχει δύο αριθμούς κινητής υποδιαστολής διπλής ακρίβειας που αντιπροσωπεύουν τις συντεταγμένες, άλλους δύο που αντιπροσωπεύουν το ύψος πάνω από το χάρτη, και μια δυαδική τιμή που υποδεικνύει αν θα υπάρξει κύλιση του χάρτη στο σημείο, ή αν θα ξανασχεδιαστεί απλά στο νέο σημείο. Η δομή αυτή είναι η MKCoordinateRegion, και οι «συντεταγμένες» υψομέτρου, που αντιστοιχούν στη δομή MKCoordinateSpan, δείχνουν ουσιαστικά το εμβαδό το χάρτη που θα δείξουμε (από βορρά προς νότο και από ανατολή προς δύση, σε μοίρες). Επισημειώσεις Το επόμενο κουμπί είναι μάλλον το δεύτερο σημαντικότερο της εφαρμογής, αφού είναι αυτό που εμφανίζει τις στάσεις των λεωφορείων, τις σημειώσεις που έχουμε δώσει ως χρήστες, ή μας καθαρίζει τον χάρτη. Στο πρώτο πάτημα του κουμπιού με σύμβολο την πινέζα, εμφανίζονται οι στάσεις των λεωφορείων. Το ποιες συγκεκριμένα θα εμφανιστούν, εξαρτάται από το αν έχουμε εφαρμόσει κάποιο φίλτρο πάνω τους, όπως θα δούμε παρακάτω. Το δεύτερο πάτημα αποσύρει τις στάσεις, και εμφανίζει τις σημειώσεις που έχει αποθηκεύσει ο χρήστης. Με το τρίτο επιστρέφουμε στην αρχική κατάσταση, όπου ο χάρτης είναι καθαρός και δεν εμφανίζεται τίποτα. MKMapViewDelegate Για να δούμε πώς προσθέτουμε σημειώσεις δικές μας, ή πως τις εμφανίζουμε πάνω στο MKMapView, αρκεί να κοιτάξουμε τα αρχεία του ελεγκτή που είναι υπεύθυνος για αυτό το view. Όπως βλέπουμε και στο header file, ο ελεγκτής του υποστηρίζει αρκετά πρωτόκολλα: <MKMapViewDelegate,DetailedBusStopDelegate,DetailedAnnotDelega te,filterandsearchdelegate> 34

40 Από τα οποία μόνο το MKMapViewDelegate είναι πρωτόκολλο έτοιμο από το framework του MapKit που έχω συμπεριλάβει. Έχοντας δηλώσει ότι το εφαρμόζουμε, μπορούμε να δώσουμε εμείς τον κώδικα για τις επισημειώσεις, υλοποιώντας την μέθοδο: - (MKAnnotationView *)mapview:(mkmapview *)themapview viewforannotation:(id <MKAnnotation>)annotation Η οποία καλείται κάθε φορά που ο χάρτης καλείται να εμφανίσει μια επισημείωση (annotation), και επιστρέφει το αντίστοιχο view. Βλέπουμε ότι παίρνει σαν παράμετρο το ίδιο το mapview, καθώς και ένα annotation, το οποίο δεν μας ενδιαφέρει ποιας κλάσης αντικείμενο είναι (id), αρκεί αυτή η κλάση να εφαρμόζει το πρωτόκολλο MKAnnotation. Αν δούμε το documentation για αυτό το πρωτόκολλο ( otation_protocol/reference/reference.html), θα δούμε ότι απαιτεί το αντικείμενο να έχει μια (nonatomic, readonly) CLLocationCoordinate2D coordinate που δίνει τις συντεταγμένες του σημείου, και προαιρετικά ένα NSString *title και ένα NSString *subtitle, αν και τουλάχιστον τον τίτλο θέλουμε να τον έχουμε γιατί είναι αυτό που θα εμφανίζεται όταν επιλέγουμε ένα annotation. MKAnnotationView Αγνοώντας προσωρινά το ποια είναι τα annotation για τα οποία γίνεται η κλήση της μεθόδου, ας αναλύσουμε το τι πρέπει να επιστρέψει. Σύμφωνα με την δήλωση της μεθόδου, πρέπει να επιστρέψει ένα MKAnnotationView, το οποίο προφανώς είναι υποκλάση του UIView. Εφαρμόζοντας εμείς την μέθοδο μπορούμε, σύμφωνα με το πρωτόκολλο, να επιστρέψουμε nil αν θέλουμε να εμφανιστεί το προκαθορισμένο view, διαφορετικά πρέπει να δημιουργήσουμε το δικό μας MKAnnotationView. Δεσμεύουμε λοιπόν μνήμη για αυτό, και στρεφόμαστε να δούμε τι αρχικοποίηση μπορούμε να κάνουμε. Η προκαθορισμένη είναι η απλή init, που ορίζεται από το NSObject, αλλά αν κοιτάξουμε πάλι το documentation της Apple, θα δούμε ότι υπάρχει μέθοδος στιγμιότυπου: - (id)initwithannotation:(id <MKAnnotation>)annotation reuseidentifier:(nsstring *)reuseidentifier 35

41 Στην οποία περνάμε ως παράμετρο το ίδιο το annotation για το οποίο δημιουργούμε το view, και ένα αλφαριθμητικό με την ένδειξη reuseidentifier. Αυτό είναι ένα από τα συχνά «κόλπα» της Apple, για καλύτερη διαχείρηση μνήμης. Όταν αφαιρείται ένα annotation από τον χάρτη, δεν διαγράφεται απ ευθείας. Αντίθετα, το view με το οποίο ήταν συνδεδεμένο κρατιέται στη μνήμη με την ένδειξη του αλφαριθμητικού που έχουμε δώσει. Έτσι σε μεταγενέστερες κλήσεις, αν έχουμε ήδη δημιουργήσει αρκετά annotations, προσπαθούμε πρώτα να πάρουμε ένα ήδη υπάρχoν view: MKAnnotationView *pinview = (MKAnnotationView *)[themapview dequeuereusableannotationviewwithidentifier:[(myannotation*)an notation identifier]]; Αν αυτή η διαδικασία δεν μας δώσει ένα view, τότε θα επιστρέψει nil, και μπορούμε τότε να μπούμε στη διαδικασία να δημιουργήσουμε ένα καινούργιο. Δημιουργώντας το MKAnnotationView, μπορούμε να ορίσουμε χαρακτηριστικά συμπεριφοράς, όπως το αν θα εμφανίζει το συννεφάκι με τις λεπτομέρειες, το αν μπορεί να μετακινηθεί πάνω στον χάρτη, καθώς επίσης και άλλα χαρακτηριστικά, όπως την εικόνα που θα έχει, το αν θα έχει κάποιο κουμπί για περισσότερες πληροφορίες, και άλλα. MKAnnotation Το πως θα επιλέξουμε ποιο MKAnnotationView θα επιστρέψουμε, εν τέλει εξαρτάται από το ίδιο το annotation. Στην εφαρμογή αυτή, ο χάρτης έχει να αντιμετωπίσει τριών ειδών διαφορετικά annotation. - Η επισημείωση που προσθέτει αυτόματα ο χάρτης όταν εντοπίζει τη θέση του χρήστη - Οι επισημειώσεις των στάσεων πάνω στο χάρτη - Οι επισημειώσεις που έχει προσθέσει ο χρήστης Στην πρώτη περίπτωση, δεν θέλουμε να αλλάξουμε κάτι, οπότε για να τηρήσουμε την προκαθορισμένη συμπεριφορά, επιστρέφουμε nil. Στην δεύτερη και τρίτη θέλουμε να δημιουργήσουμε δύο ξεχωριστά είδη από views, με διαφορετική εικόνα και διαφορετική συμπεριφορά. Κατ αρχήν για να μπορέσουμε να ξεχωρίσουμε τις περιπτώσεις, χρησιμοποιούμε την μέθοδο [object iskindofclass:[myclass class]] που μας επιστρέφει μια Boolean τιμή αληθή ή ψευδή. Αυτή η μέθοδος ανήκει στο πρωτόκολλο NSObject που εφαρμόζουν όλες οι κλάσεις (αφού όλες κληρονομούν από την κλάση NSObject που το εφαρμόζει). 36

42 Στην πρώτη περίπτωση παίρνω αντικείμενο της κλάσης MKUserLocation. Για να μπορώ να ξεχωρίσω τις άλλες δύο και παράλληλα να έχω και την συμπεριφορά που θέλω, δημιούργησα δύο νέες κλάσεις που εφαρμόζουν το πρωτόκολλο MKAnnotation. - UserAnnotation Η κλάση για τις επισημειώσεις του χρήστη. Υποχρεωτικά υλοποιεί την CLLocationCoordinate2D coordinate, και (nonatomic, copy) NSString * (nonatomic, copy) NSString * subtitle; Δίνουμε την δυνατότητα αρχικοποίησης με συντεταγμένες, καθώς επίσης και μια μέθοδο που επιστρέφει τις συντεταγμένες, τον τίτλο και τον υπότιτλο σε ένα NSString. Αυτό διευκολύνει την διαδικασία αποθήκευσης που έχουμε σχεδιάσει και θα δούμε παρακάτω. - MyAnnotation Αρκετά παρόμοια με την κλάση που περιγράφεται παραπάνω, η MyAnnotation είναι αυτή που χρησιμοποιείται για τις στάσεις των λεωφορείων. Έχει όλα τα στοιχεία της UserAnnotation, και επιπρόσθετα δύο (nonatomic, retain) UIImage * (nonatomic, retain) NSString * identifier; Και μια πιο πλήρη μέθοδο αρχικοποίησης. Η σημασία των δύο αυτών ιδιοτήτων είναι η εξής. Η εικόνα που δίνω στο annotationview δημιουργείται αλλάζοντας το μέγεθος μιας εικόνας που έχω συμπεριλάβει στο main bundle. Αυτή την εικόνα περνάω και σε κάθε annotation πρώτον για να είναι εύκολα προσβάσιμη και να μην χρειάζεται να αρχικοποιώ κάθε φορά νέο αντικείμενο, και δεύτερον επειδή στην περίπτωση που θέλω να ξεχωρίσω δύο στάσεις μεταξύ τους, τους δίνω διαφορετικές εικόνες διαφορετικού χρώματος. Για τον δεύτερο λόγο δημιουργείται και η ανάγκη να έχουν αυτές οι διαφορετικές στάσεις και διαφορετικούς identifier με τους οποίους γίνεται η επιλογή όταν επαναχρησιμοποιούνται τα annotationviews. Αν έδινα σε όλες τις MyAnnotation επισημειώσεις τον ίδιο δείκτη, δεν θα είχα έλεγχο πάνω σε στάσεις διαφορετικού χρώματος, αφού, ψάχνοντας για annotationview να επαναχρησιμοποιήσει, δεν θα μπορούσε να τις διακρίνει κάπως. Βλέποντας τα πολλά παρόμοια στοιχεία στις δύο κλάσεις θα σκεφτόταν κανείς να χρησιμοποιήσει τον μηχανισμό της κληρονομικότητας, μειώνοντας έτσι τον κώδικα που πρέπει να γράψουμε. Ωστόσο αν η μια ήταν υποκλάση της άλλης, θα δημιουργούταν το εξής πρόβλημα. Επειδή ο έλεγχος γίνεται σε επίπεδο κλάσης, 37

43 στην ερώτηση αν το αντικείμενο ανήκει στην κλάση UserAnnotation, και τα δύο αντικείμενα θα απαντούσαν YES, διότι κάθε στιγμιότυπο μιας κλάσης είναι και στιγμιότυπο όλων των υπερκλάσεων στην ιεραρχία αυτή. Συνεπώς μένουμε σε αυτή την υλοποίηση, με δύο ανεξάρτητες κλάσεις. Προσθήκη selector σε UIButton Στο συννεφάκι των επισημειώσεων έχουμε προσθέσει και ένα κουμπί το οποίο δίνει περισσότερες πληροφορίες για την συγκεκριμένη επισημείωση. Επειδή το callout view δεν μπορεί να τροποποιηθεί στο interface builder (ή το αντίστοιχο κομμάτι στην έκδοση 5.0 του XCode), το κουμπί αυτό δημιουργήθηκε με κώδικα. Πήραμε ένα στιγμιότυπο ενός έτοιμου κουμπιού από την κλάση UIButton, του αλλάξαμε το στυλ στο επιθυμητό, περιορίσαμε το μέγεθος σε ένα συγκεκριμένο πλαίσιο (frame) και προκειμένου όταν πατιέται να έχει κάποια λειτουργία, πρέπει να του δώσουμε ένα selector μιας μεθόδου για να εκτελεί. [button addtarget:self forcontrolevents:uicontroleventtouchupinside]; Έπειτα το στιγμιότυπο αυτό το δίνουμε στο MKAnnotationView μέσω της κατάλληλης setter μεθόδου. Αυτή θα το κάνει retain, οπότε το στιγμιότυπο που έχουμε δημιουργήσει εμείς είμαστε ελεύθεροι να το κάνουμε release. Προσθήκη Annotation Χρήστη Επόμενη λειτουργία είναι αυτή της προσθήκης μιας νέας επισημείωσης με εντολή του χρήστη. Αυτό γίνεται με το κουμπί με το σύμβολο της πρόσθεσης. Με το πάτημά του, γίνονται τα ακόλουθα βήματα: - Πρώτον φέρνουμε τον χάρτη στην κατάσταση όπου εμφανίζουμε τις επισημειώσεις του χρήστη. - Δεύτερον δημιουργούμε ένα νέο στιγμιότυπο UserAnnotation με τις συντεταγμένες του κέντρου του χάρτη (στο σημείο που κοιτάμε αυτή την στιγμή δηλαδή) και κάποια προκαθορισμένα στοιχεία για τίτλο και υπότιτλο. - Ύστερα δημιουργούμε ένα νέο νήμα για να εμφανίσει ένα modal view controller για να δώσουμε τα δικά μας δεδομένα για την επισημείωση. - Τέλος, ή μάλλον το τελευταίο βήμα που εκτελεί το κύριο νήμα, είναι να αποθηκεύσει στο αρχείο τη νέα επισημείωση. 38

44 Η χρήση του modal view controller θα εξηγηθεί αναλυτικά παρακάτω, όταν θα μιλήσουμε για την επεξεργασία ήδη υπαρχόντων επισημειώσεων. Για να καταλάβουμε όμως τη λογική πίσω από τη διαδικασία τόσο της προσθήκης όσο και της επεξεργασίας, πρέπει να δούμε πως γίνεται η αποθήκευση των επισημειώσεων του χρήστη στο δίσκο. Αποθήκευση σε αρχείο Όπως έχουμε ήδη αναφέρει, για κάθε εφαρμογή ορίζεται και χώρος στην δευτερεύουσα μνήμη της συσκευής, για διάφορους λόγους, όπως αρχεία, εικόνες, ρυθμίσεις και ακόμα και για τις βάσεις δεδομένων. Αυτός ο χώρος διαφέρει από τον χώρο στον οποίο τοποθετεί η εφαρμογή τα ήδη γνωστά αρχεία της. Πιο συγκεκριμένα, έχουμε τον διαχωρισμό του Main Bundle, που έχει τις εικόνες και τα αρχεία που προσθέτουμε εμείς στο project, και το Documents Library, που είναι αυτός ο χώρος που διατίθεται στην εφαρμογή. Βασική διαφορά σε αυτούς τους δύο χώρους, είναι ότι αν και μπορούμε με τον ίδιο ακριβώς τρόπο να διαβάσουμε ένα αρχείο, όπου και αν βρίσκεται, όταν ερχόμαστε στην εγγραφή, τα πράγματα διαφέρουν. Επειδή το Main Bundle είναι ο γνωστός χώρος της εφαρμογής που έχουμε γνωστά δεδομένα, δεν μπορούμε να τον αλλάξουμε, ούτε καν να προσθέσουμε κάτι. Οποιαδήποτε αλλαγή θα κινδύνευε να κάνει την εφαρμογή μας να κρεμάσει, και για αυτόν το λόγο δεν μας δίνεται το δικαίωμα. Αξίζει να σημειωθεί ότι οι μέθοδοι που προσπαθούν να γράψουν στο bundle, δεν αποτυχάνουν ούτε επιστρέφουν κάποια αρνητική τιμή ή μήνυμα. Επιστρέφουν μάλιστα επιτυχία, και απλά δεν κάνουν την εγγραφή. Χρησιμοποιούμε λοιπόν το χώρο του Documents για εγγραφές και αλλαγές. Για να πάρουμε την διεύθυνση αυτού του χώρου, επικαλούμαστε τον εξής κώδικα: NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsdirectory = [paths objectatindex:0]; Αυτό μας οδηγεί σε ένα string το οποίο έχει την διαδρομή μέχρι τον φάκελο του Documents. Σε αυτό θέλουμε να προσθέσουμε το όνομα του αρχείου που θέλουμε να γράψουμε. Δεν ξέρουμε όμως, αν το προηγούμενο string έχει τις ανάποδες καθέτους ( \ - backslash) στο τέλος όπως θα έπρεπε, ή αν πρέπει να τις βάλουμε εμείς. 39

45 Σαν διευκόλυνση, για να μην κάνουμε τέτοιους ελέγχους συνέχεια, υπάρχει η μέθοδος: NSString *appfile = [documentsdirectory stringbyappendingpathcomponent:@"userannots"]; Που διευκρινίζει ότι πρόκειται για διαδρομή αρχείου και ο κώδικας είναι έτοιμος να δώσει το σωστό αποτέλεσμα. Στη συνέχεια πρέπει να χρησιμοποιήσουμε δύο κλάσεις, την NSFileManage και την NSFileHandle. Η πρώτη είναι αυτή που μπορεί να ελέγξει αν υπάρχει το αρχείο στη διαδρομή που ορίσαμε. Αν όχι αυτή η κλάση μπορεί να το δημιουργήσει. Η δεύτερη κλάση είναι αυτή που επιτρέπει το διάβασμα και την εγγραφή σε αρχείο. Ήδη αυτός είναι αρκετός κώδικας για το viewcontroller που θέλει απλά να απεικονίζει διαφορετικά annotations πάνω στο χάρτη. Για να είναι πιο καθαρός και συνεπής ο κώδικας, δημιουργούμε μια νέα κλάση, την FileManager. Δεν μας ενδιαφέρουν οι ιδιότητες κάποιας άλλης έτοιμης κλάσης, οπότε η FileManager κληρονομεί απλά την NSObject. Αφαιρετικά θέλουμε τρεις διαφορετικές ενέργειες με το αρχείο των επισημειώσεων του χρήστη: - Την ανάγνωσή τους από το αρχείο - Την πρόσθηκη νέας γραμμής με μια νέα επισημείωση - Την εγγραφή από την αρχή του αρχείου Δημιουργούμε λοιπόν για την κλάση μια νέα μέθοδο αρχικοποίησης, που δείχνει αν θέλουμε να διαβάσουμε ή να γράψουμε, και σε ποια διαδρομή αρχείο να το κάνουμε. Ο λόγος για την πρώτη παράμετρο είναι ότι το NSFileHandle μας δίνει διαφορετικό αντικείμενο όταν η επιθυμητή συμπεριφορά είναι η ανάγνωση, και άλλο όταν είναι εγγραφή. Αντιστοιχεί στις μεθόδους κλάσης του NSFileHandle: handler = [NSFileHandle filehandleforreadingatpath:path]; handler = [NSFileHandle filehandleforwritingatpath:path]; Επόμενο εμπόδιο είναι ότι οι μέθοδοι για ανάγνωση και εγγραφή λειτουργούν με NSData και όχι NSString. Δημιουργούμε δύο μεθόδους για να κάνουν αυτή την μετατροπή. Προφανώς για να ολοκληρωθεί πρέπει να κάνουμε και μια παραδοχή για την κωδικοποίηση των χαρακτήρων (encoding). Έπελεξα την πιο απλή μορφή και όλα τα αρχεία κειμένου της εφαρμογής είναι σε κωδικοποίηση UTF-8. Τις μεθόδους αυτές για χάρη 40

46 γενικότητας τις έκανα μεθόδους κλάσης, καθώς δεν είναι απαραίτητο ότι μια τέτοια μετατροπή θα την χρειαστώ μόνο σε εγγραφή αρχείου. Οι μέθοδοι μετατροπής είναι διαθέσιμες στις κλάσεις NSString και NSData, και προκαλούν σφάλμα αν δεχτούν σαν παράμετρο το nil. Το λαμβάνουμε υπόψιν και κάνουμε τον αντίστοιχο έλεγχο πριν επιστρέψουμε την τιμή που θέλουμε. Η ανάγνωση δεδομένων γίνεται είτε με δεδομένο αριθμό bytes είτε μέχρι το τέλος του αρχείου. Αναγκαστικά κινούμαστε μέχρι το τέλος του αρχείου, καθώς αν και οι συντεταγμένες έχουν συγκεκριμένο μήκος λόγω της διπλής ακρίβειας αριθμούς που χρησιμοποιούμε, δεν μπορούμε να ξέρουμε τα κείμενα που έχει δώσει ο χρήστης για τίτλο και υπότιτλο. Το ίδιο ακριβώς πρόβλημα έχουμε και στην εγγραφή. Η προσθήκη μιας νέας γραμμής στο αρχείο γίνεται μετακινώντας τον δείκτη που δείχνει το σημείο στο οποίο κοιτάμε μέσα στο αρχείο, στο τέλος του, και γράφοντας εκεί ότι θέλουμε. Στην ενημέρωση όμως μιας επισημείωσης για παράδειγμα, αν έχει αλλάξει όνομα ή αν έχει αλλάξει θέση, δεν μπορούμε να ξέρουμε σε ποιο σημείο του αρχείου να γράψουμε. Η λύση είναι ότι στην ενημέρωση παίρνουμε τα στοιχεία των επισημειώσεων και τα γράφουμε από την αρχή στο αρχείο. Ένα πιθανό πρόβλημα είναι ότι, αν τα νέα δεδομένα έχουν μικρότερο μήκος από τα προηγούμενα, το υπόλοιπο της διαφοράς τους θα παραμείνει στο αρχείο. Για να το λύσουμε αυτό, καθαρίζουμε όλο το αρχείο μέχρι ενός σημείου (συγκεκριμένα μέχρι την αρχή του), και μετά κάνουμε την εγγραφή. Το τίμημα είναι θεωρητικά σχετικά μικρό γιατί τα δεδομένα που ενδέχεται να προσθέσει ο χρήστης, σπανίως θα υπερβούν τις μερικές δεκάδες εγγραφές. Σε περίπτωση που ο αριθμός των επισημειώσεων χρήστη ξέφευγε σε αριθμό, θα ήταν προτιμότερη η προσέγγιση των βάσεων δεδομένων. Έτσι τελικά η εγγραφή και ανάγνωση των επισημειώσεων γίνεται στο αρχείο UserAnnots που δημιουργείται στο Documents Directory της εφαρμογής. Το αρχείο αυτό στην πρώτη του γραμμή έχει πάντα το Title;Subtitle;Latitude;Longitude\n Και αυτή είναι και η μορφή με την οποία αποθηκεύονται τα δεδομένα, δηλαδή το κάθε στοιχείο διαχωρίζεται από το επόμενο με ένα ελληνικό ερωτηματικό. Αντίστοιχα στην ανάγνωση για δεδομένα αγνοείται η πρώτη γραμμή, και οι υπόλοιπες διαχωρίζονται με τα ερωτηματικά. Η επισημείωση προστίθεται, μόνο αν στο αρχείο τηρείται η παραπάνω μορφή. Αν τα κομμάτια που διαχωρίζονται με τα 41

47 ερωτηματικά δεν είναι ακριβώς τέσσερα, η γραμμή αυτή του κειμένου αγνοείται επίσης. Προσθήκη Annotation Χρήστη #2 Για να επιστρέψουμε στο πρόβλημα της προσθήκης επισημειώσεων του χρήστη: Μετά από κάθε προσθήκη, αλλαγή ή διαγραφή, το αρχείο κειμένου γράφεται από την αρχή. Τα δεδομένα με τα οποία το γεμίζουμε, θέλουμε να είναι μόνο οι επισημειώσεις του χρήστη. Αν στον χάρτη δεν εμφανίζονται αυτές, και φαίνονται οι στάσεις ή μόνο η επισημείωση που μόλις προσθέσαμε, θα γράψουμε στο αρχείο λανθασμένα δεδομένα. Συνεπώς, το πρώτο βήμα είναι να προσαρμόσουμε τον χάρτη ώστε να εμφανίσει τις σωστές επισημειώσεις. Αμέσως μετά, δημιουργούμε το στιγμιότυπο του νέου annotation, και προσθέτουμε μια νέα γραμμή στο αρχείο, για να υπάρχει η καταχώρηση ακόμα και αν διακοπεί η διαδικασία για κάποιον άλλο λόγο. Το επόμενο βήμα είναι να εμφανίσουμε ένα νέο view από το οποίο θα πάρουμε τα δεδομένα για το annotation αυτό. Για αυτή τη δουλειά συγκεκριμένα υπάρχουν οι modal view controllers, και είναι η φιλοσοφία του προγραμματισμού σε περιβάλλον iphone να χρησιμοποιούνται ως μεσολαβητές για να πάρει το πρόγραμμα περισσότερες πληροφορίες από τον χρήστη για ένα συγκεκριμένο αντικείμενο. Ο λόγος που δημιουργούμε ένα νέο νήμα, είναι για να προλάβει ο χρήστης να δει ότι ο χάρτης έχει αλλάξει και δείχνει πλέον τις επισημειώσεις του, και ότι προστίθεται μια νέα. Βάζουμε το νέο νήμα να «κοιμηθεί» για μερικά δέκατα του δευτερολέπτου και μετά να εμφανίσει το modal view controller. Αυτή η καθυστέρηση δεν μπορεί να γίνει στο κυρίως νήμα, καθώς αυτό θα σήμαινε ότι όλη η εφαρμογή θα σταματούσε, συνεπώς οι σημειώσεις στον χάρτη δεν θα φαίνονταν, αλλά θα βλέπαμε τον controller μόνο μερικές στιγμές μετά. Μέχρι εδώ έχουμε δει τι κάνουν όλα τα κουμπιά του view του PatrasRoutesViewController εκτός από τα τελευταία δύο και δεν έχουμε δει ακριβώς πώς όταν κάνουμε προσθήκη μιας επισημείωσης στο χάρτη παίρνουμε τα δεδομένα και πώς εμφανίζουμε το νέο view. Έχουμε ήδη αναφέρει την ιδέα του modal view controller, και στη συνέχεια θα την αναλύσουμε περισσότερο, μαζί με τα views που δεν έχουμε σχολιάσει ακόμα. 42

48 3.2.3 Modal View Controllers και υπόλοιπα views Οι modal view controllers, δεν αποτελούν κάποιο νέο αντικείμενο της objective c, παρά πρόκειται για ένα μηχανισμό παρουσίασης των views. Οποιοδήποτε view μπορεί να παρουσιαστεί έτσι και να αποσυρθεί αντίστοιχα, με δύο πολύ απλές εντολές: [self presentmodalviewcontroller:(uiviewcontroller *)viewcontroller animated:(bool)animated]; [self dismissmodalviewcontrolleranimated:(bool)animated]; Παρατηρούμε ότι η πρώτη εντολή δέχεται ένα view controller ως παράμετρο, το οποίο αντιστοιχεί στο view που θα παρουσιαστεί, ενώ η δεύτερη δεν δέχεται καμία. Αντίθετα, αποσύρει το τελευταίο view που παρουσιάστηκε modally. Υπάρχει η δυνατότητα εμφωλευμένων modal view controllers, και η κάθε απόσυρση αφαιρεί τον πιο πρόσφατο. Επίσης κάθε modal view controller μπορεί να δημιουργήσει το δικό του navigation stack, ανεξάρτητα από το κύριο view. Η χρήση των modal view controllers είναι κυρίως για την επιλογή, πρόσθεση ή προβολή δεδομένων, που δεν έχουν σχέση κατά κάποιο τρόπο με την κύρια ροή του προγράμματος. Στην συγκεκριμένη εφαρμογή, χρησιμοποιούνται για να προσθέσουμε, να αφαιρέσουμε ή να ενημερώσουμε μια επισημείωση στο χάρτη, να πάρουμε παραμέτρους για να απεικονίσουμε αντίστοιχες στάσεις, και να παρουσιάσουμε για μια συγκεκριμένη στάση περιταίρω πληροφορίες. Η φιλοσοφία τους είναι ότι η κύρια ροή της εφαρμογής διακόπτεται, μέχρι να ληφθούν τα δεδομένα, ή γενικότερα μέχρι να τελειώσει ο χρήστης την αλληλεπίδρασή του με το modal view controller. Για αυτό τον λόγο, το νέο view εμφανίζεται συνήθως σε ολόκληρη την οθόνη, και δεν επιτρέπει στον χρήστη να επικοινωνήσει με το view στο οποίο βρισκόταν προηγουμένως. Σε ανάπτυξη εφαρμογών για ipad ή για MacOS, υπάρχει η επιλογή να μην καλύπτεται ολόκληρη η οθόνη, αλλά και πάλι δεν επιτρέπεται η αλληλεπίδραση με το πίσω view. Ένα σημαντικό σημείο, είναι το ποιος θα αποσύρει το modal view controller. Επειδή όπως είπαμε, η εντολή της απόσυρσης δεν χρειάζεται κάποιον δείκτη σε αντικείμενο, οποιοσδήποτε μπορεί να την καλέσει, ακόμα και αν δεν γνώριζε καν για την ύπαρξη του view που απεικονίζεται. Είναι προτιμητέο όποιος παρουσίασε το νέο view, να είναι και αυτός που θα το αποσύρει. Και ξανά, υπάρχουν πολλές διαφορετικές τεχνικές επικοινωνίας, ώστε να ενημερωθεί το αρχικό view ότι πρέπει να αποσύρει το νέο. Εδώ, καθαρά από σύμβαση και πάλι, ακολουθούμε τον μηχανισμό του delegation. 43

49 Ορίζουμε ένα πρωτόκολλο, και όταν η αλληλεπίδραση ολοκληρωθεί ή ακυρωθεί, το view που παρουσιάζεται modally, ενημερώνει τον αρμόδιό του (delegate) με το αντίστοιχο μήνυμα. Τότε μπορεί ο delegate να το αποσύρει. Τέλος υπάρχουν και αρκετές παράμετροι που αφορούν στο animation ή τον τρόπο παρουσίασης, που μπορούμε πολύ εύκολα να δούμε από το documentation της Apple και να επιλέξουμε. Σημαντική παρατήρηση είναι ότι όσο είναι ενεργός ένας modal view controller, το πίσω view δεν έχει εξαφανιστεί, ούτε αφαιρείται από τη μνήμη. Αυτό σημαίνει ότι μπορούμε προγραμματιστικά ακόμα να κάνουμε αλλαγές σε αυτό ανάλογα με το τι γίνεται στον modal view controller, καθώς επίσης ότι δεν θα εκτέλεστει κώδικας που έχουμε γράψει στο viewdidunload: και dealloc. Το προηγούμενο view είναι ενεργό, απλά ο χρήστης δεν μπορεί να αλληλεπιδράσει μαζί του. Ας συνεχίσουμε την παρουσίαση των views της εφαρμογής. Για να εμφανίσουμε οποιοδήποτε view, το κάνουμε alloc και init, ορίζουμε σαν delegate το PatrasRoutesViewController, και το παρουσιάζουμε modally: Π.χ.: DetailedAnnotViewController *annotviewcontrollerforedit = [[DetailedAnnotViewController alloc] initwithuserannotation:usann]; [annotviewcontrollerforedit setdelegate: self]; [annotviewcontrollerforedit setmodalpresentationstyle:uimodalpresentationpagesheet]; [self presentmodalviewcontroller:annotviewcontrollerforedit animated:yes]; Μετά από αυτά, δεν χρειαζόμαστε άλλο εμείς τον δείκτη στο view controller, γιατί έχει γίνει retain, οπότε κάνουμε release για να εξισορροπήσουμε το retain count. [annotviewcontrollerforedit release]; DetailAnnotViewController Η προσθήκη, ενημέρωση και αφαίρεση μιας επισημείωσης του χάρτη όλες καλούν και εμφανίζουν το ίδιο view, το DetailAnnotViewController. Είναι ένα σχετικά απλό 44

50 view, με δύο πεδία κειμένου, ένα για τον τίτλο της επισημείωσης, και ένα για τον υπότιτλο, όπως υποδεικνύουν και οι αντίστοιχες ετικέτες. Έχει επίσης ένα κουμπί που εφαρμόζει τις αλλαγές που έχουμε κάνει, και άλλο ένα που αφαιρεί την επισημείωση. Στην μέθοδο αρχικοποίησης του view controller, δεχόμαστε ως παράμετρο ένα UserAnnotation. Στην μέθοδο viewdidload που καλείται αμέσως πριν εμφανιστεί το view στην οθόνη, αρχικοποιούμε τα πεδία κειμένου με τον τίτλο και τον υπότιτλο του UserAnnotation. Επίσης θέτουμε ως delegate και των δύο πεδίων κειμένου (που δεν είναι τίποτα αλλό από δύο αντικείμενα της κλάσης UITextField) τον ίδιο τον view controller, έτσι ώστε να μπορούμε να καθορίσουμε την συμπεριφορά του πληκτρολογίου που εμφανίζεται. Στο παρόν view μας ενδιαφέρει απλά όταν ο χρήστης πατάει το πλήκτρο της επιστροφής (enter), ο view controller να αποσύρει το πληκτρολόγιο. Οπότε υλοποιούμε την μέθοδο: - (BOOL)textFieldShouldReturn:(UITextField *)thetextfield Και δίνουμε την εντολή στο πεδίο κειμένου να το αποσύρει: [thetextfield resignfirstresponder]; Ύστερα μένει να καθορίσουμε την συμπεριφορά των δύο κουμπιών. Και οι δύο μέθοδοι θα καταλήξουν να καλέσουν το PatrasRoutesViewController και να αποσύρει το modal view. Πέρα όμως από αυτό το κομμάτι πρέπει να έχουμε δύο διαφορετικές αντιδράσεις, ανάλογα με το πιο κουμπί πατήθηκε. Στην περίπτωση της διαγραφής, στέλνουμε το μήνυμα: [delegate removespecificannotation]; Το οποίο το PatrasRoutesViewController υλοποιεί αφαιρώντας από τα annotations του χάρτη το επιλεγμένο, και κατόπιν αποθηκεύει την αλλαγή στο αρχείο. Στην περίπτωση της ενημέρωσης, στέλνεται το μήνυμα: [delegate annotationwasedittedwithtitle:newtitle andsubtitle:newsubtitle]; Στο οποίο η αντίδραση είναι να πάρει τις δύο παραμέτρους newtitle, newsubtitle και την επιλεγμένη επισημείωση και να της αλλάξει τον τίτλο και υπότιτλο. Εδώ πρέπει να προσέξουμε να μην αποσύρουμε τον modal view controller πριν τελειώσουμε την επεξεργασία με τις παραμέτρους, ή τουλάχιστον να τις κάνουμε 45

51 retain. Ο λόγος είναι ότι τα δύο αλφαριθμητικά αυτά ελέγχονται και γίνοται autorelease από το DetailedAnnotViewController (συγκεκριμένα από τα UITextFields αυτού). Αν αποσύρουμε το view, αυτό αυτόματα θα φύγει από τη μνήμη, και μαζί με αυτό, όλα τα αντικείμενα για τα οποία είναι υπεύθυνο. DetailedBusStopViewController Αυτό το view παρουσιάζεται επίσης ως modal view controller, και έχει σκοπό να δώσει περισσότερα στοιχεία για την επιλεγμένη στάση λεωφορείου. Ο τρόπος με τον οποίον εμφανίζεται, είναι μέσω της κλήσης της μεθόδου: -(IBAction)showMoreInfo της οποίας τον selector δώσαμε στο κουμπί που προσθέσαμε στο annotationview. Για την αρχικοποίηση του view controller χρειαζόμαστε ένα αντικείμενο στάσης, το οποίο το παίρνουμε από την βάση δεδομένων. Το πως γίνεται αυτό θα αναλυθεί παρακάτω. Σε αυτό το σημείο ας υποθέσουμε ότι παίρνουμε ένα τέτοιο αντικείμενο, το οποίο έχει όλα τα στοιχεία που έχει και το αντίστοιχο annotation, συν τις γραμμές των λεωφορείων που εξυπηρετεί. Αν δεν ήταν για τις γραμμές των λεωφορείων, θα μπορούσαμε να έχουμε χρησιμοποιήσει απλά το ίδιο το annotation. Αυτό το view έχει ένα κουμπί για να επιστρέψουμε πίσω στον χάρτη, και ετικέτες που ανάλογα με την στάση που επιλέξαμε εμφανίζουν το αντίστοιχο όνομα, κωδικό, και συντεταγμένες. Επιπλέον, έχουμε προσθέσει ένα UITextView, το οποίο είναι ένα view από το UIKit που υποστηρίζει εμφάνιση πολλαπλών γραμμών κειμένου, καθώς και κύλιση προς τα πάνω ή κάτω, αν το κείμενο δεν χωράει ολόκληρο στην οθόνη. Στο UITextView, εμφανίζουμε τους αριθμούς και τα ονόματα των γραμμών των λεωφορείων που εξυπηρετούν τη συγκεκριμένη στάση. FilterAndSearch Το τελευταίο view που παρουσιάζεται modally, είναι το FilterAndSearch, που είναι υπεύθυνο για το φιλτράρισμα των στάσεων με βάση κάποιο κριτήριο, καθώς επίσης για την αναζήτηση της διαδρομής από ένα σημείο της Πάτρας σε ένα άλλο. Το κύριο χαρακτηριστικό του είναι το UISegmentedControl, που είναι ένα View ενός πολλαπλού κουμπιού, που μας δίνει την δυνατότητα να επιλέξουμε ανάμεσα σε διαφορετικές τιμές. 46

52 Στην περίπτωσή μας, έχει τρεις διαφορετικές τιμές: - Γραμμή - Προορισμός - Από / Εώς Η συμπεριφορά που έχουμε υλοποιήσει, είναι πατώντας το κάθε διαφορετικό κομμάτι του κουμπιού, να εμφανίζουμε από κάτω το αντίστοιχο υπό-view (subview) που αντιστοιχεί στην λειτουργία που επιλέξαμε. Για να γίνει αυτό, απλά αφαιρούμε το ήδη υπάρχον view, και προσθέτουμε το νέο, με τις εντολές: [buspicker.view removefromsuperview]; Και: [self.view addsubview:router.view]; Αντίστοιχα. Στην περίπτωση της αφαίρεσης ουσιαστικά λέμε στο ίδιο το υπο-view να το αφαιρέσει από οποιοδήποτε άλλο view είναι εμφωλευμένο, ενώ στην άλλη περίπτωση, λέμε στο view του FilterAndSeach να προσθέσει ένα νέο. Αν προσπαθήσουμε να αφαιρέσουμε το view πριν του δώσουμε μνήμη και το αρχικοποιήσουμε, είναι προφανές ότι το πρόγραμμα θα συναντήσει κακή μνήμη και θα τερματίσει, για αυτό πριν από κάθε αφαίρεση, κάνουμε τον έλεγχο: if (buspicker) [buspicker.view removefromsuperview]; Προεπιλεγμένη είναι η λειτουργία της γραμμής λεωφορείων, οπότε με το που φορτώσει το view στη μνήμη (viewdidload μέθοδος), καλούμε την μέθοδο changedchoice, που θα εμφανίσει το κατάλληλο subview. Για να μην δεσμεύουμε περιττό χώρο στη μνήμη, τα τρία διαφορετικά subviews δεν γίνονται allocate μέχρι να πατηθεί το κουμπί που αντιστοιχεί στην λειτουργία τους. if (!router) router = [[RouteSelector alloc] init]; [router setdelegate:self]; CGRect frame = CGRectMake(0.0, 70.0, 320.0, 216.0); router.view.frame = frame; [self.view addsubview:router.view]; Σε όλα τα νέα views θέτουμε το FilterAndSearch ως delegate, ώστε όλες οι επιστρεφόμενες τιμές να έρχονται σε ένα κοινό view controller πριν επιστρέψουν 47

53 στο βασικό χάρτη για να φιλτράρουν τις στάσεις. Έχουμε λοιπόν και τρία διαφορετικά πρωτόκολλα τα οποία θα εφαρμόζει το FilterAndSearch : UIViewController <RouteSelectorDelegate,DestinationPickerDelegate,BusLinePicker Delegate> Τέλος το κάθε ένα από τα τρία αυτά views, εμφανίζεται σε ένα περιορισμένο πλαίσιο (frame), διότι το βασικό FilterAndSearch view περιέχει και τρία κουμπιά, ένα για την εφαρμογή των επιλογών που κάναμε, ένα για την ακύρωση όλων των επιλογών, και ένα που μας επιστρέφει στο βασικό χάρτη χωρίς να κάνει καμία αλλαγή. Εμφανίζονται λοιπόν σε ένα πλαίσιο 320 x 216 pixels, ξεκινώντας 70 pixels από την κορυφή του view, καθώς εκεί είναι το UISegmentedControl. Υπάρχει μια delegate μέθοδος που ορίζεται στο FilterAndSearchDelegate πρωτόκολλο, το οποίο υλοποιεί το PatrasRoutesViewController, για κάθε μια από αυτές τις περιπτώσεις, που κάνει την εφαρμογή του φιλτραρίσματος και αποσύρει τον modal view controller CoreData Για να δούμε αναλυτικά τα τρία subviews που ελέγχουν τις παραμέτρους του φιλτραρίσματος, πρέπει να δούμε λίγο πιο αναλυτικά την μορφή της βάσης δεδομένων που έχουμε δημιουργήσει στο CoreData. Σχήμα Βάσης Δεδομένων Αρχικά αναλύουμε το σχήμα (schema) της βάσης δεδομένων. Στην παρακάτω περιγραφή δεν περιέχονται τα γνωρίσματα που προσθέτει το CoreData, όπως το μοναδικό id που δίνει σε κάθε αντικείμενο. Αναλύονται μόνο οι πραγματικές παράμετροι που έχουν σχέση με τη συγκεκριμένη βάση: 48

54 BusLine line: Ο αριθμός της γραμμής του λεωφορείου. Προκειμένου να διαχωριστούν καλύτερα τα λεωφορεία, κάθε γραμμή έχει και ένα υπο-αριθμό που δηλώνει τις διαφορές σε ένα δρομολόγιο. Για παράδειγμα, το 1.1 είναι το λεωφορείο Εγλυκάδα- Πλαζ, ενώ το 1.2 είναι το λεωφορείο Εγλυκάδα-Τερψιθέα. name: Το όνομα της γραμμή του λεωφορείου. Δηλώνει την διαδρομή που ακολουθεί, για παράδειγμα: Εγλυκάδα-Τερψιθέα. stopsat: Είναι το σύνολο των στάσεων που κάνει το λεωφορείων. Δεν είναι γνώρισμα ουσιαστικά, αλλά σχέση με την άλλη οντότητα, BusStop. Επειδή πρόκειται για Μ προς Ν σχέση, δεν έχει μόνο μια τιμή (ένα id) για μια στάση, αλλά ολόκληρο σετ, και σαν αντικείμενο αποθηκεύεται ένα NSSet. followsroute: Η διαδρομή ανάμεσα στις στάσεις που ακολουθεί το λεωφορείο. Όπως αναφέραμε και στην αρχή της διπλωματικής εργασίας, το CoreData δεν δίνει την δυνατότητα προσθήκης γνωρισμάτων πάνω στην σχέση. Θέλοντας λοιπόν να δώσουμε ως γνώρισμα τουλάχιστον τη σειρά με την οποία επισκέπτεται ένα λεωφορείο τις στάσεις, δημιουργούμε μια νέα οντότητα, που στην ουσία σπάει την Μ προς Ν σχέση σε δύο 1 προς Ν σχέσεις και μια οντότητα που τις διασυνδέει. followsschedule: Το ωρολόγιο πρόγραμμα του λεωφορείου. Επίσης πρόκειται για Ν προς Μ σχέση, οπότε έχουμε ένα σετ ωρολόγιων προγραμμάτων. startsat: Μια στάση που είναι η αφετηρία της γραμμής του λεωφορείου. Δημιουργήθηκε ως προσπάθεια καθορισμού της σειράς των στάσεων που ακολουθούσε το λεωφορείο. Σε μια μη τέλικη έκδοση της εφαρμογής, θεωρούσαμε 49

55 ως επόμενη στάση από ένα σημείο την κοντινότερη από άποψη απόστασης. Για να ισχύει αυτό αναδρομικά, χρειαζόμασταν κάτι σαν τερματικό κανόνα, που ήταν ποια θα είναι η αρχική στάση. Ώστοσο δεν ήταν καλή σαν υλοποίηση, καθώς έκανε λάθη όταν η διαδρομή που ακολουθούσε το λεωφορείο δεν ήταν η κοντινότερη, αλλά έκανε κάποιας μορφή κύκλο. Στην παρούσα έκδοση της εφαρμογής δεν χρησιμοποιείται ιδιαίτερα. isactive: Μια δυαδική μεταβλητή που υποδεικνύει αν η γραμμή είναι ενεργή και εκτελεί δρομολόγια ή όχι. BusStop code: Ο κωδικός της στάσης, ένα αριθμός που ουσιαστικά χρησιμοποιείται ως κλειδί για να διαχωρίζονται οι στάσεις μεταξύ τους. Αυτός ο αριθμός ήταν μαζί με τα υπόλοιπα δεδομένα που μαζέψαμε από το διαδίκτυο και δυστυχώς δεν έδινε καμία πληροφορία περισσότερη από την μοναδικότητα της στάσης. name: Το όνομα της στάσης. Δεν είναι μοναδικό, είναι πολλές οι στάσεις που έχουν ίδιο όνομα, για παράδειγμα υπάρχουν αρκετές στάσεις με το όνομα Κορίνθου. latitude: Το γεωγραφικό πλάτος της στάσης σε μοίρες. Πρόκειται για αριθμό κινητής υποδιαστολής διπλής ακρίβειας. longitude: Αντίστοιχα το γεωγραφικό μήκος της στάσης σε μοίρες. Πρόκειται για αριθμό κινητής υποδιαστολής διπλής ακρίβειας. visitedby: Το σετ των λεωφορείων που επισκέπτονται και εξυπηρετούν την παρούσα στάση. startingpointof: Για ποιο σετ λεωφορείων η παρούσα στάση είναι αφετηρία. ispartofroute: Σε ποια ή ποιες διαδρομές ανήκει η στάση. Route idbus: Το λεωφορείο που επισκέπτεται την ακόλουθη στάση. idstop: Μια στάση λεωφορείου. order: Η σειρά με την οποία επισκέπτεται το λεωφορείο idbus την στάση idstop. Τα δύο πρώτα γνωρίσματα είναι ουσιαστικά σχέσεις με τις άλλες δύο οντότητες, και δημιουργήθηκαν όπως είπαμε για να μπορούμε να καθορίσουμε αυστηρά τη σειρά μιας διαδρομής. 50

56 TimeTable days: Οι μέρες για το συγκεκριμένο ωρολόγιο στιγμιότυπο του προγράμματος. Μπορεί να πάρει τις τιμές: -Καθημερινές -Σάββατο -Κυριακή -Αργίες from: Για το στιγμιότυπο που αναφερόμαστε, από ποια ώρα ξεκινάει. until: Για το στιγμιότυπο που αναφερόμαστε, ποια ώρα σταματάει. frequency: Για το στιγμιότυπο που αναφερόμαστε, η συχνότητα των δρομολογίων. keptby: Το σετ των λεωφορείων που τηρούν το παρών στιγμιότυπο. Οπότε για παράδειγμα ένα τέτοιο αντικείμενο θα μπορούσε να έχει τιμές: days: Καθημερινές from: 15:00 until: 18:00 frequency: 30 keptby: BusLine1, BusLine2, BusLine3 Προφανώς ένα λεωφορείο τηρεί πολλά τέτοιο στιγμιότυπα, και κάθε στιγμιότυπο μπορεί να τηρείται από διαφορετικά λεωφορεία. Συνεπώς πρόκειται για μια Ν προς Μ σχέση. Το CoreData απαιτεί όταν δημιουργείται μια σχέση, να δημιουργείται και η αντίστροφή της. Έτσι έχουμε τα ζεύγη σχέσεων που είδαμε παραπάνω, για παράδειγμα: followsschedule με keptby, και άλλα. Έχει επίσης το εξής πολύ θετικό σημείο, ότι όταν περνάμε δεδομένα, και δώσουμε μια τιμή σε ένα αντικείμενο για μια σχέση, άμεσα ενημερώνεται και δημιουργείται η σχέση και στο άλλο αντικείμενο, ή σε όλα τα άλλα αν πρόκειται για σχέση που δεν είναι 1 προς 1. 51

57 Επίσης υπάρχει η δυνατότητα να δημιουργήσουμε κλάσεις για κάθε μια από τις οντότητες, που κληρονομούν την κλάση NSManagedObject. Αυτές δημιουργούν αυτόματα ιδιότητες (properties) για κάθε ένα από τα γνωρίσματα, και κάποιες παραπάνω μεθόδους που επιτρέπουν να προσθέσουμε αντικείμενα στις σχέσεις, είτε ένα ένα, είτε πολλά μαζί. Τέλος, οι τύποι δεδομένων που υποστηρίζονται είναι αριθμοί (ως αντικείμενα, συνεπώς ως στιγμιότυπα της NSNumber), αλφαριθμητικά (NSString), ημερομηνίες (NSDate), και οι διάφορες σχέσεις, οπότε οποιοδήποτε αντικείμενο που κληρονομεί την NSManagedObject, και σετ αυτών, NSSet. Στην νεότερη έκδοση (ios 5.0), υποστηρίζεται και η νέα κλάση NSOrderedSet για τις σχέσεις, που ουσιαστικά είναι ένα σετ αντικειμένων, αλλά ταξινομημένο. Η παρούσα διπλωματική εργασία ξεκίνησε από ios 3.2, οπότε αυτή η δυνατότητα δεν ήταν διαθέσιμη, για αυτό και καταφύγαμε στην δημιουργία νέας οντότητας. Αν και η υλοποίηση θα ήταν πολύ πιο απλή με NSOrderedSet, η ενδιάμεση οντότητα μας δίνει την δυνατότητα να προσθέσουμε μελλοντικά και άλλα χαρακτηριστικά στην σχέση, όπως για παράδειγμα απόσταση από την προηγούμενη στάση, χρονική καθυστέρηση μέχρι την επόμενη, ίσως κάποια μεταβλητή που δείχνει αν η στάση δεν εξυπηρετείται λόγω έργων, και άλλα. Εισαγωγή Δεδομένων στη Βάση Έχοντας την παραπάνω μορφή στα υπόψιν, προχωράμε στην πρώτη εκχώρηση τιμών στην βάση δεδομένων. Η διαδικασία αυτή πραγματοποιείται την πρώτη φορά που η εφαρμογή ξεκινάει, και διαρκεί μερικα δευτερόλεπτα. Σε κάθε επόμενη έναρξη της εφαρμογής, γίνεται ο έλεγχος αν η βάση δεδομένων είναι άδεια. Αν δεν είναι, δεν προστίθεται τίποτα καινούργιο. Ο έλεγχος αυτός γίνεται ξεχωριστά για κάθε οντότητα. Αφού η δημιουργία εγγραφών γίνεται στο managed object context, τίποτα δεν επικυρώνεται μέχρι να περάσει από τον persistent store coordinator. Αυτό γίνεται σώζοντας το context, στο τέλος κάθε εισαγωγής όλων των στιγμιοτύπων μιας οντότητας. Σε περίπτωση που το γέμισμα διακοπεί ή δεν ολοκληρωθεί για κάποιο λόγο, στην επόμενη έναρξη θα συνεχίσει από το σημείο που είχε μείνει. Για την διαχείρηση της βάσης δεδομένων, τόσο για την εισαγωγή στοιχείων όσο και για την διεπαφή στην οποία θα γίνονται οι ερωτήσεις, δημιουργήσαμε μια νέα κλάση, την CoreDataHandler. 52

58 Κάθε στιγμιότυπο της κλάσης αυτής δημιουργεί το δικό του managed object context καθώς και ένα NSFetchRequest το οποίο χρησιμοποιεί για να κάνει τα διάφορα ερωτήματα πάνω στη βάση. Ξεκινώντας από το managed object context, γίνεται μια ακολουθία κλήσεων που έχουν ως αποτέλεσμα να δημιουργηθούν, με την ακόλουθη σειρά: - Το NSManagedObjectModel, το μοντέλο της βάσης δεδομένων. - Το NSPersistentStoreCoordinator, ο υπεύθυνος για την αποθήκευση στη δευτερεύουσα μνήμη. Του ορίζουμε χώρο αποθήκευσης, ένα.sqlite αρχείο, στο documents directory της εφαρμογής: NSURL *storeurl = [[self applicationdocumentsdirectory] URLByAppendingPathComponent:@"PatrasRoutes.sqlite"]; - Το NSManagedObjectContext, που είναι η διεπαφή πάνω στην οποία εργαζόμαστε, στο οποίο αναθέτουμε τον Persistent Store Coordinator. Αφού δημιουργήσουμε και αρχικοποίησουμε το στιγμιότυπο του CoreDataHandler, καλούμε την μέθοδο -(void)loadbusstopsfromfile για να γεμίσουμε την βάση. Για κάθε οντότητα, πρέπει να ελέγξουμε αν έχει τιμές στη βάση. Εδώ χρησιμοποιούμε για πρώτη φορά το NSFetchRequest. Παίρνει σαν παράμετρο την οντότητα για την οποία ενδιαφερόμαστε να πάρουμε στοιχεία. Χρησιμοποιούμε την κλάση NSEntityDescription για να επιλέξουμε την οντότητα που μας ενδιαφέρει. Ύστερα θέτουμε στο NSFetchRequest την οντότητα στο αντίστοιχο property, και είμαστε έτοιμοι να κάνουμε ερώτηση στη βάση δεδομένων. Επειδή αρχικά δεν μας ενδιαφέρουν τα ίδια τα αντικείμενα που θα επιστραφούν, αλλά το πόσα είναι, και μάλιστα απλά αν είναι μηδέν ή όχι, χρησιμοποιούμε την μέθοδο: [context countforfetchrequest:fetchrequest error:&error] Που επιστρέφει τον αριθμό των αντικειμένων που θα επέστρεφε κανονικά η ερώτηση. Αν αυτός ο αριθμός είναι μηδέν, προχωράμε στα εξής βήματα: - Διαβάζουμε το κατάλληλο αρχείο κειμένου για την οντότητα. - Αναγνωρίζοντας την μορφή του κειμένου, δημιουργούμε ένα νέο managed object με τα κατάλληλα δεδομένα. - Προχωράμε σε επόμενη γραμμή κειμένου και επαναλαμβάνουμε. - Όταν διαβαστούν όλες οι γραμμές, αποθηκεύουμε το context. 53

59 Για τις στάσεις των λεωφορείων, ξεκινάμε αναγνωρίζοντας ποιες στάσεις εξυπηρετούνται από ποια λεωφορεία. Διαβάζουμε λοιπόν το αντίστοιχο αρχείο, το οποίο θα μας δώσει ένα συνδυασμό κωδικών στάσεων και αριθμών λεωφορείων. Δημιουργούμε λοιπόν ένα πίνακα NSArray, του οποίου τα στοιχεία εισάγονται ανά δυάδες. Το πρώτο εκ των δύο είναι ο αριθμός της γραμμής του λεωφορείου, το δε δεύτερο είναι ένα σετ με τις τιμές των κωδικών στάσεων. Ακόμα όμως δεν είμαστε έτοιμοι να πούμε ότι έχουμε ολοκληρώσει την σχέση μεταξύ στάσεων και λεωφορείων. Ο λόγος είναι ότι για να υλοποιήσουμε την σχέση πρέπει να περάσουμε στην εγγραφή της οντότητας το κατάλληλο σετ με δείκτες της άλλης οντότητας. Προχωράμε λοιπόν στην ανάγνωση του αρχείου των στάσεων. Κατά την ανάγνωση του αρχείου, όπως έχουμε περιγράψει στην κλάση FileManager που δημιουργήσαμε, διαβάζουμε όλα τα περιεχόμενα του αρχείου σε ένα εννιαίο NSString. Στη συνέχεια, διαχωρίζουμε το αλφαριθμητικό σε ένα πίνακα από αλφαριθμητικά, όπως διαχωρίζονται από τις νέες γραμμές. Σε αυτό μας βοηθάει η έτοιμη μέθοδος της NSString: alluserlinedstrings = [filestring componentsseparatedbycharactersinset:[nscharacterset newlinecharacterset]]; Από εκεί, για κάθε μια από τις γραμμές, διαχωρίζουμε τα στοιχεία έχοντας ως χαρακτήρα κλειδί το ελληνικό ερωτηματικό: for (NSString *line in alluserlinedstrings) NSArray *separatedstrings = [line componentsseparatedbycharactersinset: [NSCharacterSet charactersetwithcharactersinstring:@";"]]; Δημιουργούμε νέο στιγμιότυπο για την στάση του λεωφορείου, με την κλάση που δημιουργήσαμε από το CoreData, την BusStop. BusStop *stop = [NSEntityDescription insertnewobjectforentityforname:@"busstop" inmanagedobjectcontext:context]; Και της θέτουμε τις τιμές που παίρνουμε από την κάθε γραμμή. Σε αυτό το σημείο, θέλουμε να αρχίσουμε να μαζεύουμε και τους δείκτες των στάσεων για να δημιουργήσουμε σύντομα την σχέση. Να υπενθυμίσουμε ότι επειδή έχουμε ορίσει την σχέση αμφίδρομα, αρκεί να δώσουμε τα κατάλληλα στοιχεία στις εγγραφές της μιας οντότητας, και αυτόματα θα μπουν και στα στοιχεία της άλλης. 54

60 Ελέγχουμε λοιπόν αν η τιμή του κωδικού βρίσκεται στο κάθε σετ του πίνακα που ορίσαμε νωρίτερα, και δημιουργούμε έναν παρόμοιο πίνακα που αντί για σετ με τους αριθμούς των κωδικών, έχει δείκτες σε αντικείμενα BusStop. Σε αυτό το σημείο έχουμε τελειώσει με τις εγγραφές της οντότητας των στάσεων. Πριν προχωρήσουμε, αποθηκεύουμε την κατάσταση, και για να σημειώσουμε κάποια πρόοδο, αλλά και για να μπορούμε στην συνέχεια να εκτελούμε ερωτήματα πάνω στις εγγραφές της οντότητας. Με την αντίστοιχη λογική διαβάζουμε το αρχείο των γραμμών του λεωφορείου, και δημιουργούμε νέα αντικείμενα λεωφορείων. Σε αυτό το σημείο κάνουμε τον έλεγχο αν ο αριθμός της γραμμής είναι στον πίνακα που έχουμε δημιουργήσει, και προσθέτουμε το σωστό σετ με δείκτες στάσεων στο κάθε λεωφορείο. Επόμενο βήμα είναι να θέσουμε τις αφετηρίες των λεωφορείων. Στο αντίστοιχ αρχείο έχουμε τις τιμές για τον κωδικό της στάσης και για τον αριθμό της γραμμής λεωφορείου. Θέλουμε λοιπόν να βρούμε τα δύο αυτά αντικείμενα και να δημιουργήσουμε την σχέση. Θα μπορούσαμε να χρησιμοποιήσουμε το NSFetchRequest για να κάνουμε ένα ερώτημα και να πάρουμε όλα τα αντικείμενα της αντίστοιχης οντότητας. Ύστερα θα έπρεπε να κάνουμε μια αναζήτηση σε όλα τα αντικείμενα και να τα συγκρίνουμε ένα προς ένα για να βρούμε αυτό που ζητάμε. Προφανώς αυτός ο τρόπος δεν είναι βολικός, και δεν εκμεταλλεύεται τις δυνατότητες των βάσεων δεδομένων για ερωτήματα. Θέλουμε να περιορίσουμε την αναζήτησή μας σε αντικείμενα με γνωστό κωδικό στάσης και γραμμής. Για να την περιορίσουμε, χρησιμοποιούμε την κλάση NSPredicate. Δημιουργούμε ένα περιορισμό, τον οποίο θα προσθέσουμε στο NSFetchRequest, και μετά θα προβούμε στο ερώτημα. entity = [NSEntityDescription entityforname:@"busline" inmanagedobjectcontext:context]; [fetchrequest setentity:entity]; predicate = [NSPredicate predicatewithformat:@"line == %@",line]; [fetchrequest setpredicate:predicate]; bus = (BusLine *)[[context executefetchrequest:fetchrequest error:&error] objectatindex:0]; όπου εδώ δίνουμε τον περιορισμό ο αριθμός της γραμμής να είναι αυτός που διαβάσαμε. Επίσης πλέον δεν ζητούμε τον αριθμό των αντικειμένων, αλλά να επιστραφούν όλα τα αντικείμενα. Στην δικιά μας περίπτωση θα είναι σίγουρα ένα, 55

61 αλλά το [context executefetchrequest:fetchrequest error:&error] επιστρέφει πάντα ένα πίνακα με τιμές, οπότε παίρνουμε το πρώτο του στοιχείο (στοιχείο στη θέση μηδέν). Δρούμε αντίστοιχα και για την στάση, και τέλος υλοποιούμε την σχέση: [bus setstartsat:stop]; Αποθηκεύουμε ακόμη μια φορά το context, και προχωράμε στο επόμενο βήμα. Για τα δρομολόγια των λεωφορείων, έχουμε λίγο διαφορετική μορφή στο αρχείο κειμένου, και κάποιες ανωμαλίες που μας ωθούν σε διαφορετική αντιμετώπιση. Πρώτα από όλα, να αναφέρουμε ότι το αρχείο κειμένου αυτό δημιουργήθηκε από parsing ιστοσελίδας, αφαιρώντας html στοιχεία και κρατώντας το περιεχόμενο. Αντιμετωπίζουμε τις εξής δυσκολίες: - Οι κατηγορίες Καθημερινές, Αργίες, Σάββατο και Κυριακή, δεν ήταν η κάθε μια μόνη της, αλλά μπορούσαν να είναι και στην ίδια γραμμή, αν τύχαινε το ωρολόγιο πρόγραμμα να συμπίπτει. Αυτό σημαίνει ότι όταν μιλάμε για μια γραμμή λεωφορείου, ολόκληρη η παράγραφός της πρέπει να εξετάζεται, και δεν μπορούμε να προχωράμε γραμμή γραμμή στο κείμενο. - Οι υποκατηγορίες του ωρολόγιου προγράμματος δεν έχουν σταθερό αριθμό. Δηλαδή μπορεί μια γραμμή να εξυπηρετεί από τις 08:00 ως τις 22:00 ανά 30 λεπτά, οπότε σαν κείμενο θα έπιανε χώρο μιας γραμμής, θα μπορούσε όμως η συχνότητα να άλλαζε μετά τις 12:00, οπότε θα μιλούσαμε για κείμενο δύο γραμμών. - Τέλος στις ίδιες υποκατηγορίες, δεν έχουμε σταθερή μορφή. Άλλες γραμμές ξεκινούν στις 06:00 άλλες στις 05:45, οπότε δεν έχουμε κάτι σταθερό για να αναφερόμαστε. Για να αντιμετωπίσουμε το πρώτο πρόβλημα, αρχικό διαχωρίζουμε το κείμενο που διαβάσαμε σε υποκείμενα με βάση τις γραμμές λεωφορείων. Έχουμε ήδη διαχωρίσει σε ένα πίνακα το κείμενο με βάση τις γραμμές του, οπότε κρατάμε δύο ακέραιους αριθμούς που καθορίζουν το αρχικό και το τελικό σημείο στον πίνακα που αναφέρεται σε μια μονή γραμμή. Για να το κάνουμε αυτό, ελέγχουμε κάθε γραμμή μέχρι να βρούμε μια κενή γραμμή (που διαχωρίζει τα λεωφορεία). 56

62 Τώρα για κάθε κατηγορία (Καθημερινές, Αργίες,...) ελέγχουμε όλο το υποκείμενο μέχρι να βρούμε αυτές τις λέξεις κλειδιά. Μόλις εντοπίσουμε κάποιο, ξέρουμε ότι οι επόμενες γραμμές περιέχουν τιμές για τα δρομολόγια, για τις συγκεκριμένες μέρες. Όπως είπαμε ήδη, δεν ξέρουμε σε πόσες επόμενες γραμμές να κοιτάξουμε, οπότε πρέπει να κάνουμε επαναληπτικά κάποιον έλεγχο. Σαν αρκετά γρήγορη λύση, και επειδή δεν ξέρουμε ακριβώς ποιους αριθμούς ψάχνουμε για ώρες και λεπτά, καταφεύγουμε στην χρήση κανονικών εκφράσεων (regular expressions). NSRegularExpression *regex = [NSRegularExpression *([0-9]2:[0-9]2 )2[0-9]2,3'" options: NSRegularExpressionCaseInsensitive error:&error]; Με αυτή την κανονική έκφραση δηλώνουμε ότι ψάχνουμε για 2 αριθμούς, που ακολουθούνται από (:) διαλυτικά, που ακολουθούνται από άλλους δύο αριθμούς και δύο κενά, και όλη αυτή η έκφραση να υφίσταται δύο φορές, ακολουθώντας μηδέν ή περισσότερα κενά και ακολουθούμενη από δύο ή τρεις ακόμα αριθμούς. Κοινώς ανιχνεύει οποιαδήποτε από τις παρακάτω γραμμές: 12:12 14: :12 18:12 20 Φυσικά αυτή η έκφραση λειτουργεί μόνο στο συγκεκριμένο αρχείο και μόνο επειδή έχει τηρηθεί αυτή η σύμβαση μορφής του, καθώς δεν είναι αρκετά γενική. Όσο ανιχνεύουμε τέτοιες τιμές, προχωράμε τον δείκτη του πίνακα του υποκειμένου. Για κάθε μια από τις τιμές που ανιχνεύουμε, ελέγχουμε αν έχουμε τέτοια καταχώρηση, με τον κατάλληλο περιορισμό: predicate = [NSPredicate == %@ AND from == %@ AND until == %@ AND frequency == %d", [days objectatindex:k],[formatter datefromstring:fromtime],[formatter datefromstring:totime],frequency]; [fetchrequest setpredicate:predicate]; if ([context countforfetchrequest:fetchrequest error:&error] == 0) Αν δεν έχουμε, δημιουργούμε μια τέτοια καταχώρηση, και επειδή μιλάμε για συγκεκριμένο λεωφορείο, κρατάμε και ένα δείκτη στην καταχώρηση για να δημιουργήσουμε αργότερα την σχέση με τη γραμμή λεωφορείου. Αν υπάρχει τέτοια καταχώρηση, απλά προσθέτουμε τον δείκτη της στο κατάλληλο σετ. Παρατηρούμε ότι όταν έχουμε να αναφερθούμε σε αντικείμενα ημερομηνιών NSDate, δεν πρέπει να τα συγκρίνουμε με string. Για αυτό τον λόγο χρησιμοποιούμε 57

63 έναν NSDateFormatter, για να εξάγουμε ημερομηνία από το string (και συγκεκριμένα, μόνο την ώρα της): NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setlocale:[[[nslocale alloc] initwithlocaleidentifier:@"gr_gr"] autorelease]]; [formatter setdateformat:@"hh:mm"]; Όλα τα παραπάνω γίνονται για την κάθε μια κατηγορία (Καθημερινές, Σάββατο, Κυριακή, Αργίες). Μόλις γίνουν και για τις τέσσερις, έχουμε τελειώσει τους ελέγχους για το συγκεκριμένο λεωφορείο, όποτε του προσθέτουμε το σετ της σχέσης, και προχωράμε στο επόμενο υποκείμενο. Τέλος, με εντελώς παρόμοια λογική συμπεριφερόμαστε και για το τελευταίο αρχείο κειμένου, που περιέχει για κάθε γραμμή λεωφορείου τις στάσεις ταξινομημένες με σειρά επίσκεψης. Για να εξαχθεί αυτό το αρχείο κειμένου, δεν υπήρχε καμία απολύτως πληροφορία στα δεδομένα που λάβαμε από το διαδίκτυο, οπότε έγινε εξ ολοκλήρου με το χέρι. Δυστυχώς υπήρχαν διαδρομές που δεν ήταν ξεκάθαρο ποια είναι η σωστή ακολουθία στάσεων, είτε γιατί οι στάσεις ήταν μπερδεμένες, είτε γιατί κάποιες διαδρομές κάνανε κύκλους, και πιθανόν επισκέφτονταν κάποιες στάσεις πάνω από μια φορά. Για να μην υπάρχουν περιπλοκές, στο αρχείο γράφτηκαν οι επισκέψεις από μια φορά για κάθε στάση, και με όσο το δυνατόν πιο αντικειμενική άποψη για την ακολουθία τους Views για φιλτράρισμα Ως εδώ έχουμε δει όλες τις οντότητες πάνω στις οποίες θα εφαρμόσουμε τα διάφορα φίλτρα. Ως τελική έξοδος είναι οι στάσεις του λεωφορείου πάνω στον χάρτη της εφαρμογής, οπότε υπάρχει μια τάση να δίνεται μεγαλύτερη σημασία εκεί. BusLinePicker Η προεπιλογή με το που δίνουμε εντολή να εμφανιστεί το view του FilterAndSearch, είναι αυτή του φιλτραρίσματος των στάσεων με βάση την γραμμή του λεωφορείου που τις εξυπηρετεί. Το view controller για αυτή την περίπτωση, είναι αυτό του BusLinePicker. 58

64 Ουσιαστικά πρόκειται απλά για ένα UIPickerView, ένα από τα view που προσφέρει το UIKit, που δέχεται μια σειρά τιμών τις οποίες απεικονίζει σε ένα πίνακα, ενός ή περισσοτέρων στηλών, ο οποίος μπορεί να κυλίσει προς τα πάνω ή προς τα κάτω. Η λειτουργικότητα για την κύλιση είναι ήδη έτοιμη και ενσωματομένη στο view που χρησιμοποιούμε, και αφήνεται σε εμάς να ορίσουμε την πηγή από την οποία θα αντλεί τα δεδομένα του, και την αντιμετώπιση της επιλογής μιας εκ των γραμμών του πίνακα. Ο λόγος που δεν ενσωματώνουμε τον κώδικα στο FilterAndSearch, αφού πρόκειται για ένα UIPickerView, στο οποίο δεν κάνουμε καμία απολύτως αλλαγή, είναι για να μην περιπλέκουμε τον ήδη υπάρχων κώδικα, να είναι πιο ξεκάθαρο ποιος είναι υπεύθυνος για τί, και επίσης επειδή θα δημιουργούσαμε νέο view για τις υπόλοιπες δύο επιλογές, οπότε δεν υπάρχει νόημα στο να γίνει τέτοια διάκριση. Συνεπώς το BusLinePicker, εφαρμόζει τα κατάλληλα πρωτόκολλα για να δίνει δεδομένα στο BusLinePicker : UIViewController <UIPickerViewDataSource,UIPickerViewDelegate> Με το που φορτωθεί το view στη μνήμη, πρέπει να ορίσουμε τον πίνακα (NSArray) από τον οποίο το UIPickerView θα αντλεί τα δεδομένα του. Κάνουμε λοιπόν μια απλή ερώτηση στην βάση δεδομένων για να μας δώσει όλα τα στοιχεία για τις γραμμές λεωφορείων. NSArray *rawbuses = [mycdhandler getbuslinesforpredicate:nil]; Όπου η μέθοδος (void)getbuslinesforpredicate:(nspredicate *)predicate είναι μια μέθοδος που επιστρέφει αντικείμενα BusLine, φιλτραρισμένα με τους κατάλληλους περιορισμούς. Στην δικιά μας περίπτωση θέλουμε όλες τις γραμμές, οπότε περνάμε το nil σαν παράμετρο. Στην υλοποίηση του CoreDataHandler, έχουμε γράψει τις ακόλουθες παρόμοιες μεθόδους: - (NSArray *)getbusstopsforpredicate:(nspredicate *)predic; - (NSArray *)getbuslinesforpredicate:(nspredicate *)predic; - (NSArray *)getbusstops; - (NSArray *)findbusstopsbyname:(nspredicate *)name; Οι οποίες εκφυλίζονται σε κλήσεις της μεθόδου: - (NSArray *)getdataforentityname:(nsstring *)dataname forpredicate:(nspredicate *)predicate onentityname:(nsstring *)filtername; 59

65 Όλες οι πρώτες μέθοδοι, επωνομαζόμενες και ως μέθοδοι διευκόλυνσης (convenience methods), έχουν σκοπό να είναι πιο φιλικές στο μάτι, και να μην καλείται απ ευθείας η τελευταία που είναι πιο πολύπλοκη. Στο σώμα της κάθε μεθόδου, έχουμε γράψει τον κατάλληλο κώδικα ώστε να περνιούνται οι κατάλληλες παράμετροι και να γίνεται κληση της τελευταίας. Έτσι πετυχαίνουμε και να είναι πιο ευανάγνωστος ο κώδικας, και να μην έχουμε επαναλαμβανόμενο κώδικα. Το σώμα της συνάρτησης που χρησιμοποιούμε: -(NSArray *)getbuslinesforpredicate:(nspredicate *)predicate return [self forpredicate:predicate Υποδεικνύει ότι θα λάβουμε δεδομένα τύπου BusLine, εφαρμόζοντας πρώτα τον περιορισμό predicate στην οντότητα BusLine πάλι. Ως απάντηση λοιπόν, θα λάβουμε ένα πίνακα με αντικείμενα BusLine. Οι βάσεις δεδομένων δεν έχουν κάποια ταξινόμηση στα δεδομένα τους, ούτε κατά αλφαβητική σειρά, ούτε κατα σειρά καταχώρησης (μια από τις βασικές αρχές των σχεσιακών βάσεων δεδομένων). Για τις γραμμές που πήραμε, μας ενδιαφέρει ο αριθμός και το όνομα της κάθε μίας. Προσθέτουμε αυτά τα στοιχεία σε ένα NSString το οποίο αποθηκεύουμε σε ένα πίνακα. Το μόνο που μένει να κάνουμε, πριν τροφοδοτήσουμε τον πίνακα στο UIPickerView, είναι να τα ταξινομήσουμε, καθώς είναι άκρως αντιαισθητικό τα δεδομένα να εμφανίζονται χύμα. Είναι από τις απαιτήσεις του χρήστη να μπορεί να βρει εύκολα αυτό που ψάχνει, και τι ποιο εύκολο από το να ψάχνει σε μια ταξινομημένη λίστα. Αλφαβητική ταξινόμηση δεν μας βολεύει, καθώς η γραμμή 6.10 Πανεπιστήμιο θα πήγαινε πριν την 6.2 και όχι μετά. Οπότε αναγκαζόμαστε να καταφύγουμε σε μια πιο πολύπλοκη σχέση ταξινόμησης, που δεν μας καθυστερεί όμως, αφού ο αριθμός των γραμμών είναι στις μερικές δεκάδες. Συνεπώς στο τέλος της μεθόδου (void)viewdidload, έχουμε ένα ταξινομημένο πίνακα με τα δεδομένα. Αυτόν τον πίνακα τον κρατάμε σε μια μεταβλητή στιγμιοτύπου (NSMutableArray *lines), και τον απελευθερώνουμε μόνο όταν το view πρόκειται να εξαφανιστεί και να φύγει από τη μνήμη. Στα διάφορα στοιχεία του UIPickerView απαντάμε με βάση τα στοιχεία του πίνακα, για παράδειγμα, για τον αριθμό γραμμών δίνουμε τον αριθμό των στοιχείων του 60

66 πίνακα lines, για τον τίτλο μιας γραμμής δίνουμε την αντίστοιχη καταχώρηση στον πίνακα lines. Τέλος όταν ο χρήστης επιλέξει κάποια από τις τιμές, πρέπει να ενημερώσουμε τον αρμόδιο (delegate) για την επιλογή του. Όπως έχουμε ήδη αναφέρει, έχουμε ορίσει το αντίστοιχο πρωτόκολλο το οποίο εφαρμόζει το FilterAndSearch. - (void)pickerview:(uipickerview *)thepickerview didselectrow:(nsinteger)row incomponent:(nsinteger)component [delegate setselectedbus:[lines objectatindex:row]]; Έτσι ο delegate αποθηκεύει την τιμή που επιλέξαμε. Αν σε αυτό το σημείο πατήσουμε το κουμπί Apply που υπάρχει από προηγουμένως, από το view του FilterAndSearch, θα φιλτράρουμε και με βάση αυτή την τιμή. Το FilterAndSearch θα ελέγξει την τιμή του κουμπιού UISegmentedControl, θα δει ότι βρισκόμαστε στην πρώτη περίπτωση, και θα στείλει το εξής μήνυμα στον δικό του delegate, που δεν είναι κανείς άλλος από τον PatrasRoutesViewController: [delegate applyfilteringforselection: 0 forstring:[str substringtoindex:i]]; Όπου η τελευταία παράμετρος είναι το NSString που επιστρέψαμε, ελαφρώς αλλαγμένο για τις ανάγκες του κώδικα. Πίσω στο PatrasRoutesViewController, θα εκτελεστεί ο ακόλουθος κώδικας: -(void)applyfilteringforselection:(int)i forstring:(nsstring *)sel [sel retain]; [self changepredicateto:sel forindex:i]; [self dismissmodalviewcontrolleranimated:yes]; preference = 0; [self addannotations]; [sel release]; Ο οποίος ακολουθεί τα εξής βήματα. 1. Πρώτον κάνουμε retain το string, καθώς όταν αποσύρουμε τον modal view controller, θα αποσύρουμε και όλα τα στοιχεία που ελέγχει και μαζί με αυτά και την επιλογή μας. 2. Δίνουμε την εντολή να δημιουργηθεί ο κατάλληλος predicate σύμφωνα με την επιλογή μας. 3. Αποσύρουμε το FilterAndSearch. 4. Ξεκινάμε την διαδικασία εμφάνισης των επισημειώσεων. 61

67 5. Κάνουμε release το string, αφού δεν το χρειαζόμαστε πια, και πρέπει να ισορροπήσουμε το retain count. Το predicate που θα δημιουργηθεί είναι το εξής: [self setpredicate:[nspredicate like Δηλαδή φιλτράρουμε την επιλογή μας με βάση τον αριθμό της γραμμής. Η διαδικασία εμφάνισης των επισημειώσεων θα αναλυθεί στο τέλος του παρόντος κεφαλαίου. Τέλος πρέπει να λάβουμε υπόψιν την εξής περίπτωση: Με το που εμφανίζεται το UIPickerView, είναι ήδη επιλεγμένη η πρώτη εγγραφή. Αν ο χρήστης θέλει όντως αυτή την εγγραφή, είναι περιττό να πρέπει να αλλάξει τιμή και να την επαναφέρει στην αρχή. Όμως αν δεν το κάνει, δεν θα κληθεί η μέθοδος (void)pickerview: didselectrow: incomponent:. Έτσι στην μέθοδο του delegate, ελέγχουμε αν έχει επιλεγεί κάποια εγγραφή: if ([selectedbus [buspicker selectrow:0 incomp:0 animated:no]; Και αν όχι, αυτό σημαίνει (κατά πάσα πιθανότητα) ότι ο χρήστης ήθελε να επιλέξει την πρώτη. Δίνουμε λοιπόν το κατάλληλο σήμα, που θα αναγκάσει την μέθοδο ενημέρωσης να κληθεί. DestinationPicker Η επόμενη επιλογή περιορισμού των στάσεων, γίνεται δίνοντας ένα όνομα, και εμφανίζοντας μόνο όσες στάσεις έχουν αυτό το όνομα. Υπάρχει ένα απλό UITextField στο οποίο δίνουμε το όνομα, και εξίσου απλά περιορίζουμε τις στάσεις δίνοντας αυτή την φορά ως predicate το εξής: [NSPredicate like[cd] Όπου το [cd] σημαίνει ότι δεν γίνεται διάκριση πεζών και κεφαλαίων, και δεν γίνεται διάκριση σε τονισμό. Αρκετά απλό στην υλοποίηση, έχει όμως το μειονέκτημα ότι ο χρήστης δεν μπορεί να ξέρει επακριβώς το όνομα της στάσης, καθώς επίσης δεν έχει νόημα να πρέπει 62

68 να πληκτρολογήσει ολόκληρο το όνομα, όταν από τα πρώτα κιόλας γράμματα είναι εμφανές ποια είναι η επιλογή. Χρησιμοποιούμε και ένα UITableView στο οποίο θα εμφανίζουμε τυχόν προτάσεις ολοκλήρωσης ή και διόρθωσης των όσων έχει γράψει ο χρήστης ως τώρα. Αρκετά παρόμοια με το UIPickerView, έχουμε δύο πρωτόκολλα που πρέπει να υλοποιήσουμε, το UITableViewDelegate και το UITableViewDataSource. Δημιουργούμε και πάλι έναν πίνακα (NSArray) από τον οποίο θα αντλούμε τα δεδομένα, και γράφουμε τον κώδικα για τις κατάλληλες αναθέσεις. Αυτό που μας έχει μείνει, είναι να εντοπίσουμε τις βέλτιστες προτάσεις για το κείμενο που πληκτρολογεί ο χρήστης. Εδώ χρησιμοποιείται ο αλγόριθμος Levenshtein. Όπως έχουμε ήδη αναφέρει, πρόκειται για έναν αλγόριθμο που μετράει την «απόσταση» όπως την ορίσαμε, ανάμεσα σε δύο λέξεις. Με βάση αυτή την απόσταση ταξινομούμε τον πίνακα με τις στάσεις που έχουμε λάβει, και εμφανίζουμε τις πρώτες 10. Ο κώδικας που έχουμε γράψει ενδέχεται να εμφανίζει και λιγότερες από δέκα εγγραφές, αφού αρκετές από τις στάσεις έχουν ίδιο όνομα (πχ. Κορίνθου), και αν και δεν τις εμφανίζουμε πολλαπλές φορές, προσμετρούνται στις δέκα. Ενδιαφέρον έχει η περίπτωση που η λέξη που έχουμε δώσει ταιριάζει με ένα μέρος ονόματος μιας στάσης, όταν όμως το όνομα της στάσης αποτελείται ουσιαστικά από πολλές λέξεις. Για παράδειγμα, δίνοντας την λέξη «Γεωργίου», αποκλείεται να βρούμε μια στάση στην πλατεία Γεωργίου, καθώς πρώτον, η απόσταση των δύο λέξεων είναι ήδη 7 (7 προσθήκες, όσα και τα γράμματα της λέξης πλατεία) και δεύτερον, το όνομα της στάσης είναι στην πραγματικότητα «Πλατεία Βασιλέως Γεωργίου του Πρώτου». Αν και σχετικά ξεκάθαρο ότι ψάχνουμε μια τέτοια στάση, ο αλγόριθμος αποκλείεται να την φέρει στις πρώτες δέκα. Κάνουμε λοιπόν κατά την ταξινόμηση μια ακόμα παραδοχή: Αν το όνομα που εξετάζουμε έχει πάνω από μια λέξεις, συγκρίνουμε την λέξη που έχουμε γράψει ως τώρα με κάθε μια από τις επιμέρους λέξεις, και ως απόσταση θεωρούμε την ελάχιστη από κάθε μία. Αυτό μας βοηθάει πάρα πολύ στο να δώσουμε και στα πολύ μακρυά ονόματα μια ευκαιρία να επιλαγούν. Ωστόσο αν μείνουμε εκεί, παραμελούμε τις στάσεις με μεσαία μήκη ονομάτων, καθώς είναι πολύ πιο εύκολο να ταιριάζουμε πάντα την λέξη με ένα μέρος από ένα μεγάλο όνομα. Για να το αντισταθμίσουμε αυτό, πολλαπλασιάζουμε την απόσταση για μερικό ταίριασμα με 1.5, ώστε να είναι κατά μια έννοια πιο ακριβό το ταίριασμα με μεγάλη λέξη, αλλά όχι απαγορευτικό. Έτσι στην περίπτωση που η λέξη ταιριάζει στο 100%, θα μας δώσει απόσταση μηδέν, που με ότι και να πολλαπλασιαστεί θα επιστρέψει μηδέν. 63

69 Έτσι έχουμε βελτιώσει κατά πολύ την συμπεριφορά της ταξινόμησης. Τέλος, σαν πρόσθετη παρατήρηση, στο UITableView, είναι αρκετά δύσκολο να επιλέξουμε την τελεύταια εγγραφή (από άποψη επιφάνειας επαφής). Για αυτό τον λόγο προσθέτουμε και μια κενή εγγραφή στο τέλος, ώστε να είναι δυνατόν να κυλίσουμε τον πίνακα προς τα κάτω και να έχουμε πρόσβαση και στην τελευταία εγγραφή. RouteSelector Τελευταία επιλογή είναι η δρομολόγηση από ένα σημείο του χάρτη σε ένα άλλο. Για την ίδια την αναζήτηση, ο αλγόριθμος που υλοποιήσαμε απαιτεί υποχρεωτικά δύο παραμέτρους, και προαιρετικά μπορεί να έχει άλλες δύο: Υποχρεωτικά: Προαιρετικά: - Γεωγραφικές Συντεταγμένες Αρχικού Σημείου - Γεωγραφικές Συντεταγμένες Τελικού Σημείου - Μέγιστη απόσταση γύρω από το επιλεγμένο σημείο που θα αναζητήσουμε στάσεις - Μέγιστος αριθμός αλλαγών λεωφορείων που επιτρέπονται Οι πρώτες τρεις παράμετροι λαμβάνουν τιμή από το view που εμφανίζουμε μέσω του FilterAndSearch (το RouteSelector), ενώ η τέταρτη, ως πιο γενική παράμετρος που αφορά τις προτιμήσεις του χρήστη, έχει συμπεριληφθεί στην αρχική σελίδα, στο view των προτιμήσεων. Για τον καθορισμό των συντεταγμένων, εμφανίζουμε ένα νέο MKMapView. Αυτό μπορεί να γίνει πατώντας οποιοδήποτε από τα δύο κουμπιά που προσφέρει το view. Το πρώτο θα μας επιτρέψει να καθορίσουμε τις συντεταγμένες του σημείου έναρξης, ενώ το δεύτερο αυτές του σημείου τερματισμού. Για να καθορίσουμε τις συντεταγμένες, αρκεί ο χρήστης να ακουμπήσει το δάχτυλο του μια φορά σε ένα σημείο του χάρτη. Σκοπός μας είναι να εντοπίσουμε το άγγιγμα, να πάρουμε τις συντεταγμένες του πάνω στο view που είμαστε, να εντοπίσουμε την σχετική θέση τους στο MKMapView, και να τις μετατρέψουμε σε γεωγραφικές συντεταγμένες. Για αυτή τη δουλειά, δημιουργούμε μια νέα κλάση, που κληρονομεί την UIGestureRecognizer, την ViewTouch. Σκοπός της είναι να εντοπίσει τα αγγίγματα 64

70 του χρήστη, και να μας ειδοποιήσει μόνο για εκείνα που μας ενδιαφέρουν, δηλαδή τα μονά αγγίγματα μέσα στο χάρτη (single tap). Επίσης για όλα τα υπόλοιπα αγγίγματα, δεν πρέπει να επηρεάσουμε ή να απαγορέψουμε την συμπεριφορά των αντικείμενων που ήδη υπάρχουν εκεί. Επίσης δεν θέλουμε να λάβουμε υπόψιν πατήματα που δεν ήταν στο ίδιο σημείο. Δηλαδή αν ολόκληρη η κίνηση του δαχτύλου του χρήστη ήταν να ακουμπήσει σε ένα σημείο, να τραβήξει το δάχτυλό του και να το αφήσει μετά, αυτό πρέπει να απορριφθεί. Ενημερώνουμε λοιπόν το RouteSelector μόνο όταν το tapcount είναι 1, και όταν το σημείο που έγινε η αρχική επαφή είναι το ίδιο με το τελικό. Στον δεύτερο περιορισμό έχουμε το πρόβλημα ότι το δάκτυλο ενός χρήστη δεν είναι το ίδιο σταθερό με το κλικ ενός ποντικιού στον υπολογιστή. Για αυτό τον λόγο θέλουμε να χαλαρώσουμε λίγο αυτή την απαίτηση. Ορίζουμε την μέθοδο: - (BOOL)point:(CGPoint)a aboutequaltopoint:(cgpoint)b; Που επιστρέφει αληθές, αν το δεύτερο σημείο απέχει το πολύ 10 pixel από το αρχικό. Σε αυτό το σημείο επιστρέφουμε την τιμή στον delegate (RouteSelector) του ViewTouch, μέσω της μεθόδου που ορίσαμε στο αντίστοιχο πρωτόκολλο: - (void)touchatpoint:(cgpoint)p Από την y (κάθετη) συνιστώσα, αφαιρούμε 70 pixel, καθώς ο gesture recognizer ορίζεται για το view στο οποίο ανατίθεται, και όχι για το ολικό (έτσι τον έχουμε ορίσει εμείς δηλαδή). Έτσι υπάρχει μια σχετική κάθετη μετατόπιση 70 pixel που είναι ο χώρος που έχουμε αφήσει για το UISegmentedControl στο επάνω μέρος. Το mapview υποστηρίζει την μετατροπή από σημείο του view σε συντεταγμένες στο χάρτη, μέσω της μεθόδου: [mapviewc convertpoint:point tocoordinatefromview:mapviewc]; Συνεπώς σε αυτό το σημείο έχουμε τις γεωγραφικές συντεταγμένες και ορίζουμε ένα annotation το οποίο και προσθέτουμε στον χάρτη. Αυτό μπορεί να μετακινηθεί, αλλάζοντας τις συντεταγμένες του, και ενημερώνοντάς μας. Έχει προστεθεί και η δυνατότητα του reverse geocoding, της μετάφρασης δηλαδή των γεωγραφικών συντεταγμένων σε στοιχεία όπως: Οδός, Αριθμός, Πόλη, Ταχυδρομικός Κώδικας, Χώρα, και άλλα. Αυτά τα στοιχεία εμφανίζονται στον υπότιτλο της επισημείωσης, όταν φυσικά ολοκληρωθεί η κλήση σε όποια online βάση δεδομένων παρέχει αυτά τα δεδομένα για την Apple. Συνεπώς αν δεν υπάρχει σύνδεση στο διαδίκτυο, αυτή η δυνατότητα δεν ολοκληρώνεται. 65

71 Έχοντας ορίσει τις δύο βασικές παραμέτρους, μένει να ορίσουμε την τρίτη, που είναι και προαιρετική. Σαν ιδέα, βασίζεται στο ότι το σημείο που επιλέγει ο χρήστης δεν θα είναι κατά κανόνα στάση λεωφορείου. Δίνουμε λοιπόν ένα τετραγωνικό παράθυρο, μέσα στο οποίο επιλέγουμε τις στάσεις. Για αυτό το παράθυρο καθορίζουμε την ημιδιαγώνιο: Με τον αριθμό που ζητάμε, ουσιαστικά ζητάμε από τον χρήστη την μέγιστη απόσταση που είναι διατεθιμένος να περπάτησει, μέχρι να βρει στάση, και αντίστοιχα από την τερματική στάση μέχρι το σημείο που επέλεξε. Επειδή σαν υλοποίηση η ιδέα του κύκλου είναι λίγο πιο πολύπλοκη, αντί για ακτίνα a μέτρων από το κάθε σημείο, θεωρούμε τετράγωνο πλευράς 2a με κέντρο το αρχικό σημείο. Συνεπώς, η ημιδιαγώνιος θα έχει μήκος a. Σαν προκαθορισμένη τιμή είναι τα 200 μέτρα, συνεπώς περίπου 280 μέτρα ημιδιαγώνιος. Στην αναζήτηση, πρέπει να χρησιμοποιήσουμε το κατάλληλο predicate, ώστε να πάρουμε τις στάσεις που βρίσκονται μέσα σε αυτό το τετράγωνο. Αυτές είναι αυτές των οποίων οι συντεταγμένες (τετμημένη και τεταγμένη) έχουν τιμή μεγαλύτερη από αυτές του σημείου της κάτω αριστερά γωνίας, και μικρότερες από εκείνες του σημείου της πάνω δεξιά γωνίας. Υπολογίζουμε επαναληπτικά μέχρι να βρούμε τις συντεγμένες των σημείων αυτών, και τελικά προκύπτει ο περιορισμός: predicate = [NSPredicate predicatewithformat:@"latitude >= %f AND latitude <= %f AND longitude >= %f AND longitude <= %f",minlatstart,maxlatstart,minlongstart,maxlongstart]; Όποια τιμή δώσει ο χρήστης πολλαπλασιάζεται με ημιδιαγωνίου. και εφαρμόζεται ως τιμή της 66

72 Ο Αλγόριθμος Αναζήτησης Ως εδώ έχουμε πάρει από τον χρήστη τις απαραίτητες παραμέτρους της διαδρομής που καλούμαστε να εντοπίσουμε. Ακολούθως παρουσιάζεται ο αλγόριθμος αναζήτησης που υλοποιήθηκε για τις ανάγκες της εφαρμογής. Ο αλγόριθμος ψάχνει να εντοπίσει την τομή ανάμεσα σε δύο σύνολα, το σετ των λεωφορείων που εξυπηρετούν μια αρχική στάση και το σετ αυτών που εξυπηρετούν μια τερματική. Η υλοποίηση του ελέγχου είναι πολύ απλή, καθώς η βάση δεδομένων μας δίνει έτοιμα σε σετ τα λεωφορεία που εξυπηρετούν μια στάση, και η Objective C μας δίνει την δυνατότητα της εύρεσης της τομής των δύο αυτών συνόλων. Ο αλγόριθμος λοιπόν δέχεται σαν είσοδο δύο άλλα σετ, ένα σετ με όλες τις πιθανές στάσεις αρχής, όπως τις βρήκαμε με το predicate που αναφέραμε πιο πάνω, και ένα με τις τελικές που βρήκαμε με τον ίδιο τρόπο. Εξετάζουμε κάθε μια από τις στάσεις ξεχωριστά, και για κάθε πιθανό συνδυασμό αρχικής και τελικής στάσης. Με το που βρούμε μια οποιαδήποτε λύση, τερματίζουμε τον αλγόριθμο. Πρώτα εξετάζουμε κάθε αρχική στάση με την πρώτη από τις τελικές, και αν αποτύχουμε, αλλάζουμε τελική στάση και επαναλαμβάνουμε. Σαν επιπλέον περιορισμός, είναι όχι απλά να εξυπηρετούνται από το ίδιο λεωφορείο αυτές οι δύο στάσεις, αλλά προφανώς να εξυπηρετούνται και με την σωστή φορά. Συνεπώς, δεν θέλουμε να βρούμε μια διαδρομή που το λεωφορείο περνάει πρώτα από την τελική στάση και μετά από την αρχική. Αν αποτύχουν όλοι οι δυνατοί συνδυασμοί, αυτό σημαίνει ότι δεν υπάρχει γραμμή που να εξυπηρετεί αυτές τις δύο στάσεις απ ευθείας. Άρα πρέπει να ψάξουμε για ανταπόκριση μεταξύ δύο λεωφορείων. Ψάχνουμε μια ενδιάμεση στάση, στην οποία είναι δυνατόν να μεταβούμε από την αρχική, και από την οποία είναι δυνατόν να μεταβούμε στην τελική. Αυτό γίνεται για κάθε πιθανή ενδιάμεση στάση, για κάθε πιθανό λεωφορείο που ξεκινάει από κάθε πιθανή αρχική στάση. Αν και πάλι αποτύχουμε, το πρόβλημα διογκώνεται ακόμα περισσότερο, ψάχνοντας αυτή την φορά για δύο ενδιάμεσες στάσεις, με σκοπό να φτάσουμε στην τελική. Από πλευρά υλοποίησης, αυτός ο αλγόριθμος θα βρίσκει πάντα την βέλτιση λύση από άποψη αλλαγών γραμμών λεωφορείου. Η φιλοσοφία της υλοποίησης ταιριάζει αρκετά σε αλγόριθμο επαναληπτικής εμβάθυνσης (iterative deepening search), στον οποίον ορίζουμε ένα μέγιστο βάθος αναζήτησης, και αν αποτύχει η αναζήτηση, τότε αυξάνουμε το βάθος της. Είναι αρκετά σαφής και γρήγορος για μικρό βάθος αναζήτησης, ανεβαίνει όμως πάρα πολύ η πολυπλοκότητα του σε συνάρτηση με το βάθος αναζήτησης. Ο λόγος για αυτό είναι ο παράγοντας διακλάδωσης της 67

73 αναζήτησης, καθώς από κάθε στάση, μπορούμε να μεταβούμε σε έναν άλλο αριθμό στάσεων της τάξης του 50. Η αρχική υλοποίηση του αλγορίθμου μπορούσε να χειριστεί μόνο τις δύο αρχικές περιπτώσεις της άμεσης σύνδεσης δύο στάσεων, και της σύνδεσης με μία ενδιάμεση στάση. Για τις ανάγκες της γενικοποίησης του αλγορίθμου, εισάγαμε δύο νέες κλάσεις. Αυτό έγινε πρώτον για να ξεχωριστεί ο κώδικας ελέγχου της σύνδεσης των στάσεων από τους ελέγχους του αν έχουν εξεταστεί όλες οι στάσεις ή τα λεωφορεία, και δεύτερον γιατί η υλοποίηση των δύο αρχικών περιπτώσεων είχε γίνει με εντολές if/else, ενώ είναι ξεκάθαρο ότι έπρεπε να εφαρμοστεί επαναληπτική δομή. Το διάγραμμα ροής του αλγορίθμου είναι το εξής: 68

74 Όπως βλέπουμε, οι δυνατότητες τερματισμού του αλγορίθμου διαχωρίζονται σε επιτυχείς και μη επιτυχείς. Ο αλγόριθμος μπορεί να αποτύχει, αν δεν βρεθούν στάσεις κοντά στα σημεία που έδωσε ο χρήστης είτε ως αρχικά είτε ως τελικά, ή αν στην αναζήτηση φτάσουμε τον μέγιστο αριθμό αλλαγών γραμμών λεωφορείων και δεν έχουμε βρει ακόμα λύση. Σε αυτήν την περίπτωση επιστρέφουμε ένα nil πίνακα, τον οποίο διαχειρίζεται κατάλληλα το PatrasRoutesViewController. Διαφορετικά επιστρέφουμε ένα πίνακα με τα ακόλουθα στοιχεία: - Τον αριθμό των αλλαγών λεωφορείων που χρειάστηκαν - Την αρχική στάση για την οποία επιτεύχθηκε λύση - «Οποιοδήποτε» αριθμό ενδιάμεσων στάσεων (περιορισμένο φυσικά από την επιλογή του χρήστη) - Αντίστοιχο αριθμό των ενδιάμεσων λεωφορείων - Την τελική στάση - Το λεωφορείο για την μετάβαση στην τελική στάση Επειδή ελέγχουμε σειριακά τους δυνατούς συνδυασμούς, χρειαζόμαστε μια δομή στην οποία να μπορούμε εύκολα να βρίσκουμε τι έχουμε ήδη ελέγξει και τι όχι. Επειδή τα αντικείμενα σε ένα σετ (που είναι η δομή που χρησιμοποιεί το CoreData για τις σχέσεις) δεν έχουν κάποια σειρά, είναι πιο δύσκολο να έχουμε αυτό τον έλεγχο. Χρησιμοποιώντας όμως ένα πίνακα, και κρατώντας έναν ακέραιο ως δείκτη στην τελευταία θέση που ελέγξαμε πετυχαίνουμε ακριβώς αυτό. Ένα τέτοιον πίνακα χρειαζόμαστε για: - Την αρχική στάση που ελέγχουμε αυτή την στιγμή - Το λεωφορείο της αρχικής στάσης που ελέγχουμε - Την ενδιάμεση στάση του λεωφορείου Και ούτω καθεξής, μέχρι να φτάσουμε στην τελική στάση. Συνεπώς παρατηρούμε ότι θα χρειαστούμε αρκετούς τέτοιους πίνακες, με τον κάθε έναν από αυτούς να έχει ένα δικό του δείκτη, και να επιστρέφει το στοιχείο που αντιστοιχεί εκεί. Επίσης όλοι αυτοί οι δείκτες αυξάνονται, δείχνοντας στο επόμενο στοιχείο (είτε στάση είτε λεωφορείο), και πρέπει να ελέγχουμε για κάθε έναν από αυτούς το πότε έχει φτάσει στο τέλος του πίνακα. Για να συμπτίξουμε τον κώδικα, ολοκληρώνουμε όλη αυτήν τη συμπεριφορά σε μια νέα κλάση, την TablePair, που αντιπροσωπεύει αυτό ακριβώς το ζευγάρι πίνακαδείκτη. 69

75 Έχει επίσης ένα γνώρισμα που υποδεικνύει αν ο πίνακας περιέχει στάσεις ή λεωφορεία, και μια μέθοδο που κάνει την αύξηση του εσωτερικού αυτού δείκτη, και επιστρέφει μια δυαδική (boolean) τιμή που ενημερώνει αν έχουμε φτάσει στο τέρμα του πίνακα ή όχι. Αυτή η κλάση περιγράφει μόνο την δομή με την οποία αποθηκεύουμε τα δεδομένα, και μας διευκολύνει να τα ανακτήσουμε. Η επόμενη κλάση, PairOfPairs, μας δίνει την λειτουργικότητα που απαιτείται από τον αλγόριθμο, που είναι και αυτή που εμφανίζεται αφαιρετικά στον ψευδοκώδικα του διαγράμματος ροής. Συγκεκριμένα, έχει τις εξής αρμοδιότητες: - Να υπολογίσει και να επιλέξει την επόμενη στάση προς σύνδεση - Να επαναφέρει την αρχική κατάσταση όταν επιλέγουμε άλλη τελική στάση - Να δημιουργήσει κατάλληλη δομή, όταν από ένα αριθμό ενδιάμεσων στάσεων αποτυγχάνουμε και πρέπει να τον αυξήσουμε. Η κλάση αυτή περιέχει ένα πίνακα NSMutableArray, ο οποίος περιέχει με την σειρά, ένα TablePair για τις αρχικές στάσεις, ένα για τα λεωφορεία της στάσεις που ελέγχουμε, και τα λοιπά, μέχρι να φτάσουμε στην στάση που προσπαθούμε να διασυνδέσουμε με την τελική. Αυτό σημαίνει ότι ο πίνακας αυτός θα έχει πάντα περιττό αριθμό στοιχείων, με αρχικό στοιχείο ένα TablePair με τις αρχικές στάσεις, το οποίο δεν θα αλλάζει ποτέ, και υπόλοιπα στοιχεία εναλλάξ λεωφορεία και στάσεις. Παρακάτω αυτός ο πίνακας θα αναφέρεται με το όνομα array, για να διαχωρίζεται από τους πίνακες των TablePair. Έτσι, στον πρώτο «γύρο» αναζήτησης, ψάχνοντας απ ευθείας σύνδεση αρχικών στάσεων και τελικών, έχουμε την ακόλουθη δομή: Ενώ στον αμέσως επόμενο, η δομή θα διευρυνόταν ως εξής: 70

76 Συνεπώς, για να λάβουμε την στάση για την οποία θα κάνουμε τον έλεγχο, παίρνουμε το τελευταίο στοιχείο του array. Επειδή τα ενδιάμεσα στάδια δεν είναι εμφανή στον εξωτερικό αλγόριθμο, πρέπει να φροντίζουμε ώστε η στάση που επιστρέφουμε να είναι έγκυρη, με την έννοια ότι η στάση στο σημείο i του array είναι προσβάσιμη μέσω του λεωφορείου στο σημείο i-1 του array, από την στάση στο σημείο i-2 (συμπεριλαμβανόμενης και της φοράς του λεωφορείου). Αν αυτό γίνεται για κάθε στάση που υπολογίζουμε, η προκύπτουσα στάση θα είναι έγκυρη, ανεξαρτήτως του πόσες ενδιάμεσες ελέγξαμε μέχρι να την επιλέξουμε. Αν η στάση είναι έγκυρη, λαμβάνουμε μέσω του CoreData το σετ των λεωφορείων που την εξυπηρετούν, και αναζητούμε τομή με το αντίστοιχο σετ της τελικής στάσης. Αν βρούμε λύση, τερματίζουμε τον αλγόριθμο. Στην αντίθετη περίπτωση καλούμαστε να υπολογίσουμε την επόμενη στάση που θα εξετάσουμε. Για να γίνει αυτό, πρέπει να αυξήσουμε κατάλληλα τους εσωτερικούς δείκτες των TablePair. - Δίνουμε εντολή αύξησης στο τελευταίο στοιχείο TablePair του array. - Αν λάβουμε απάντηση ότι ο δείκτης έχει υπερβεί τον αριθμό των στοιχείων του πίνακά του, πρέπει να δώσουμε εντολή στο προηγούμενο στοιχείο του array. - Αυτό γίνεται επαναληπτικά, μέχρι να μην λάβουμε τέτοια απάντηση. Αν συμβεί για όλα τα στοιχεία του array, σημαίνει ότι για την επιλεγμένη τελική στάση έχουμε εξαντλήσει όλες τις εναλλακτικές. - Αρχικοποιούμε όλες τις μεταβλητές, και επαναλαμβάνουμε τον αλγόριθμο για μια νέα τελική στάση. - Αν τελειώσουν όλες οι τελικές στάσεις, σημαίνει πως για τον συγκεκριμένο αριθμό αλλαγών λεωφορείων, δεν υπάρχει σύνδεση. 71

77 - Αρχικοποιούμε όλες τις μεταβλητές (συμπεριλαμβανομένων αυτή την φορά και των τελικών στάσεων), και δοκιμάζουμε ξανά, για περισσότερες ενδιάμεσες στάσεις. Αυτό σημαίνει ότι αυτή τη φορά ο array θα έχει δύο περισσότερα στοιχεία. Οι τελικές στάσεις αυξάνονται χωριστά, διότι αποτελούν κάτι σαν σημείο αναφοράς, αφού μας δίνουν το δεύτερο σύνολο λεωφορείων με το οποίο κάνουμε την σύγκριση. Κάθε φορά που αυξάνουμε τον δείκτη σε ένα TablePair που δεν είναι το τελευταίο, σημαίνει ότι όλα τα επόμενα TablePair δεν έχουν ισχύ και πρέπει να ξαναυπολογιστούν. Ουσιαστικά όταν εξετάσουμε όλες τις στάσεις ενός λεωφορείου (στοιχείο του πίνακα array στη θέση i), θέλουμε να αλλάξουμε το λεωφορείο που εξετάζουμε (στοιχείο του πίνακα array στη θέση i-1). Ο πίνακας όμως του TablePair του στοιχείου i έχει υπολογιστεί ως οι στάσεις του λεωφορείου i-1. Εφόσον αυτό αλλάζει, αλλάζουν και οι στάσεις του. Στο τέλος του αλγορίθμου επιστρέφονται οι τιμές για την αρχική στάση, την τελική, τις ενδιάμεσες και τα αντίστοιχα λεωφορεία. Στην παρουσίασή τους όμως, πρέπει να γίνει μια κλήση ακόμα που θα επιστρέψει την διαδρομή που θα ακολουθηθεί, δηλαδή όλες τις στάσεις της πρώτης γραμμής από την αρχική στάση ως την πρώτη ενδιάμεση, από εκεί όλες τις στάσεις της επόμενης γραμμής μέχρι την επόμενη, και τα λοιπά μέχρι και την τελική. Για ολόκληρο τον αλγόριθμο, είναι πολύ σημαντική η μέθοδος: - (BOOL)stop:(BusStop *)startstop IsBefore:(BusStop *)endstop 72

78 forbus:(busline *)bus που εξετάζει δύο στάσεις και απαντάει αν η μια είναι επόμενη της άλλης για μια δεδομένη γραμμή λεωφορείου, αξιοποιώντας την αντίστοιχη εγγραφή στην οντότητα Route. Εμφάνιση Επισημειώσεων στο Χάρτη Για την εμφάνιση των στάσεων στο χάρτη χρησιμοποιούμε την κλάση MyAnnotation που έχουμε ήδη αναφέρει, και ο κώδικας που διακρίνει τις διάφορες περιπτώσεις ανήκει στην κλάση PatrasRoutesViewController. Όταν επιστρέφουμε από το FilterAndSearch modal view controller, έχουμε ένα predicate για τον περιορισμό των στάσεων. Παράλληλα με το predicate, θέτουμε και μια ακέραια τιμή κλάσης (static), που υποδεικνύει κυρίως με ποια από τις τρεις επιλογές πρέπει να γίνει το φιλτράρισμα. Αν δεν υπάρχει τέτοια τιμή, η προεπιλογή είναι να εμφανίσουμε όλες τις στάσεις. Σε οποιαδήποτε περίπτωση διαφορετική της δρομολόγησης από σημείο σε σημείο, ο πίνακας που επιστρέφεται είναι και ο τελικός πίνακας των στάσεων. Διαβάζουμε λοιπόν κάθε μια από τις στάσεις αυτές, και δημιουργούμε ένα αντίστοιχο annotation το οποίο και προσθέτουμε στο χάρτη. Στην περίπτωση της δρομολόγησης, ο πίνακας που δεχόμαστε δεν είναι πίνακας στάσεων, αλλά μονάχα των κομβικών σημείων. Για να είναι και πιο εμφανές στον χρήστη ότι υπάρχει κάποια αλλαγή στην διαδρομή, οι εικόνες των επισημειώσεων για τα κομβικά σημεία έχουν μωβ χρώμα, ενώ οι διαδρομές από το ένα στο άλλο έχουν ξεχωριστό χρώμα ανά γραμμή λεωφορείου. Για να γίνει αυτό, περνάμε διαφορετική παράμετρο για την εικόνα στο MyAnnotation που δημιουργούμε. Σημαντικό είναι να αλλάξουμε και τον identifier με βάση τον οποίο επαναχρησιμοποιούνται οι επισημειώσεις. Αν έχουμε κοινό για τις κομβικές στάσεις και τις υπόλοιπες, όταν το σύστημα ζητήσει ένα νέο annotation view, μπορεί να λάβει είτε εικόνα μωβ χρώματος είτε μπλε, καθώς το μόνο κριτήριο για να τις ξεχωρίσει είναι ο identifier Application Delegate Το application delegate (ή appdelegate) είναι η κλάση που λαμβάνει τα μηνύματα που στέλνει το σύστημα στην εφαρμογή, όπως προειδοποιήσεις μνήμης, αν η εφαρμογή πρόκειται να γίνει ενεργή ή ανενεργή, και άλλα. 73

79 Είναι επίσης η κλάση που είναι υπεύθυνη για να ορίσει το βασικό ελεγκτή view (root view controller), από το οποίο θα ξεκινάει το navigation stack, το modal stack και ούτω καθεξής. Ως την πρώτη κλάση που αντιδράει όταν ξεκινάει η εφαρμογή, οφείλουμε να κάνουμε τις κυριότερες ρυθμίσεις στον κώδικά της. Στην εφαρμογή μας, βασικότερο μέλλημα είναι να ξεκινήσει η εφαρμογή και να έχει στη διάθεση της την βάση δεδομένων γεμισμένη με όλα τα δεδομένα της εφαρμογής. Η αρχικοποίηση της βάσης είναι μια πολύ χρονοβόρα διαδικασία, ειδικά στο υλικό του iphone ή του ipod, καθώς η προσομοίωση στον υπολογιστή δεν είναι αντικειμενικό κριτήριο. Αν περάσουμε τα δεδομένα πριν εμφανίσουμε το κύριο view της εφαρμογής, η εφαρμογή θα φαίνεται να μην έχει ξεκινήσει, καθώς θα υπάρχει μόνο μια μαύρη οθόνη. Ομοίως αν ξεκινήσουμε την διαδικασία αφού αναθέσουμε τον root view controller. Σε οποιαδήποτε περίπτωση, ρισκάρουμε είτε να μην εμφανιστεί κάτι στην οθόνη της συσκευής, είτε ότι εμφανιστεί να μην ανταποκρίνεται μέχρι να τελειώσει η εκτέλεση του κώδικα που περνάει τα δεδομένα. Η λύση είναι να δημιουργήσουμε ένα ξεχωριστό νήμα για να τα περάσει, ενώ στο ενδιάμεσο διάστημα το κύριο νήμα είναι ελεύθερο να εμφανίσει το κατάλληλο view. Επειδή δεν θέλουμε να υπάρχει αλληλεπίδραση που μπορεί να ζητήσει δεδομένα από την βάση όταν αυτά ακόμα μεταφέρονται, εμφανίζουμε ένα προσωρινό view που ενημερώνει για την κατάσταση. Το πρόβλημα με το CoreData είναι ότι δεν είναι thread-safe, δηλαδή δεν προστατεύεται από συνθήκες συγχρονισμού. Αυτό έχει ως αποτέλεσμα η εφαρμογή να τερματίζει απροσδόκητα, αν προσπαθήσουμε απλά να εκτελέσουμε κώδικα πάνω στη βάση δεδομένων σε δευτερεύον νήμα. Προκύπτει η απαίτηση, που δίνει και την λύση σε αυτό το πρόβλημα, κάθε νέο νήμα έχει το δικό του managed object context, την οποία ικανοποιούμε δημιουργώντας ένα νέο στιγμιότυπο του CoreDataHandler, που όταν αρχικοποιείται δημιουργεί και καινούργιο managed object context. Μια παραπάνω λειτουργία που έχουμε αναθέσει στο appdelegate είναι αυτή της εναλλαγής των views από το μενού προς τα εμπρός ή και πίσω σε αυτό. Το πραγματοποιούμε αλλάζοντας απλά τον εκάστοτε root view controller με το νέο, προσθέτοντας και κάποια κίνηση, μέσω του μηχανισμού των animation. Μέχρι το ios 4.0, χρησιμοποιούταν κώδικας που δήλωνε αρχή και τέλος δηλώσεων για το animation, ανάμεσα στον οποίο δίνονταν οι ακριβείς αλλαγές που έπρεπε να συμβούν, και κάποιες παράμετροι για την μετάβαση από την αρχική στην τελική 74

80 κατάσταση. Με το ios 5.0, υπάρχουν αντίστοιχες convenience methods, που υλοποιούν τον ίδιο κώδικα, με μια μονάχα εντολή, δίνοντας βέβαια τα κατάλληλα ορίσματα. 75

81 ΚΕΦΑΛΑΙΟ 4 ΠΑΡΑΔΕΙΓΜΑΤΑ ΧΡΗΣΗΣ Σε αυτό το κεφάλαιο δίνεται μια τυπική περιήγηση στο περιβάλλον της εφαρμογής, αναλύοντας όλες τις δυνατότητες που μπορεί ο χρήστης να δει. 4.1 Παράδειγμα χρήσης της εφαρμογής Ανοίγοντας την εφαρμογή οδηγούμαστε στο πιο κομβικό της σημείο, που είναι το βασικού μενού. Από εκεί, όπως είναι εμφανές και από τα αντίστοιχα κουμπιά, μπορούμε να πλοηγηθούμε σε πολλά διαφορετικά σημεία της εφαρμογής, από τα οποία επιστρέφοντας βρισκόμαστε πάντα στο κύριο μενού αυτό. 76

82 Όπως έχουμε αναφέρει, στην πρώτη εκκίνηση της εφαρμογής γίνεται η μεταφόρτωση όλων των δεδομένων στη βάση ώστε να έχουμε πιο έξυπνη και γρήγορη διαχείρησή τους στο μέλλον. Για τα μερικά δευτερόλεπτα που διαρκεί η διαδικασία, ο χρήστης βλέπει το διπλανό view, με την ένδειξη στο κέντρο να περιστρέφεται, δίνοντας στο χρήστη την εικόνα ότι η εφαρμογή δεν είναι στάσιμη, αλλά λειτουργεί, και σύντομα θα είναι διαθέσιμη. Το παράθυρο των επιλογών. Επιλέγουμε τον τύπο για τον χάρτη που θέλουμε, κινόντας το slider αριστερά ή δεξιά. Η ετικέτα από πάνω μας ενημερώνει για την τρέχουσα κατάσταση. Δεν είναι απαραίτητο να πετύχουμε ακριβώς το κέντρο ή την άκρη για να αποθηκευτεί η αλλαγή, αρκεί απλά να φανεί στην αντίστοιχη ετικέτα. Ομοίως, για την παράμετρο του αλγορίθμου αναζήτησης. 77

83 Το view δρομολογίων. Επιλέγοντας την γραμμή που θέλουμε, εμφανίζεται στο πλαίσιο από κάτω το ωρολόγιο πρόγραμμά της, για καθημερινές μέρες, σαββατοκύριακα και αργίες. Το παράθυρο οδηγιών. Προσφέρει μια συνοπτική παρουσίαση της εφαρμογής, με υπερσυνδέσμους για εύκολη καθοδήγηση μέσα στο κείμενο, και τα αντίστοιχα κουμπιά για περιταίρω διευκόλυνση. 78

84 Κάθε ένα από τα παραπάνω views μπορεί να το επισκεφτεί κανείς από το αντίστοιχο κουμπί στο βασικό μενού, και επιστρέφει στο μενού με το κουμπί με την ένδειξη Back. Μια ακόμα δυνατότητα είναι αυτή του χάρτη του Πανεπιστημίου Πατρών, με επισημειώσεις για τις σχολές, τα κτήρια διοικητικών υπηρεσιών και άλλα, που προσφέρεται από τον ιστότοπο του Πανεπιστημίου Πατρών, Η εφαρμογή γυρίζει σε landscape σε αυτό το σημείο, για εμφανίζεται καλύτερα η σελίδα του Πανεπιστημίου. Επιλέγοντας από αριστερά το κουμπί ανάλυσης, μας δίνεται ένας λεπτομερής διαχωρισμός των χώρων του Πανεπιστημίου Πατρών, σε τμήματα σχολών και διοικητικά κτήρια, κτήρια δραστηριοτήτων, και άλλα. Τέλος επιλέγοντας το κουτάκι δίπλα στο επιθυμητό κτήριο, το επισημειώνει στο χάρτη, και δίνει κάποιες παραπάνω πληροφορίες. 79

85 Το κύριο κομμάτι της εφαρμογής, είναι αυτό του χάρτη της Πάτρας, που ακολουθεί: Ο χάρτης φορτώνεται στη μνήμη της συσκευής μέσω του διαδικτύου, και κρατιέται εκεί, ώστε να μην είναι απαραίτητη η σύνδεση κάθε φορά που εκκινεί η εφαρμογή. Ανάλογα με την επιλογή του χρήστη στις ρυθμίσεις, μπορεί να εμφανιστεί ως οδικός χάρτης, όπως φαίνεται αριστερά. Ή ως υβριδικός ή γεωγραφικός όπως φαίνεται εδώ. Πέρα από την εμφάνιση, προφανώς δεν αλλάζει τίποτα περισσότερο στις λειτουργίες της εφαρμογής. 80

86 Με το κουμπί με την ένδειξη Uni, μεταφέρεται ο χάρτης αυτόματα στον χώρο του Πανεπιστημίου Πατρών. Με το κουμπί με το σύμβολο της πινέζας, εμφανίζουμε στο πρώτο πάτημα, τις στάσεις των λεωφορείων. Εφόσον δεν έχουμε κάνει κάποια επιλογή για να τις περιορίσουμε, θα φαίνονται όλες οι υπάρχουσες. Επιλέγοντας μια τέτοια επισημείωση, μας εμφανίζει το όνομα της στάσης, καθώς και τον κωδικό της, και ένα κουμπί το οποίο μας προτρέπει να το πατήσουμε, αν θέλουμε παραπάνω πληροφορίες. 81

87 Πατώντας το κουμπί, εμφανίζεται ένα νέο view, που μας δίνει το όνομα και τον κωδικό της στάσης όπως πριν, και επιπροσθέτως: - Τις γεωγραφικές της συντεταγμένες - Μια λίστα με αριθμούς και ονόματα λεωφορείων που την εξυπηρετούν. Με το κουμπί της επιστροφής γυρνάμε πίσω στον βασικό χάρτη. Με δεύτερο πάτημα του κουμπιού της πινέζας, εμφανίζουμε όσες επισημειώσεις έχει προσθέσει ο χρήστης από μόνος του στον χάρτη. Η προσθήκη γίνεται με το κουμπί με το σύμβολο της πρόσθεσης. Επιλέγοντας μια από τις επισημειώσεις: Εμφανίζεται ο τίτλος και ο υπότιτλος που έχει δώσει ο χρήστης. Πατώντας το μπλε κουμπί που εμφανίζεται, εμφανίζεται ένα νέο view που μας επιτρέπει να αλλάξουμε τα στοιχεία αυτά. 82

88 Εδώ παρέχουμε τον νέο τίτλο και υπότιτλο της επισημείωσης, είτε όταν επεξεργαζόμαστε μια ήδη υπάρχουσα, είτε όταν μόλις έχουμε προσθέσει μια καινούργια. Με το κουμπί Remove, μπορούμε να αφαιρέσουμε ολοκληρωτικά από τον χάρτη την επιλεγμένη επισημείωση. Με το κουμπί της αναζήτησης, μπορούμε να περιορίσουμε τις στάσεις που εμφανίζονται με κάποιο κριτήριο. Ανάλογα με την επιλογή στην κορυφή του view, μπορούμε να φιλτράρουμε τις στάσεις σύμφωνα με την γραμμή στην οποία ανήκουν, το όνομα μιας στάσης, ή μιας διαδρομής. Η πρώτη περίπτωση φαίνεται αριστερά, και μπορούμε να επιλέξουμε μια οποιαδήποτε γραμμή από τις υπάρχουσες. Με το κουμπί Apply εφαρμόζεται η επιλογή, και μένουν μόνο οι στάσεις που ανήκουν στην γραμμή αυτή. 83

89 Με την δεύτερη επιλογή, καλούμαστε να δώσουμε το όνομα της στάσης που αναζητούμε. Δίνοντας τα πρώτα κιόλας γράμματα, από κάτω δίνονται πιθανές στάσεις που μπορεί να εννοούμε: Επιλέγοντας μια οποιαδήποτε από τις προτεινόμενες επιλογές, το όνομά της μεταφέρεται στο πεδίο που γράφαμε, και πατώντας το Apply, επιστρέφουμε στον κεντρικό χάρτη. 84

90 Η τρίτη περίπτωση, είναι αυτή της δρομολόγησης, όπου δεδομένων ενός αρχικού και ενός τελικού σημείου, η εφαρμογή καλείται να εντοπίσει την βελτιστη διαδρομή αξιοποιώντας τις συγκοινωνίες. Μπορούμε να δώσουμε και ως παράμετρο την μέγιστη απόσταση από τα σημεία αρχής και τέλους που θα εντοπιστούν οι στάσεις, μέσω του πρώτου πεδίου κειμένου. Αν δεν δώσουμε έγκυρο αριθμό, εμφανίζεται αντίστοιχο μήνυμα, και η τιμή ορίζεται στα 200 μέτρα. Για να δώσουμε τα σημεία αρχής και τέλους, εμφανίζουμε ένα νέο χάρτη που με το άγγιγμά μας πάνω στην οθόνη, αναγνωρίζεται και πέφτει μια πινέζα για κάθε περίπτωση. 85

91 Επιλέγοντας και τοποθετώντας κατάλληλα τις πινέζες, πρέπει επιστρέφοντας να βλέπουμε ότι οι δύο ετικέτες που γράφαν προηγουμένως «Συντεταγμένες» έχουν τώρα πάρει τιμή. Δίνοντας την εντολή Apply, εκτελείται αναζήτηση, που αν είναι επιτυχής μας εμφανίζει μια διαδρομή, αν είναι ανεπιτυχής, δεν εμφανίζει καμία στάση. Με διαφορετικά χρώματα είναι οι στάσεις της πρώτης γραμμής που πρέπει να πάρουμε (μπλε), της δεύτερης γραμμής (κόκκινο) και των κομβικών στάσεων όπως στάσεις επιβίβασης, αποβίβασης ή αλλαγής (μωβ). 86

92 ΚΕΦΑΛΑΙΟ 5 ΜΕΤΡΗΣΕΙΣ Στο κεφάλαιο αυτό, αναλύονται κάποιες μετρήσεις που αφορούν στην εφαρμογή και στην ανάπτυξή της. Ένα πολύ σημαντικό κομμάτι στην ανάπτυξη λογισμικού για υλικό όπως το iphone, είναι να γνωρίζει κανείς ότι δεν έχει τις ίδιες δυνατότητες με έναν σταθερό υπολογιστή. Έτσι δεν είναι δυνατόν ή επιτρεπτό να γίνεται κακή διαχείρηση της μνήμης, ή να απαιτείται πολύ υπολογιστική ισχύς για μια και μόνο διεργασία. Για το κομμάτι της διαχείρησης της μνήμης, το XCode προσφέρει αρκετά εργαλεία τόσο διόρθωσης, όσο και ελέγχου. Για παράδειγμα έχει ένα εργαλείο το οποίο υποδεικνύει πιθανά memory leaks, δεσμεύσεων δηλαδή χώρων μνήμης στους οποίους δεν μπορούμε να έχουμε πρόσβαση επειδή έχουμε χάσει τον δείκτη τους. Αυτό το εργαλείο είναι ο Analyzer, και εντοπίζει τέτοια σφάλματα, χωρίς να εκτελέσει τον κώδικα. Για παράδειγμα, ένα υπάρχον τέτοιο σφάλμα που εντοπίζει αυτό το εργαλείο, υπάρχει και στον κώδικα της εφαρμογής. Ωστόσο, επειδή υπολογίζει μόνο πιθανά leaks, πρέπει να είμαστε σίγουροι πριν κάνουμε κάτι release. Διαφορετικά, οδηγούμαστε στο αντίθετο πρόβλημα, το πρόβλημα των zombies, αποδέσμευσης δηλαδή χώρου μνήμης, που έχει ήδη αποδεσμευθεί. Υπάρχει και αντίστοιχο εργαλείο για τον εντοπισμό των zombies: 87

93 Τέλος, υπάρχει και δεύτερο εργαλείο για τον εντοπισμό των memory leaks, που το κάνει τρέχοντας τον κώδικα, και όχι απλά αναλυοντάς τον. Αυτό το εργαλείο είναι ο Profiler. Εκτός από τα leaks, δίνει και μια εικόνα των δεσμεύσεων μνήμης που κάνει το πρόγραμμα στην διάρκεια εκτέλεσής του. Εδώ βλέπουμε ότι η εφαρμογή μέσα σε 3 λεπτά λειτουργίας, που έχουμε περάσει από όλες τις δυνατές της λειτουργίες, κρατάει συνολικά 5.5 megabyte μνήμης ram. Αντίστοιχα, έχουμε καταφέρει να εκμηδενήσουμε τα memory leaks. 88

94 Το δεύτερο κομμάτι, της υπολογιστικής ισχύος, έχει να κάνει με την πολυπλοκότητα του κώδικα, και τον ρυθμό εκτέλεσης των διεργασιών. Για την παρούσα εφαρμογή, τρία ήταν τα πιο ακριβά υπολογιστικά σημεία: - Η μεταφορά των δεδομένων στην βάση δεδομένων - Η αναζήτηση λύσεων για την δρομολόγηση από σημείο σε σημείο - Η εύρεση και ταξινόμηση των στάσεων, στην αναζήτηση με όνομα στάσης Για το πρώτο πρόβλημα, αναθέτουμε την διεργασία σε δευτερεύον νήμα, ώστε να μπορέσει τουλάχιστον το πρωτεύον να εκτελέσει τις λειτουργίες εμφάνισης στην οθόνη. Η εκτέλεση όλου του κώδικα παίρνει μερικά δευτερόλεπτα, 4 με 5 στην εξομοίωση στον υπολογιστή, και περίπου 10 με 11 σε συσκεύη ipod. Δεν αποτελεί μεγάλο πρόβλημα, καθώς αυτός ο κώδικας θα εκτελείται μόνο μια φορά στην ζωή της εφαρμογής, στην πρώτη εκκίνησή της. Το δεύτερο πρόβλημα, η διαδικασία της δρομολόγησης δηλαδή, αποτελείται από έναν αλγόριθμο σχετικά κακής πολυπλοκότητας. Ένδεικτικά αναφέρουμε ότι: 1) Αν η βέλτιστη λύση βρίσκεται ακολουθώντας μόνο μια γραμμή λεωφορείου, κατά μέσο όρο γίνονται 2 με 4 συγκρίσεις μεταξύ συνόλων (set) λεωφορείων. 2) Αν πρέπει να γίνει μια εναλλαγή γραμμών, πρέπει να ακολουθήσουμε δηλαδή δύο γραμμές λεωφορείων, η λύση συναντάται σε περίπου 100 συγκρίσεις 3) Στην χείριστη περίπτωση που επιτρέπεται στην εφαρμογή, να γίνει δρομολόγηση με τρία λεωφορεία, ο αριθμός των συγκρίσεων είναι της τάξης των 15 με 20 χιλιάδων. Βέβαια δεν είναι πολύ αντιπροσωπευτικός αριθμός, καθώς στην συγκοινωνία της Πάτρας, δεν έχουμε βρει παράδειγμα διασύνδεσης δύο σημείων με τρία λεωφορεία, που να μην μπορεί να πραγματοποιηθεί με δύο. Έτσι αυτός ο αριθμός είναι εκείνος της χείριστης περίπτωσης που δεν υπάρχει λύση, και πρέπει να εξεταστούν όλες οι στάσεις σε όλες τις διακλαδώσεις πριν τερματίσει ο αλγόριθμος. Για την μέση χρήση της εφαρμογής, η επιλογή του 2 ως τον μέγιστο αριθμό αλλαγών λεωφορείων είναι αρκετή για οποιαδήποτε δρομολόγηση, και δεν φέρει καμία καθυστέρηση. 89

SOCIAL LOCATION BASED APP

SOCIAL LOCATION BASED APP 1 ΠΑΝΕΠΙΣΤΗΜΙΟ ΘΕΣΣΑΛΙΑΣ ΤΜΗΜΑ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ Η/Υ ΕΦΑΡΜΟΓΗ ios APPLICATION ios SOCIAL LOCATION BASED APP ΣΟΥΜΠΛΗΣ ΑΝΤΩΝΙΟΣ ΙΩΑΝΝΗΣ ΤΖΑΝΑΚΗΣ ΑΡΝΑΟΥΤΑΚΗΣ ΛΕΑΝΔΡΟΣ Επιβλέπων : Ακρίτας

Διαβάστε περισσότερα

Κατανεμημένα Συστήματα

Κατανεμημένα Συστήματα Κατανεμημένα Συστήματα Σημειώσεις εργαστηρίου Lab#7 - Διεργασίες, Nήματα, Πολυνημάτωση στη Python Νεβράντζας Βάιος-Γερμανός Λάρισα, Φεβρουάριος 2013 Lab#7 - Διεργασιές, Νη ματα, Πολυνημα τωση στη Python,

Διαβάστε περισσότερα

Η-Υ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ. Εργαστήριο 1 Εισαγωγή στη C. Σοφία Μπαλτζή s.mpaltzi@di.uoa.gr

Η-Υ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ. Εργαστήριο 1 Εισαγωγή στη C. Σοφία Μπαλτζή s.mpaltzi@di.uoa.gr Η-Υ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Εργαστήριο 1 Εισαγωγή στη C Σοφία Μπαλτζή s.mpaltzi@di.uoa.gr Διαδικαστικά Ιστοσελίδα μαθήματος: http://eclass.uoa.gr/courses/f30/ Υποχρεωτική παρακολούθηση: Παρασκευή 14:00 16:00 στην

Διαβάστε περισσότερα

Προγραμματισμός ΙI (Θ)

Προγραμματισμός ΙI (Θ) Τεχνολογικό Εκπαιδευτικό Ίδρυμα Κεντρικής Μακεδονίας - Σέρρες Τμήμα Μηχανικών Πληροφορικής Προγραμματισμός ΙI (Θ) Δρ. Δημήτρης Βαρσάμης Επίκουρος Καθηγητής Μάρτιος 2017 Δρ. Δημήτρης Βαρσάμης Μάρτιος 2017

Διαβάστε περισσότερα

lab13grades Άσκηση 2 -Σωστά απελευθερώνετε ολόκληρη τη λίστα και την κεφαλή

lab13grades Άσκηση 2 -Σωστά απελευθερώνετε ολόκληρη τη λίστα και την κεφαλή ΑΕΜ ΒΑΘΜΟΣ ΣΧΟΛΙΑ 00497 -Δεν ελέγχετε αν η createlist εκτελλέστικε σωστά και δεν τερµατίζετε το πρόγραµµα σε διαφορετική -Σωστά βρίσκετε το σηµείο στο οποίο πρέπει να προστεθεί ο κόµβος. -Σωστά τερµατίζετε

Διαβάστε περισσότερα

«ΕΙΔΙΚΑ ΘΕΜΑΣΑ ΣΟΝ ΠΡΟΓΡΑΜΜΑΣΙΜΟ ΤΠΟΛΟΓΙΣΩΝ» Κεφάλαιο 4: Αντικειμενοςτρεφήσ Προγραμματιςμόσ

«ΕΙΔΙΚΑ ΘΕΜΑΣΑ ΣΟΝ ΠΡΟΓΡΑΜΜΑΣΙΜΟ ΤΠΟΛΟΓΙΣΩΝ» Κεφάλαιο 4: Αντικειμενοςτρεφήσ Προγραμματιςμόσ «ΕΙΔΙΚΑ ΘΕΜΑΣΑ ΣΟΝ ΠΡΟΓΡΑΜΜΑΣΙΜΟ ΤΠΟΛΟΓΙΣΩΝ» Κεφάλαιο 4: Αντικειμενοςτρεφήσ Προγραμματιςμόσ 1 4.1. Οριςμόσ φνθετων τφπων κλάςεων 2 Με εξαίρεςη τουσ βαςικούσ τύπουσ τησ Java (int, float, boolean, κλπ) τα

Διαβάστε περισσότερα

Συνοπτικό εγχειρίδιο χρήσης του Microsoft Visual Studio 2010

Συνοπτικό εγχειρίδιο χρήσης του Microsoft Visual Studio 2010 Τμήμα Πληροφορικής & Επικοινωνιών Τομέας Υπολογιστικών Τεχνικών & Συστημάτων Συνοπτικό εγχειρίδιο χρήσης του Microsoft Visual Studio 2010 Ιωάννης Γεωργουδάκης - Πάρις Μαστοροκώστας Σεπτέμβριος 2011 ΠΕΡΙΕΧΟΜΕΝΑ

Διαβάστε περισσότερα

Προγραμματισμός Ι. Δυναμική Διαχείριση Μνήμης. Δημήτρης Μιχαήλ. Ακ. Έτος 2011-2012. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Προγραμματισμός Ι. Δυναμική Διαχείριση Μνήμης. Δημήτρης Μιχαήλ. Ακ. Έτος 2011-2012. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο Προγραμματισμός Ι Δυναμική Διαχείριση Μνήμης Δημήτρης Μιχαήλ Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο Ακ. Έτος 2011-2012 Ανάγκη για Δυναμική Μνήμη Στατική Μνήμη Μέχρι τώρα χρησιμοποιούσαμε

Διαβάστε περισσότερα

Προγραμματισμός Η/Υ 1 (Εργαστήριο)

Προγραμματισμός Η/Υ 1 (Εργαστήριο) Προγραμματισμός Η/Υ 1 (Εργαστήριο) Ενότητα 2: Δομή ενός προγράμματος C Καθηγήτρια Εφαρμογών: Τσαγκαλίδου Ροδή Τμήμα: Ηλεκτρολόγων Μηχανικών Τ.Ε. Άδειες Χρήσης Το παρόν εκπαιδευτικό υλικό υπόκειται σε άδειες

Διαβάστε περισσότερα

Προγραμματισμός 2 Σημειώσεις εργαστηρίου

Προγραμματισμός 2 Σημειώσεις εργαστηρίου Προγραμματισμός 2 Σημειώσεις εργαστηρίου 02-Java, Τύποι Δεδομένων ως Αντικείμενα Νεβράντζας Βάιος-Γερμανός Λάρισα, Μάρτιος 2013 02-iProgramminginJava, Τυ ποι δεδομε νων ως Αντικει μενα, σελίδα 1 Περίληψη

Διαβάστε περισσότερα

Field Service Management ΕΓΧΕΙΡΙΔΙΟ ΧΡΗΣΗΣ

Field Service Management ΕΓΧΕΙΡΙΔΙΟ ΧΡΗΣΗΣ Field Service Management ΕΓΧΕΙΡΙΔΙΟ ΧΡΗΣΗΣ 1 ΠΕΡΙΕΧΟΜΕΝΑ 1. ΑΝΑΛΥΣΗ ΜΕΝΟΥ ΕΦΑΡΜΟΓΗΣ... 4 2. ΕΠΕΞΗΓΗΣΗ ΚΕΝΤΡΙΚΟΥ ΜΕΝΟΥ ΚΑΡΤΕΛΑΣ... 5 3. ΔΗΜΙΟΥΡΓΙΑ ΠΕΛΑΤΗ... 6 4. ΑΝΑΖΗΤΗΣΗ ΠΕΛΑΤΗ... 6 5. ΕΠΕΞΕΡΓΑΣΙΑ/ΔΙΑΓΡΑΦΗ

Διαβάστε περισσότερα

2 Ορισμός Κλάσεων. Παράδειγμα: Μηχανή για Εισιτήρια. Δομή μιας Κλάσης. Ο Σκελετός της Κλάσης για τη Μηχανή. Ορισμός Πεδίων 4/3/2008

2 Ορισμός Κλάσεων. Παράδειγμα: Μηχανή για Εισιτήρια. Δομή μιας Κλάσης. Ο Σκελετός της Κλάσης για τη Μηχανή. Ορισμός Πεδίων 4/3/2008 Παράδειγμα: Μηχανή για Εισιτήρια 2 Ορισμός Κλάσεων Σύνταξη κλάσης: πεδία, κατασκευαστές, μέθοδοι Ένας αυτόματος εκδότης εισιτηρίων είναι μια μηχανή που δέχεται χρήματα και εκδίδει ένα εισιτήριο. Εκδίδει

Διαβάστε περισσότερα

ΕΡΓΑΣΤΗΡΙΟ 9: Συμβολοσειρές και Ορίσματα Γραμμής Εντολής

ΕΡΓΑΣΤΗΡΙΟ 9: Συμβολοσειρές και Ορίσματα Γραμμής Εντολής ΕΡΓΑΣΤΗΡΙΟ 9: Συμβολοσειρές και Ορίσματα Γραμμής Εντολής Στο εργαστήριο αυτό θα δούμε πώς ορίζονται και πώς χρησιμοποιούνται οι συμβολοσειρές στην C. Επίσης, θα μελετήσουμε κάποιες από τις συναρτήσεις

Διαβάστε περισσότερα

2.1 Αντικειµενοστρεφής προγραµµατισµός

2.1 Αντικειµενοστρεφής προγραµµατισµός 2.1 Αντικειµενοστρεφής προγραµµατισµός Στον αντικειµενοστρεφή προγραµµατισµό (object oriented programming, OOP) ένα πρόγραµµα υπολογιστή είναι ένα σύνολο αλληλεπιδρώντων αντικειµένων. Μπορεί να ειπωθεί

Διαβάστε περισσότερα

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων Δομημένος Προγραμματισμός Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων www.bpis.teicrete.gr Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων www.bpis.teicrete.gr 2 Ορισμός

Διαβάστε περισσότερα

FAIL PASS PASS οριακά

FAIL PASS PASS οριακά AEM 0001 0002 COMMENTS οριακά -Το πρόγραµµά σου δουλεύει λάθος για τις εισόδους: 7 -Δεν έχεις µεριµνήσει για την περίπτωση step=1. Μπορούσες να θεωρήσεις ειδική περίπτωση και να την υλοποιείς σε άλλον

Διαβάστε περισσότερα

ΕΡΓΑΣΤΗΡΙΟ 3: Προγραμματιστικά Περιβάλλοντα και το Πρώτο Πρόγραμμα C

ΕΡΓΑΣΤΗΡΙΟ 3: Προγραμματιστικά Περιβάλλοντα και το Πρώτο Πρόγραμμα C ΕΡΓΑΣΤΗΡΙΟ 3: Προγραμματιστικά Περιβάλλοντα και το Πρώτο Πρόγραμμα C Στο εργαστήριο αυτό, θα ασχοληθούμε με δύο προγραμματιστικά περιβάλλοντα για τη γλώσσα C: τον gcc μεταγλωττιστή της C σε περιβάλλον

Διαβάστε περισσότερα

3ο σετ σημειώσεων - Πίνακες, συμβολοσειρές, συναρτήσεις

3ο σετ σημειώσεων - Πίνακες, συμβολοσειρές, συναρτήσεις 3ο σετ σημειώσεων - Πίνακες, συμβολοσειρές, συναρτήσεις 5 Απριλίου 01 1 Πίνακες Είδαμε ότι δηλώνοντας μία μεταβλητή κάποιου συγκεκριμένου τύπου δεσμεύουμε μνήμη κατάλληλη για να αποθηκευτεί μία οντότητα

Διαβάστε περισσότερα

ΠΛΗΡΟΦΟΡΙΚΗ Ι JAVA Τμήμα θεωρίας με Α.Μ. σε 3, 7, 8 & 9 25/10/07

ΠΛΗΡΟΦΟΡΙΚΗ Ι JAVA Τμήμα θεωρίας με Α.Μ. σε 3, 7, 8 & 9 25/10/07 ΠΛΗΡΟΦΟΡΙΚΗ Ι JAVA Τμήμα θεωρίας με Α.Μ. σε 3, 7, 8 & 9 25/10/07 Αριθμητική στο δυαδικό σύστημα (γενικά) Συμπληρωματικά για δυαδικό σύστημα Η πρόσθεση στηρίζεται στους κανόνες: 0 + 0 = 0, 0 + 1 = 1, 1

Διαβάστε περισσότερα

Δυναμικές Ιστοσελίδες Εισαγωγή στην Javascript για προγραμματισμό στην πλευρά του client

Δυναμικές Ιστοσελίδες Εισαγωγή στην Javascript για προγραμματισμό στην πλευρά του client ΕΣΔ 516 Τεχνολογίες Διαδικτύου Δυναμικές Ιστοσελίδες Εισαγωγή στην Javascript για προγραμματισμό στην πλευρά του client Περιεχόμενα Περιεχόμενα Javascript και HTML Βασική σύνταξη Μεταβλητές Τελεστές Συναρτήσεις

Διαβάστε περισσότερα

ΚΕΦΑΛΑΙΟ 8 Η ΓΛΩΣΣΑ PASCAL

ΚΕΦΑΛΑΙΟ 8 Η ΓΛΩΣΣΑ PASCAL 8.1. Εισαγωγή ΚΕΦΑΛΑΙΟ 8 Η ΓΛΩΣΣΑ PACAL Πως προέκυψε η γλώσσα προγραμματισμού Pascal και ποια είναι τα γενικά της χαρακτηριστικά; Σχεδιάστηκε από τον Ελβετό επιστήμονα της Πληροφορικής Nicklaus Wirth to

Διαβάστε περισσότερα

Εισαγωγή στον Αντικειμενοστρεφή Προγραμματισμό Διάλεξη #2

Εισαγωγή στον Αντικειμενοστρεφή Προγραμματισμό Διάλεξη #2 Ανασκόπηση Μια εφαρμογή Java είναι ένα σύνολο από συνεργαζόμενες κλάσεις Διάλεξη #2: Αντικείμενα, Κλάσεις και Μέθοδοι Εισαγωγή στον Αντικειμενοστρεφή Προγραμματισμό,, Slide 1 Εισαγωγή στον Αντικειμενοστρεφή

Διαβάστε περισσότερα

Διάλεξη 2η: Αλγόριθμοι και Προγράμματα

Διάλεξη 2η: Αλγόριθμοι και Προγράμματα Διάλεξη 2η: Αλγόριθμοι και Προγράμματα Τμήμα Επιστήμης Υπολογιστών, Πανεπιστήμιο Κρήτης Εισαγωγή στην Επιστήμη Υπολογιστών Βασίζεται σε διαφάνειες του Κ Παναγιωτάκη Πρατικάκης (CSD) Αλγόριθμοι και Προγράμματα

Διαβάστε περισσότερα

Αρχές Τεχνολογίας Λογισμικού Εργαστήριο

Αρχές Τεχνολογίας Λογισμικού Εργαστήριο Αρχές Τεχνολογίας Λογισμικού Εργαστήριο Κωδικός Μαθήματος: TP323 Ώρες Εργαστηρίου: 2/εβδομάδα (Διαφάνειες Νίκου Βιδάκη) 1 JAVA Inheritance Εβδομάδα Νο. 3 2 Προηγούμενο μάθημα (1/2) Τι είναι αντικείμενο?

Διαβάστε περισσότερα

Εαρινό. Ύλη εργαστηρίου, Ασκήσεις Java

Εαρινό. Ύλη εργαστηρίου, Ασκήσεις Java Εξάμηνο Μάθημα Τίτλος 2017 2018 Εαρινό Αντικειμενοστραφής Προγραμματισμός Ι Ύλη εργαστηρίου, Ασκήσεις Java Ημερομηνία Εργαστήριο 5 ο Α. Ύλη εργαστηρίου 5.1 Έννοιες αντικειμενοστραφούς προγραμματισμού,

Διαβάστε περισσότερα

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ Ακαδημαϊκό έτος 2001-2002 ΤΕΤΡΑΔΙΟ ΕΡΓΑΣΤΗΡΙΟΥ #4

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ Ακαδημαϊκό έτος 2001-2002 ΤΕΤΡΑΔΙΟ ΕΡΓΑΣΤΗΡΙΟΥ #4 ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ Ακαδημαϊκό έτος 2001-2002 ΤΕΤΡΑΔΙΟ ΕΡΓΑΣΤΗΡΙΟΥ #4 «Προγραμματισμός Η/Υ» - Τετράδιο Εργαστηρίου #4 2 Γενικά Στο Τετράδιο #4 του Εργαστηρίου θα αναφερθούμε σε θέματα διαχείρισης πινάκων

Διαβάστε περισσότερα

Κλήση Συναρτήσεων ΚΛΗΣΗ ΣΥΝΑΡΤΗΣΕΩΝ. Γεώργιος Παπαϊωάννου ( )

Κλήση Συναρτήσεων ΚΛΗΣΗ ΣΥΝΑΡΤΗΣΕΩΝ. Γεώργιος Παπαϊωάννου ( ) ΚΛΗΣΗ ΣΥΝΑΡΤΗΣΕΩΝ Γεώργιος Παπαϊωάννου (2013-16) gepap@aueb.gr Περιγραφή: Μορφές μεταβίβασης ορισμάτων σε συναρτήσεις (και μεθόδους) και οι επιπτώσεις τους Επιστροφή τιμών από κλήση συναρτήσεων Υπερφόρτωση

Διαβάστε περισσότερα

ΕΡΓΑΣΤΗΡΙΟ 3: Προγραμματιστικά Περιβάλλοντα και το Πρώτο Πρόγραμμα C

ΕΡΓΑΣΤΗΡΙΟ 3: Προγραμματιστικά Περιβάλλοντα και το Πρώτο Πρόγραμμα C ΕΡΓΑΣΤΗΡΙΟ 3: Προγραμματιστικά Περιβάλλοντα και το Πρώτο Πρόγραμμα C Στο εργαστήριο αυτό, θα ασχοληθούμε με δύο προγραμματιστικά περιβάλλοντα της γλώσσας C, το Dev-C++, το οποίο είναι εφαρμογή που τρέχει

Διαβάστε περισσότερα

Εισαγωγή σε αντικειμενοστραφή concepts. Και λίγη C#

Εισαγωγή σε αντικειμενοστραφή concepts. Και λίγη C# Εισαγωγή σε αντικειμενοστραφή concepts Και λίγη C# Κλάσεις Κλάση: τύπος δεδομένων που αποτελεί συλλογή πεδίων, ορισμών συναρτήσεων/μεθόδων και ορισμών άλλων τύπων δεδομένων. Αντίστοιχο σκεπτικό με struct

Διαβάστε περισσότερα

ΚΕΦΑΛΑΙΟ 5. Κύκλος Ζωής Εφαρμογών ΕΝΟΤΗΤΑ 2. Εφαρμογές Πληροφορικής. Διδακτικές ενότητες 5.1 Πρόβλημα και υπολογιστής 5.2 Ανάπτυξη εφαρμογών

ΚΕΦΑΛΑΙΟ 5. Κύκλος Ζωής Εφαρμογών ΕΝΟΤΗΤΑ 2. Εφαρμογές Πληροφορικής. Διδακτικές ενότητες 5.1 Πρόβλημα και υπολογιστής 5.2 Ανάπτυξη εφαρμογών 44 Διδακτικές ενότητες 5.1 Πρόβλημα και υπολογιστής 5.2 Ανάπτυξη εφαρμογών Διδακτικοί στόχοι Σκοπός του κεφαλαίου είναι οι μαθητές να κατανοήσουν τα βήματα που ακολουθούνται κατά την ανάπτυξη μιας εφαρμογής.

Διαβάστε περισσότερα

Προγραμματισμός Ι (HY120)

Προγραμματισμός Ι (HY120) Προγραμματισμός Ι (HY20) # μνήμη & μεταβλητές πρόγραμμα & εκτέλεση Ψηφιακά δεδομένα, μνήμη, μεταβλητές 2 Δυαδικός κόσμος Οι υπολογιστές είναι δυαδικές μηχανές Όλη η πληροφορία (δεδομένα και κώδικας) κωδικοποιείται

Διαβάστε περισσότερα

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Αναφορές

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Αναφορές ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ Κλάσεις και Αντικείμενα Αναφορές Μαθήματα από το lab Υπενθύμιση: Η άσκηση ζητούσε να υλοποιήσετε μία κλάση vector που να διαχειρίζεται διανύσματα οποιουδήποτε

Διαβάστε περισσότερα

Διαδικασιακός Προγραμματισμός

Διαδικασιακός Προγραμματισμός Τμήμα ΜΗΧΑΝΙΚΩΝ ΠΛΗΡΟΦΟΡΙΚΗΣ ΤΕ ΤΕΙ ΔΥΤΙΚΗΣ ΕΛΛΑΔΑΣ Διαδικασιακός Προγραμματισμός Διάλεξη 7 η Πίνακες Οι διαλέξεις βασίζονται στο βιβλίο των Τσελίκη και Τσελίκα C: Από τη Θεωρία στην Εφαρμογή Σωτήρης Χριστοδούλου

Διαβάστε περισσότερα

Ειδικά θέματα Αλγορίθμων και Δομών Δεδομένων (ΠΛΕ073) Απαντήσεις 1 ου Σετ Ασκήσεων

Ειδικά θέματα Αλγορίθμων και Δομών Δεδομένων (ΠΛΕ073) Απαντήσεις 1 ου Σετ Ασκήσεων Ειδικά θέματα Αλγορίθμων και Δομών Δεδομένων (ΠΛΕ073) Απαντήσεις 1 ου Σετ Ασκήσεων Άσκηση 1 α) Η δομή σταθμισμένης ένωσης με συμπίεση διαδρομής μπορεί να τροποποιηθεί πολύ εύκολα ώστε να υποστηρίζει τις

Διαβάστε περισσότερα

Περιεχόμενα. Κεφάλαιο 1 Εισαγωγή στην Access Κεφάλαιο 2 Χειρισμός πινάκων... 27

Περιεχόμενα. Κεφάλαιο 1 Εισαγωγή στην Access Κεφάλαιο 2 Χειρισμός πινάκων... 27 Περιεχόμενα Κεφάλαιο 1 Εισαγωγή στην Access... 9 Γνωριμία με την Access... 12 Δημιουργία βάσης δεδομένων... 22 Άνοιγμα και κλείσιμο βάσης δεδομένων... 24 Ερωτήσεις ανακεφαλαίωσης... 25 Πρακτική εξάσκηση...

Διαβάστε περισσότερα

Πατώντας την επιλογή αυτή, ανοίγει ένα παράθυρο που έχει την ίδια μορφή με αυτό που εμφανίζεται όταν δημιουργούμε μία μεταβλητή.

Πατώντας την επιλογή αυτή, ανοίγει ένα παράθυρο που έχει την ίδια μορφή με αυτό που εμφανίζεται όταν δημιουργούμε μία μεταβλητή. Λίστες Τι είναι οι λίστες; Πολλές φορές στην καθημερινή μας ζωή, χωρίς να το συνειδητοποιούμε, χρησιμοποιούμε λίστες. Τέτοια παραδείγματα είναι η λίστα του super market η οποία είναι ένας κατάλογος αντικειμένων

Διαβάστε περισσότερα

Η γλώσσα προγραμματισμού C

Η γλώσσα προγραμματισμού C Η γλώσσα προγραμματισμού C Εισαγωγή στη C Λίγα λόγια για την C Γλώσσα προγραμματισμού υψηλού επιπέδου. Σχεδιάστηκε και υλοποιήθηκε από τον Dennis Richie στις αρχές της δεκαετίας του 1970 (Bell Labs). Η

Διαβάστε περισσότερα

Sheet2. Σωστή, και µπράβο που µεριµνήσατε για λίστες διαφορετικών µεγεθών.

Sheet2. Σωστή, και µπράβο που µεριµνήσατε για λίστες διαφορετικών µεγεθών. Α.Μ. ΒΑΘΜΟΣ ΣΧΟΛΙΑ Δεν κάνει compile και το λάθος είναι σηµαντικό: Το head1 είναι δείκτης σε struct, εποµένως η προσπέλαση πεδίου γίνεται 321 FAIL µε head1->next και όχι head1.next. Επιπλέον, έχετε λάθος

Διαβάστε περισσότερα

Πως θα κατασκευάσω το πρώτο πρόγραμμα;

Πως θα κατασκευάσω το πρώτο πρόγραμμα; Εργαστήριο Δομημένος Προγραμματισμός (C#) Τμήμα Μηχανολογίας Νικόλαος Ζ. Ζάχαρης Καθηγητής Εφαρμογών Σκοπός Να γίνει εξοικείωση το μαθητών με τον ΗΥ και το λειτουργικό σύστημα. - Επίδειξη του My Computer

Διαβάστε περισσότερα

ΕΡΓΑΣΤΗΡΙΟ 9: Συμβολοσειρές και Ορίσματα Γραμμής Εντολής

ΕΡΓΑΣΤΗΡΙΟ 9: Συμβολοσειρές και Ορίσματα Γραμμής Εντολής ΕΡΓΑΣΤΗΡΙΟ 9: Συμβολοσειρές και Ορίσματα Γραμμής Εντολής Στο εργαστήριο αυτό θα δούμε πώς ορίζονται και πώς χρησιμοποιούνται οι συμβολοσειρές στην C. Επίσης, θα μελετήσουμε κάποιες από τις συναρτήσεις

Διαβάστε περισσότερα

Κεφάλαιο ΙV: Δείκτες και πίνακες. 4.1 Δείκτες.

Κεφάλαιο ΙV: Δείκτες και πίνακες. 4.1 Δείκτες. Κεφάλαιο ΙV: Δείκτες και πίνακες. 4.1 Δείκτες. Η C, όπως έχουμε αναφέρει, είναι μια γλώσσα προγραμματισμού υψηλού επιπέδου η οποία αναπτύχθηκε για πρώτη φορά το 1972 από τον Dennis Ritchie στα AT&T Bell

Διαβάστε περισσότερα

Διαδικτυακό Περιβάλλον Διαχείρισης Ασκήσεων Προγραμματισμού

Διαδικτυακό Περιβάλλον Διαχείρισης Ασκήσεων Προγραμματισμού ΠΑΝΕΠΙΣΤΗΜΙΟ ΜΑΚΕΔΟΝΙΑΣ ΔΙΑΤΜΗΜΑΤΙΚΟ ΜΕΤΑΠΤΥΧΙΑΚΟ ΠΡΟΓΡΑΜΜΑ ΣΤΑ ΠΛΗΡΟΦΟΡΙΑΚΑ ΣΥΣΤΗΜΑΤΑ Διπλωματική Εργασία με θέμα: Διαδικτυακό Περιβάλλον Διαχείρισης Ασκήσεων Προγραμματισμού Καραγιάννης Ιωάννης Α.Μ.

Διαβάστε περισσότερα

Δομημένος Προγραμματισμός (ΤΛ1006)

Δομημένος Προγραμματισμός (ΤΛ1006) Τεχνολογικό Εκπαιδευτικό Ίδρυμα Κρήτης Σχολή Εφαρμοσμένων Επιστημών Τμήμα Ηλεκτρονικών Μηχανικών Τομέας Αυτοματισμού και Πληροφορικής Δομημένος Προγραμματισμός (ΤΛ1006) Δρ. Μηχ. Νικόλαος Πετράκης, Καθηγητής

Διαβάστε περισσότερα

ΠΑΝΕΠΙΣΤΗΜΙΟ ΘΕΣΣΑΛΙΑΣ ΣΧΟΛΗ ΘΕΤΙΚΩΝ ΕΠΙΣΤΗΜΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ

ΠΑΝΕΠΙΣΤΗΜΙΟ ΘΕΣΣΑΛΙΑΣ ΣΧΟΛΗ ΘΕΤΙΚΩΝ ΕΠΙΣΤΗΜΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ ΠΑΝΕΠΙΣΤΗΜΙΟ ΘΕΣΣΑΛΙΑΣ ΣΧΟΛΗ ΘΕΤΙΚΩΝ ΕΠΙΣΤΗΜΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ ΑΝΑΠΤΥΞΗ ΚΑΙ ΣΧΕΔΙΑΣΗ ΛΟΓΙΣΜΙΚΟΥ Η γλώσσα προγραμματισμού C ΕΡΓΑΣΤΗΡΙΟ 2: Εκφράσεις, πίνακες και βρόχοι 14 Απριλίου 2016 Το σημερινό εργαστήριο

Διαβάστε περισσότερα

ΠΛΗΡΟΦΟΡΙΚΗ ΙΙ (JAVA) 11/3/2008

ΠΛΗΡΟΦΟΡΙΚΗ ΙΙ (JAVA) 11/3/2008 ΠΛΗΡΟΦΟΡΙΚΗ ΙΙ (JAVA) 11/3/2008 Κατασκευαστές (Constructors) Ειδικός τύπος μεθόδων, οι οποίες: - είναι public και έχουν το ίδιο όνομα με αυτό της κλάσης - χρησιμοποιούνται για να αρχικοποιήσουν κάποιες

Διαβάστε περισσότερα

Εισαγωγή στον Προγραμματισμό

Εισαγωγή στον Προγραμματισμό Εισαγωγή στον Προγραμματισμό Πίνακες Δημήτρης Μιχαήλ Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο Ακ. Έτος 2012-2013 Πίνακες Πολλές φορές θέλουμε να κρατήσουμε στην μνήμη πολλά αντικείμενα

Διαβάστε περισσότερα

(Διαφάνειες Νίκου Βιδάκη)

(Διαφάνειες Νίκου Βιδάκη) (Διαφάνειες Νίκου Βιδάκη) JAVA Inheritance Εβδομάδα Νο. 3 2 Προηγούμενο μάθημα (1/2) Τι είναι αντικείμενο? Ανάλυση αντικειμένων Πραγματικά αντικείμενα Καταστάσεις Συμπεριφορές Αντικείμενα στον προγραμματισμό

Διαβάστε περισσότερα

Εγχειρίδιο Φοιτητή. Course Management Platform. Εισαγωγή. for Universities Ομάδα Ασύγχρονης Τηλεκπαίδευσης Παν. Μακεδονίας Σεπτέμβριος 2004

Εγχειρίδιο Φοιτητή. Course Management Platform. Εισαγωγή. for Universities Ομάδα Ασύγχρονης Τηλεκπαίδευσης Παν. Μακεδονίας Σεπτέμβριος 2004 Εγχειρίδιο Φοιτητή Εισαγωγή Η ηλεκτρονική πλατφόρμα, αποτελεί ένα ολοκληρωμένο σύστημα Ασύγχρονης Τηλεκπαίδευσης. Στόχος της είναι η παροχή υποδομών εκπαίδευσης και κατάρτισης ανεξάρτητα από τους περιοριστικούς

Διαβάστε περισσότερα

PRISMA Win POS Sync Merge Replication

PRISMA Win POS Sync Merge Replication ΤΜΗΜΑ ΥΠΟΣΤΗΡΙΞΗΣ Οδηγός Ρυθμίσεων Συγχρονισμού PRISMA Win POS Sync Merge Replication Η διαδικασία του συγχρονισμού γίνεται από τον Η/Υ που έχει το Back Office. Βασική προϋπόθεση για να ενεργοποιηθεί ο

Διαβάστε περισσότερα

Unity Editor #02 Κεντρικό Μενού: File, Build Settings και Build & Run

Unity Editor #02 Κεντρικό Μενού: File, Build Settings και Build & Run Unity Editor #02 Κεντρικό Μενού: File, Build Settings και Build & Run Καλώς ήλθες. Στο προηγούμενο μάθημα είδαμε τις λειτουργίες του μενού File του Editor της Unity. Όπως είπαμε οι δύο επιλογές που διαφέρουν

Διαβάστε περισσότερα

Εισαγωγή στην Πληροφορική

Εισαγωγή στην Πληροφορική Ανοικτά Ακαδημαϊκά Μαθήματα στο ΤΕΙ Ιονίων Νήσων Εισαγωγή στην Πληροφορική Ενότητα 8: Λειτουργικά Συστήματα Το περιεχόμενο του μαθήματος διατίθεται με άδεια Creative Commons εκτός και αν αναφέρεται διαφορετικά

Διαβάστε περισσότερα

Λύβας Χρήστος Αρχική επιµέλεια Πιτροπάκης Νικόλαος και Υφαντόπουλος Νικόλαος

Λύβας Χρήστος Αρχική επιµέλεια Πιτροπάκης Νικόλαος και Υφαντόπουλος Νικόλαος ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ IΙ Λύβας Χρήστος chrislibas@ssl-unipi.gr Αρχική επιµέλεια Πιτροπάκης Νικόλαος και Υφαντόπουλος Νικόλαος >_ ΣΥΝΑΡΤΗΣΕΙΣ ΣΤΗ C (1/3) +- Στη C χρησιμοποιούμε συχνα τις συναρτήσεις (functions),

Διαβάστε περισσότερα

Διαδικασιακός Προγραμματισμός

Διαδικασιακός Προγραμματισμός Τμήμα ΜΗΧΑΝΙΚΩΝ ΠΛΗΡΟΦΟΡΙΚΗΣ ΤΕ ΤΕΙ ΔΥΤΙΚΗΣ ΕΛΛΑΔΑΣ Διαδικασιακός Προγραμματισμός Διάλεξη 2 η Τύποι Δεδομένων Δήλωση Μεταβλητών Έξοδος Δεδομένων Οι διαλέξεις βασίζονται στο βιβλίο των Τσελίκη και Τσελίκα

Διαβάστε περισσότερα

Βάσεις δεδομένων (Access)

Βάσεις δεδομένων (Access) Βάσεις δεδομένων (Access) Όταν εκκινούμε την Access εμφανίζεται το παρακάτω παράθυρο: Για να φτιάξουμε μια νέα ΒΔ κάνουμε κλικ στην επιλογή «Κενή βάση δεδομένων» στο Παράθυρο Εργασιών. Θα εμφανιστεί το

Διαβάστε περισσότερα

Τιμή Τιμή. σκορ. ζωές

Τιμή Τιμή. σκορ. ζωές Εισαγωγή στην έννοια των μεταβλητών Οι μεταβλητές Θα πρέπει να έχετε παρατηρήσει ότι έχουμε φτιάξει τόσα παιχνίδια μέχρι αυτό το σημείο και δεν έχουμε αναφερθεί πουθενά για το πως μπορούμε να δημιουργήσουμε

Διαβάστε περισσότερα

FORTRAN και Αντικειμενοστραφής Προγραμματισμός

FORTRAN και Αντικειμενοστραφής Προγραμματισμός FORTRAN και Αντικειμενοστραφής Προγραμματισμός Παραδόσεις Μαθήματος 2016 Δρ Γ Παπαλάμπρου Επίκουρος Καθηγητής ΕΜΠ georgepapalambrou@lmentuagr Εργαστήριο Ναυτικής Μηχανολογίας (Κτίριο Λ) Σχολή Ναυπηγών

Διαβάστε περισσότερα

Διαγράμματα Κλάσεων στη Σχεδίαση

Διαγράμματα Κλάσεων στη Σχεδίαση Διαγράμματα Κλάσεων στη Σχεδίαση περιεχόμενα παρουσίασης Αφηρημένες κλάσεις Ιδιότητες Λειτουργίες Απλοί τύποι Συσχετίσεις Εξάρτηση Διεπαφές αφηρημένες κλάσεις Οι αφηρημένες κλάσεις δεν μπορούν να δημιουργήσουν

Διαβάστε περισσότερα

Εργαστήριο «Τεχνολογία Πολιτισμικού Λογισμικού» Ενότητα. Επεξεργασία πινάκων

Εργαστήριο «Τεχνολογία Πολιτισμικού Λογισμικού» Ενότητα. Επεξεργασία πινάκων Ενότητα 4 Επεξεργασία πινάκων 36 37 4.1 Προσθήκη πεδίων Για να εισάγετε ένα πεδίο σε ένα πίνακα που υπάρχει ήδη στη βάση δεδομένων σας, βάζετε τον κέρσορα του ποντικιού στο πεδίο πάνω από το οποίο θέλετε

Διαβάστε περισσότερα

Αντικειμενοστρέφεια. Henri Matisse, Harmony in Red, Κωστής Σαγώνας Νίκος Παπασπύρου

Αντικειμενοστρέφεια. Henri Matisse, Harmony in Red, Κωστής Σαγώνας Νίκος Παπασπύρου Αντικειμενοστρέφεια Henri Matisse, Harmony in Red, 1908 Κωστής Σαγώνας Νίκος Παπασπύρου Ορισμοί αντικειμενοστρέφειας Ποιοι είναι οι ορισμοί των παρακάτω; Αντικειμενοστρεφής

Διαβάστε περισσότερα

Μεταγλώττιση και σύνδεση πολλαπλών αρχείων κώδικα. Προγραμματισμός II 1

Μεταγλώττιση και σύνδεση πολλαπλών αρχείων κώδικα. Προγραμματισμός II 1 Μεταγλώττιση και σύνδεση πολλαπλών αρχείων κώδικα Προγραμματισμός II 1 lalis@inf.uth.gr Χρήση λογισμικού που ήδη υπάρχει Τα πολύπλοκα συστήματα αναπτύσσονται σταδιακά, «χτίζοντας» πάνω σε υπάρχουσα λειτουργικότητα

Διαβάστε περισσότερα

Κεφάλαιο 14: Συμβουλές προς έναν νέο προγραμματιστή

Κεφάλαιο 14: Συμβουλές προς έναν νέο προγραμματιστή Κεφάλαιο 14: Συμβουλές προς έναν νέο προγραμματιστή Φτάσαμε σιγά σιγά στο τέλος του βιβλίου. Αντί για κάποιον επίλογο σκέφτηκα να συλλέξω κάποια πράγματα που θα ήθελα να πω σε κάποιον ο οποίος αρχίζει

Διαβάστε περισσότερα

Αλγόριθμος. Αλγόριθμο ονομάζουμε τη σαφή και ακριβή περιγραφή μιας σειράς ξεχωριστών οδηγιών βημάτων με σκοπό την επίλυση ενός προβλήματος.

Αλγόριθμος. Αλγόριθμο ονομάζουμε τη σαφή και ακριβή περιγραφή μιας σειράς ξεχωριστών οδηγιών βημάτων με σκοπό την επίλυση ενός προβλήματος. Αλγόριθμος Αλγόριθμο ονομάζουμε τη σαφή και ακριβή περιγραφή μιας σειράς ξεχωριστών οδηγιών βημάτων με σκοπό την επίλυση ενός προβλήματος. Εντολές ή οδηγίες ονομάζονται τα βήματα που αποτελούν έναν αλγόριθμο.

Διαβάστε περισσότερα

Δομές Δεδομένων. Καθηγήτρια Μαρία Σατρατζέμη. Τμήμα Εφαρμοσμένης Πληροφορικής. Δομές Δεδομένων. Τμήμα Εφαρμοσμένης Πληροφορικής

Δομές Δεδομένων. Καθηγήτρια Μαρία Σατρατζέμη. Τμήμα Εφαρμοσμένης Πληροφορικής. Δομές Δεδομένων. Τμήμα Εφαρμοσμένης Πληροφορικής Ενότητα 5: Δείκτες και Δυναμική Δέσμευση- Αποδέσμευση Μνήμης στη C/ Υλοποίηση ΑΤΔ Συνδεδεμένη Λίστα με δείκτες /Ένα πακέτο για τον ΑΤΔ Συνδεδεμένη Λίστα Καθηγήτρια Μαρία Σατρατζέμη Άδειες Χρήσης Το παρόν

Διαβάστε περισσότερα

Unity Editor #04 Κεντρικό Μενού: Edit, Unity Preferences

Unity Editor #04 Κεντρικό Μενού: Edit, Unity Preferences Unity Editor #04 Κεντρικό Μενού: Edit, Unity Preferences Γεια σου. Σε αυτό το μάθημα θα μιλήσουμε για τις δυνατότητες που μας δίνει η Unity να την κάνουμε να λειτουργεί όπως θέλουμε. Η αλήθεια είναι ότι

Διαβάστε περισσότερα

Εισαγωγή στον Προγραµµατισµό, Αντώνιος Συµβώνης, ΣΕΜΦΕ, ΕΜΠ,, Slide 6

Εισαγωγή στον Προγραµµατισµό, Αντώνιος Συµβώνης, ΣΕΜΦΕ, ΕΜΠ,, Slide 6 Ανασκόπηση Μια εφαρµογή Java είναι ένα σύνολο από συνεργαζόµενες κλάσεις Εβδοµάδα 2: Αντικείµενα, Κλάσεις και Μέθοδοι Εισαγωγή στον Προγραµµατισµό,,, Slide 1 Εισαγωγή στον Προγραµµατισµό,,, Slide 2 Ανασκόπηση:

Διαβάστε περισσότερα

Εισαγωγή στην Αριθμητική Ανάλυση

Εισαγωγή στην Αριθμητική Ανάλυση Εισαγωγή στην Αριθμητική Ανάλυση Εισαγωγή στη MATLAB ΔΙΔΑΣΚΩΝ: ΓΕΩΡΓΙΟΣ ΑΚΡΙΒΗΣ ΒΟΗΘΟΙ: ΔΗΜΗΤΡΙΑΔΗΣ ΣΩΚΡΑΤΗΣ, ΣΚΟΡΔΑ ΕΛΕΝΗ E-MAIL: SDIMITRIADIS@CS.UOI.GR, ESKORDA@CS.UOI.GR Τι είναι Matlab Είναι ένα περιβάλλον

Διαβάστε περισσότερα

Προγραμματισμός Η/Υ (ΤΛ2007 )

Προγραμματισμός Η/Υ (ΤΛ2007 ) Τμήμα Ηλεκτρονικών Μηχανικών Τ.Ε.Ι. Κρήτης Προγραμματισμός Η/Υ (ΤΛ2007 ) Δρ. Μηχ. Νικόλαος Πετράκης (npet@chania.teicrete.gr) Ιστοσελίδα Μαθήματος: https://eclass.chania.teicrete.gr/ Εξάμηνο: Εαρινό 2015-16

Διαβάστε περισσότερα

Εισαγωγή στα Αντικείμενα

Εισαγωγή στα Αντικείμενα 1 CSE-391: Artificial Intelligence University of Pennsylvania Matt Huenerfauth Εισαγωγή στα Αντικείμενα Δεκέμβριος 2016 2 Όλα είναι αντικείμενα Στην Python ότι χρησιμοποιούμε είναι αντικείμενο: hello.upper()

Διαβάστε περισσότερα

Εισαγωγή στον Προγραµµατισµό. Πανεπιστήµιο Θεσσαλίας Τµήµα Ηλεκτρολόγων Μηχανικών και Μηχανικών Η/Υ

Εισαγωγή στον Προγραµµατισµό. Πανεπιστήµιο Θεσσαλίας Τµήµα Ηλεκτρολόγων Μηχανικών και Μηχανικών Η/Υ Εισαγωγή στον Προγραµµατισµό Πανεπιστήµιο Θεσσαλίας Τµήµα Ηλεκτρολόγων Μηχανικών και Μηχανικών Η/Υ Συναρτήσεις 19.11.16 Β. Ντουφεξή 2 Προβλήματα: Οσο μεγαλώνουν τα προγράμματα, γίνονται πιο πολύπλοκα.

Διαβάστε περισσότερα

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Στοίβα και Σωρός Μνήμης Αντικείμενα ως ορίσματα

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Στοίβα και Σωρός Μνήμης Αντικείμενα ως ορίσματα ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ Αναφορές Στοίβα και Σωρός Μνήμης Αντικείμενα ως ορίσματα ΑΝΑΦΟΡΕΣ new Όπως είδαμε για να δημιουργήσουμε ένα αντικείμενο χρειάζεται να καλέσουμε τη new. Για

Διαβάστε περισσότερα

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ Κλάσεις και Αντικείμενα Η εξέλιξη των γλωσσών προγραμματισμού Η εξέλιξη των γλωσσών προγραμματισμού είναι μια διαδικασία αφαίρεσης Στην αρχή ένα πρόγραμμα ήταν

Διαβάστε περισσότερα

Πληροφορική & Τηλεπικοινωνίες K18 - Υλοποίηση Συστηµάτων Βάσεων εδοµένων Εαρινό Εξάµηνο 2009 2010

Πληροφορική & Τηλεπικοινωνίες K18 - Υλοποίηση Συστηµάτων Βάσεων εδοµένων Εαρινό Εξάµηνο 2009 2010 Πληροφορική & Τηλεπικοινωνίες K18 - Υλοποίηση Συστηµάτων Βάσεων εδοµένων Εαρινό Εξάµηνο 2009 2010 Καθηγητής. Γουνόπουλος Άσκηση 1 Σκοπός της εργασίας αυτής είναι η κατανόηση της εσωτερικής λειτουργίας

Διαβάστε περισσότερα

Πίνακες: μια σύντομη εισαγωγή. Πίνακες χαρακτήρων: τα "Αλφαριθμητικά"

Πίνακες: μια σύντομη εισαγωγή. Πίνακες χαρακτήρων: τα Αλφαριθμητικά Πίνακες: μια σύντομη εισαγωγή Πίνακες χαρακτήρων: τα "Αλφαριθμητικά" Πίνακες(Arrays): έννοιες και ορισμοί Ορισμός: Πίνακας (array) = σύνολο μεταβλητών του ιδίου τύπου (int, float, char,...) με ένα κοινό

Διαβάστε περισσότερα

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αντικείμενα ως ορίσματα Εισαγωγή στις αναφορές

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αντικείμενα ως ορίσματα Εισαγωγή στις αναφορές ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ Αντικείμενα ως ορίσματα Εισαγωγή στις αναφορές Αντικείμενα ως ορίσματα Μπορούμε να περνάμε αντικείμενα ως ορίσματα σε μία μέθοδο όπως οποιαδήποτε άλλη μεταβλητή

Διαβάστε περισσότερα

Προγραμματισμός Ι. Δείκτες. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Προγραμματισμός Ι. Δείκτες. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο Προγραμματισμός Ι Δείκτες Δημήτρης Μιχαήλ Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο Τι είναι ο δείκτης Ένας δείκτης είναι μια μεταβλητή που περιέχει μια διεύθυνση μνήμης. Θυμηθείτε πως

Διαβάστε περισσότερα

ΠΕΡΙΕΧΟΜΕΝΑ. Μονοδιάστατοι πίνακες Πότε πρέπει να χρησιμοποιούνται πίνακες Πολυδιάστατοι πίνακες Τυπικές επεξεργασίες πινάκων

ΠΕΡΙΕΧΟΜΕΝΑ. Μονοδιάστατοι πίνακες Πότε πρέπει να χρησιμοποιούνται πίνακες Πολυδιάστατοι πίνακες Τυπικές επεξεργασίες πινάκων ΠΕΡΙΕΧΟΜΕΝΑ Μονοδιάστατοι πίνακες Πότε πρέπει να χρησιμοποιούνται πίνακες Πολυδιάστατοι πίνακες Τυπικές επεξεργασίες πινάκων Εισαγωγή Η χρήση των μεταβλητών με δείκτες στην άλγεβρα είναι ένας ιδιαίτερα

Διαβάστε περισσότερα

Κεφάλαιο Αλφαριθμητικές Σειρές Χαρακτήρων (Strings) (Διάλεξη 20) 1) Strings στη C

Κεφάλαιο Αλφαριθμητικές Σειρές Χαρακτήρων (Strings) (Διάλεξη 20) 1) Strings στη C Κεφάλαιο 9.1-9.2 Αλφαριθμητικές Σειρές Χαρακτήρων (Strings) (Διάλεξη 20) 1) Strings στη C Ένα string είναι μία ακολουθία αλφαριθμητικών χαρακτήρων, σημείων στίξης κτλ. Π.χ. Hello How are you? 121212 *Apple#123*%

Διαβάστε περισσότερα

ΠΑΝΕΛΛΑΔΙΚΕΣ ΕΞΕΤΑΣΕΙΣ ΗΜΕΡΗΣΙΩΝ ΕΠΑΛ ΤΡΙΤΗ 11 ΙΟΥΝΙΟΥ 2019 ΕΞΕΤΑΖΟΜΕΝΟ ΜΑΘΗΜΑ: ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΥΠΟΛΟΓΙΣΤΩΝ

ΠΑΝΕΛΛΑΔΙΚΕΣ ΕΞΕΤΑΣΕΙΣ ΗΜΕΡΗΣΙΩΝ ΕΠΑΛ ΤΡΙΤΗ 11 ΙΟΥΝΙΟΥ 2019 ΕΞΕΤΑΖΟΜΕΝΟ ΜΑΘΗΜΑ: ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΥΠΟΛΟΓΙΣΤΩΝ ΠΑΝΕΛΛΑΔΙΚΕΣ ΕΞΕΤΑΣΕΙΣ ΗΜΕΡΗΣΙΩΝ ΕΠΑΛ ΤΡΙΤΗ 11 ΙΟΥΝΙΟΥ 2019 ΕΞΕΤΑΖΟΜΕΝΟ ΜΑΘΗΜΑ: ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΥΠΟΛΟΓΙΣΤΩΝ ΘΕΜΑ Α Α1. Να χαρακτηρίσετε τις προτάσεις που ακολουθούν, γράφοντας στο τετράδιό σας, δίπλα στο

Διαβάστε περισσότερα

Ανάπτυξη και Σχεδίαση Λογισμικού

Ανάπτυξη και Σχεδίαση Λογισμικού Ανάπτυξη και Σχεδίαση Λογισμικού Η γλώσσα προγραμματισμού C Γεώργιος Δημητρίου Βασικά Στοιχεία Το αλφάβητο της C Οι βασικοί τύποι της C Δηλώσεις μεταβλητών Είσοδος/Έξοδος Βασικές εντολές της C Αλφάβητο

Διαβάστε περισσότερα

Δομημένος Προγραμματισμός

Δομημένος Προγραμματισμός ΕΛΛΗΝΙΚΗ ΔΗΜΟΚΡΑΤΙΑ Ανώτατο Εκπαιδευτικό Ίδρυμα Πειραιά Τεχνολογικού Τομέα Δομημένος Προγραμματισμός Ενότητα: Συναρτήσεις θεωρία Δ. Ε. Μετάφας Τμ. Ηλεκτρονικών Μηχ. Τ.Ε. Άδειες Χρήσης Το παρόν εκπαιδευτικό

Διαβάστε περισσότερα

3 ο Εργαστήριο Μεταβλητές, Τελεστές

3 ο Εργαστήριο Μεταβλητές, Τελεστές 3 ο Εργαστήριο Μεταβλητές, Τελεστές Μια μεταβλητή έχει ένα όνομα και ουσιαστικά είναι ένας δείκτης σε μια συγκεκριμένη θέση στη μνήμη του υπολογιστή. Στη θέση μνήμης στην οποία δείχνει μια μεταβλητή αποθηκεύονται

Διαβάστε περισσότερα

Ηλεκτρονικοί Υπολογιστές ΙI. Βάσεις Δεδομένων. Ακαδημαϊκό Έτος Εργαστήριο 2. Διαφάνεια 1. Κάπαρης Αναστάσιος

Ηλεκτρονικοί Υπολογιστές ΙI. Βάσεις Δεδομένων. Ακαδημαϊκό Έτος Εργαστήριο 2. Διαφάνεια 1. Κάπαρης Αναστάσιος Βάσεις Δεδομένων Εργαστήριο 2 Διαφάνεια 1 Πώς να δημιουργήσω μια συσχέτιση ένα προς πολλά στην ACCESS; Η απάντηση στο παραπάνω θέμα, θα δοθεί μέσα από ένα παράδειγμα μιας μικρής βάσης δεδομένων. Το μοντέλο

Διαβάστε περισσότερα

Εισαγωγή στη γλώσσα προγραμματισμού C++

Εισαγωγή στη γλώσσα προγραμματισμού C++ Εισαγωγή στη γλώσσα προγραμματισμού C++ Περιβάλλον Εργασίας 2 Περιβάλλον Εργασίας 1. Χρήση απλού κειμενογράφου και Μεταγλωττιστή 2. Ολοκληρωμένα Περιβάλλοντα Εργασίας (Integrated Development Environments)

Διαβάστε περισσότερα

Προγραμματισμός Ι (ΗΥ120)

Προγραμματισμός Ι (ΗΥ120) Προγραμματισμός Ι (ΗΥ120) Διάλεξη 9: Συναρτήσεις Ορισμός συναρτήσεων () { /* δήλωση μεταβλητών */ /* εντολές ελέγχου/επεξεργασίας */ o Μια συνάρτηση ορίζεται δίνοντας

Διαβάστε περισσότερα

Διαδικτυακές Εφαρμογές Ενότητα 1: JPA

Διαδικτυακές Εφαρμογές Ενότητα 1: JPA Διαδικτυακές Εφαρμογές Ενότητα 1: JPA Μιχάλας Άγγελος Βούρκας Δημήτριος Τμήμα Μηχανικών Πληροφορικής ΤΕ Άδειες Χρήσης Το παρόν εκπαιδευτικό υλικό υπόκειται σε άδειες χρήσης Creative Commons. Για εκπαιδευτικό

Διαβάστε περισσότερα

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Δημιουργία Κλάσεων και Αντικειμένων

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Δημιουργία Κλάσεων και Αντικειμένων ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ Δημιουργία Κλάσεων και Αντικειμένων Κλάση Μια κλάση είναι μία αφηρημένη περιγραφή αντικειμένων με κοινά χαρακτηριστικά και κοινή συμπεριφορά. Ένα καλούπι/πρότυπο

Διαβάστε περισσότερα

Δομή Προγράμματος C++, Χειρισμός Μεταβλητών και Συναρτήσεις Εισόδου - Εξόδου

Δομή Προγράμματος C++, Χειρισμός Μεταβλητών και Συναρτήσεις Εισόδου - Εξόδου Εργαστήριο 2: Δομή Προγράμματος C++, Χειρισμός Μεταβλητών και Συναρτήσεις Εισόδου - Εξόδου Ο σκοπός αυτής της εργαστηριακής άσκησης είναι η ανάλυση των βασικών χαρακτηριστικών της Γλώσσας Προγραμματισμού

Διαβάστε περισσότερα

Διδάσκων: Κωνσταντίνος Κώστα Διαφάνειες: Δημήτρης Ζεϊναλιπούρ

Διδάσκων: Κωνσταντίνος Κώστα Διαφάνειες: Δημήτρης Ζεϊναλιπούρ Διάλεξη 2:Αλφαριθμητικές Σειρές Χαρακτήρων (Strings)- Επανάληψη Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: Εισαγωγικές Έννοιες σε Strings(Αρχικοποίηση, Ανάγνωση & Εκτύπωση) Πίνακες από Strings

Διαβάστε περισσότερα

Πληροφορική & Τηλεπικοινωνίες. K18 - Υλοποίηση Συστημάτων Βάσεων Δεδομένων Εαρινό Εξάμηνο

Πληροφορική & Τηλεπικοινωνίες. K18 - Υλοποίηση Συστημάτων Βάσεων Δεδομένων Εαρινό Εξάμηνο Πληροφορική & Τηλεπικοινωνίες K18 - Υλοποίηση Συστημάτων Βάσεων Δεδομένων Εαρινό Εξάμηνο 2010 2011 Δ. Γουνόπουλος Ι. Ιωαννίδης Άσκηση 2: Υλοποίηση Ευρετηρίου Β+ Δένδρου Προθεσμία: 6 Ιουνίου 2011, 11:59μμ

Διαβάστε περισσότερα

Διεργασίες (μοντέλο μνήμης & εκτέλεσης) Προγραμματισμός II 1

Διεργασίες (μοντέλο μνήμης & εκτέλεσης) Προγραμματισμός II 1 Διεργασίες (μοντέλο μνήμης & εκτέλεσης) Προγραμματισμός II 1 lalis@inf.uth.gr Ο κώδικας δεν εκτελείται «μόνος του» Ο εκτελέσιμος κώδικας αποθηκεύεται σε ένα αρχείο Το αρχείο είναι μια «παθητική» οντότητα

Διαβάστε περισσότερα

int array[10]; double arr[5]; char pin[20]; Προγραµµατισµός Ι

int array[10]; double arr[5]; char pin[20]; Προγραµµατισµός Ι Εισαγωγή Στον Προγραµµατισµό «C» Πίνακες Πανεπιστήµιο Πελοποννήσου Τµήµα Πληροφορικής & Τηλεπικοινωνιών Νικόλαος Δ. Τσελίκας Νικόλαος Προγραµµατισµός Δ. Τσελίκας Ι Πίνακες στη C Ένας πίνακας στη C είναι

Διαβάστε περισσότερα

ΠΛΗΡΟΦΟΡΙΚΗ Ι JAVA Τμήμα θεωρίας με Α.Μ. σε 8 & 9 18/10/07

ΠΛΗΡΟΦΟΡΙΚΗ Ι JAVA Τμήμα θεωρίας με Α.Μ. σε 8 & 9 18/10/07 ΠΛΗΡΟΦΟΡΙΚΗ Ι JAVA Τμήμα θεωρίας με Α.Μ. σε 8 & 9 18/10/07 Αλγόριθμος: Βήμα προς βήμα διαδικασία για την επίλυση κάποιου προβλήματος. Το πλήθος των βημάτων πρέπει να είναι πεπερασμένο. Αλλιώς: Πεπερασμένη

Διαβάστε περισσότερα

Βιβλιοθήκες Αφηρημένοι τύποι δεδομένων. Προγραμματισμός II 1

Βιβλιοθήκες Αφηρημένοι τύποι δεδομένων. Προγραμματισμός II 1 Βιβλιοθήκες Αφηρημένοι τύποι δεδομένων Προγραμματισμός II 1 lalis@inf.uth.gr Βιβλιοθήκες Τμήματα λογισμικού ευρύτερης χρησιμότητας που έχουν σχεδιαστεί με σκοπό να διευκολύνουν την ανάπτυξη πολλών διαφορετικών

Διαβάστε περισσότερα

Visual Flowchart Γενικά

Visual Flowchart Γενικά Visual Flowchart 3.020 -Γενικά Το Visual Flowchart ή «Data-Flow Visual Programming Language 3.020» (http://www. emu8086.com/fp) είναι ένα περιβάλλον ανάπτυξης και εκτέλεσης αλγορίθμων απευθείας σε μορφή

Διαβάστε περισσότερα

Η βασική συνάρτηση προγράμματος main()

Η βασική συνάρτηση προγράμματος main() Η βασική συνάρτηση προγράμματος main() HEADER FILES main(){ ΔΗΛΩΣΕΙΣ ΜΕΤΑΒΛΗΤΩΝ ΕΝΤΟΛΕΣ (σειριακές, επιλογής ή επανάληψης) ΕΠΙΣΤΡΕΦΟΜΕΝΟΣ ΤΥΠΟΣ (return 0;) Συναρτήσεις Η συνάρτηση είναι ένα υποπρόγραμμα

Διαβάστε περισσότερα

Κατακερματισμός (Hashing)

Κατακερματισμός (Hashing) Κατακερματισμός (Hashing) O κατακερματισμός είναι μια τεχνική οργάνωσης ενός αρχείου. Είναι αρκετά δημοφιλής μέθοδος για την οργάνωση αρχείων Βάσεων Δεδομένων, καθώς βοηθάει σημαντικά στην γρήγορη αναζήτηση

Διαβάστε περισσότερα

Ενότητα 5: ΜΕΤΑΒΛΗΤΕΣ

Ενότητα 5: ΜΕΤΑΒΛΗΤΕΣ Ενότητα 5: ΜΕΤΑΒΛΗΤΕΣ Οι Μεταβλητές στον Προγραμματισμό Οι μεταβλητές είναι θέσεις μνήμης που έχουν κάποιο όνομα. Όταν δίνω τιμή σε μία μεταβλητή, ουσιαστικά, αποθηκεύουμε στη μνήμη αυτή τον αριθμό που

Διαβάστε περισσότερα

Σχεδίαση Εφαρμογών και Υπηρεσιών Διαδικτύου 7 η Διάλεξη: Σύντομη εισαγωγή στην Java

Σχεδίαση Εφαρμογών και Υπηρεσιών Διαδικτύου 7 η Διάλεξη: Σύντομη εισαγωγή στην Java Σχεδίαση Εφαρμογών και Υπηρεσιών Διαδικτύου 7 η Διάλεξη: Σύντομη εισαγωγή στην Java Δρ. Απόστολος Γκάμας Λέκτορας (407/80) gkamas@uop.gr Σχεδίαση Εφαρμογών και Υπηρεσιών Διαδικτύου Διαφάνεια 1 Εισαγωγή

Διαβάστε περισσότερα

ΣΕΤ ΑΣΚΗΣΕΩΝ 3. Προθεσµία: 7/1/2014, 22:00

ΣΕΤ ΑΣΚΗΣΕΩΝ 3. Προθεσµία: 7/1/2014, 22:00 ΣΕΤ ΑΣΚΗΣΕΩΝ 3 ΕΡΓΑΣΤΗΡΙΟ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ I, ΑΚΑΔΗΜΑΪΚΟ ΕΤΟΣ 2013-2014 Προθεσµία: 7/1/2014, 22:00 Περιεχόµενα Διαβάστε πριν ξεκινήσετε Εκφώνηση άσκησης 1 Οδηγίες αποστολής άσκησης Πριν ξεκινήσετε (ΔΙΑΒΑΣΤΕ

Διαβάστε περισσότερα

Η πρώτη παράμετρος είναι ένα αλφαριθμητικό μορφοποίησης

Η πρώτη παράμετρος είναι ένα αλφαριθμητικό μορφοποίησης Η συνάρτηση printf() Η συνάρτηση printf() χρησιμοποιείται για την εμφάνιση δεδομένων στο αρχείο εξόδου stdout (standard output stream), το οποίο εξ ορισμού συνδέεται με την οθόνη Η συνάρτηση printf() δέχεται

Διαβάστε περισσότερα