स्ट्रक्चर और क्लास Swift प्रोग्रामिंग के दो मुख्य निर्माण खंड हैं। दोनों ही संबंधित डेटा और फंक्शन को जोड़कर अपने आप में पूरी इकाइयाँ बनाने के तरीके हैं। और जब आप कोई स्ट्रक्चर या क्लास परिभाषित करते हैं, तो आप Swift के अंदर इस्तेमाल होने के लिए एक नया टाइप परिभाषित करते हैं, ठीक उसी तरह जैसे आपने पहले से काम किए टाइप इस्तेमाल किए हैं, जैसे Int और String.
There are many similarities between structs and classes in Swift.
Swift में स्ट्रक्चर और क्लास के बीच कई समानताएँ हैं। इनमें यह भी शामिल है कि दोनों प्रॉपर्टी में वैल्यू रख सकते हैं और मेथड के ज़रिए काम करने की सुविधा देते हैं।
दोनों ही कुछ अतिरिक्त सुविधाएँ भी देते हैं, जो इस अभ्यास के दायरे से बाहर हैं।
स्ट्रक्चर और क्लास दोनों लगभग एक ही तरह परिभाषित किए जाते हैं। शुरुआत उचित कीवर्ड से होती है, उसके बाद परिभाषित किए जा रहे टाइप का नाम आता है, और फिर स्ट्रक्चर/क्लास का बॉडी कर्ली ब्रैकेट के बीच लिखा जाता है।
बॉडी में संग्रहीत प्रॉपर्टी हो सकती हैं, जिन्हें सामान्य कॉन्स्टेंट या वेरिएबल की तरह ही परिभाषित किया जाता है और जो उन्हीं की तरह व्यवहार करती हैं।
struct CharacterStats {
var health = 0.0
var speed = 0
var strength = 0
}
class GameCharacter {
var stats = CharacterStats()
var characterClass: String?
var name: String?
var active = false
let id = makeRandomID()
}
जैसा ऊपर बताया गया, किसी स्ट्रक्चर या क्लास को परिभाषित करना सिर्फ एक नया टाइप परिभाषित करना है। यह सिर्फ इस बात का खाका है कि उस टाइप की वैल्यू कैसी दिखेंगी, लेकिन यह आपके काम करने के लिए उस टाइप की कोई वैल्यू बनाता नहीं है।
उस टाइप का इंस्टेंस बनाने के लिए आपको टाइप का नाम लिखना होता है और उसके बाद ब्रैकेट का एक जोड़ा लगाना होता है।
let someStats = CharacterStats()
let someCharacter = GameCharacter()
इससे इन टाइप की वैल्यू बनती हैं, और इनमें प्रॉपर्टी परिभाषा में दी गई डिफॉल्ट वैल्यू से भर दी जाती हैं। ध्यान दीजिए कि GameCharacter की name प्रॉपर्टी जैसे ऑप्शनल मामलों में, अगर कोई वैल्यू न दी जाए, तो प्रॉपर्टी डिफॉल्ट रूप से nil हो जाएगी, ठीक वैसे ही जैसे सामान्य ऑप्शनल टाइप परिभाषित करने पर, जहाँ कोई वैल्यू तुरंत नहीं दी जाती।
स्ट्रक्चर के मामले में Swift अपने आप एक मेम्बरवाइज़ इनिशियलाइज़र देता है, जिसमें स्ट्रक्चर की प्रॉपर्टी की वैल्यू ब्रैकेट के अंदर दी जा सकती हैं, और ये परिभाषा में दी गई डिफॉल्ट वैल्यू को बदल देती हैं।
let differentStats = CharacterStats(health: 100.0, speed: 6, strength: 18)
स्ट्रक्चर और क्लास की प्रॉपर्टी को डॉट नोटेशन से एक्सेस किया जा सकता है, जिसमें वैल्यू का नाम लिखने के बाद एक डॉट (.) और फिर प्रॉपर्टी का नाम आता है। अगर किसी स्ट्रक्चर या क्लास की प्रॉपर्टी की अपनी प्रॉपर्टी हों, तो इस डॉट नोटेशन से इन नेस्टेड प्रॉपर्टी को भी एक्सेस किया जा सकता है।
इस नोटेशन का इस्तेमाल प्रॉपर्टी की वैल्यू पाने के लिए, और जहाँ अनुमति हो वहाँ उसे बदलने के लिए भी किया जा सकता है।
someStats.health
// => 0
someCharacter.name
// => nil
someStats.health = 87.3
someStats.health
// => 87.3
someCharacter.name = "Luther"
someCharacter.name
// => "Luther"
someCharacter.id = "new id"
// Error: Cannot assign to property: 'id' is a 'let' constant
प्रॉपर्टी की तरह, जो आपके स्ट्रक्चर और क्लास में डेटा रखती हैं, आप मेथड भी परिभाषित कर सकते हैं, जो आपके स्ट्रक्चर या क्लास में फंक्शन रखते हैं।
मेथड उसी तरह परिभाषित किए जाते हैं जैसे एक सामान्य फंक्शन, बस वे स्ट्रक्चर या क्लास के बॉडी के अंदर होते हैं। ध्यान दीजिए कि अगर कोई फंक्शन स्ट्रक्चर में किसी प्रॉपर्टी की वैल्यू बदलता है, तो उससे पहले mutating कीवर्ड लिखना ज़रूरी है। इसके अलावा, अगर कोई प्रॉपर्टी किसी मेथड से बदली जा सकती है, तो उस प्रॉपर्टी को let की जगह var से परिभाषित करना होगा, ठीक सामान्य वेरिएबल की तरह।
struct CharacterStats {
var health = 0.0
var speed = 0
var strength = 0
mutating func takeHit(_ damage: Double) {
health = max(0.0, health - damage)
}
func canLift(_ weight: Int) -> Bool {
weight < strength * 100
}
}
class GameCharacter {
var stats = CharacterStats()
var characterClass: String?
var name: String?
var active = false
let id: String = makeRandomID()
func takesDamage(_ damage: Double) {
stats.takeHit(damage)
if stats.health <= 0 {
active = false
}
}
func sayName() -> String {
return "My name is \(name ?? "no one"), my class is \(characterClass ?? "undetermined")"
}
func lift(_ weight: Int) -> String {
if stats.canLift(weight) {
return "No problem!"
} else {
return "Ooof! No way."
}
}
}
इन मेथड को प्रॉपर्टी की तरह ही डॉट नोटेशन से कॉल किया जा सकता है।
var myChar = GameCharacter()
myChar.stats = CharacterStats(health: 72.8, speed: 19, strength: 6)
myChar.active = true
myChar.lift(750)
// => "Ooof! No way."
myChar.takesDamage(80)
myChar.active
// => false
स्ट्रक्चर या क्लास के हर इंस्टेंस में एक अंतर्निहित प्रॉपर्टी होती है जिसका नाम self है, जो उस इंस्टेंस को ही दर्शाती है।
self का सबसे आम इस्तेमाल इंस्टेंस प्रॉपर्टी और उसी नाम वाले पैरामीटर के बीच फर्क साफ करना है:
struct Position {
var x = 0
mutating func move(x: Int) {
// Here, 'self.x' refers to the struct's property,
// while 'x' refers to the parameter passed to the method.
self.x = x
}
}
Swift के टाइप एक बड़े अक्षर वाला Self कीवर्ड भी देते हैं, जो खुद इंस्टेंस की जगह उस इंस्टेंस के टाइप को दर्शाता है।
इस अभ्यास में आप एक ऐसे कंप्यूटर सिस्टम की नकल करेंगे जो विंडो पर आधारित है। आप कुछ विंडो बनाएँगे जिन्हें हिलाया जा सकता है, जिनका साइज़ बदला जा सकता है, और जो अपनी सामग्री दिखाती हैं। नीचे दी गई तस्वीर उन वैल्यू को दर्शाती है जिनके साथ आप काम करेंगे।
<--------------------- screenSize.width --------------------->
^ ╔════════════════════════════════════════════════════════════╗
| ║ ║
| ║ position.x,_ ║
| ║ position.y \ ║
| ║ \<----- size.width -----> ║
| ║ ^ *──────────────────────┐ ║
| ║ | │ title │ ║
| ║ | ├──────────────────────┤ ║
screenSize.height ║ | │ │ ║
| ║ size.height │ │ ║
| ║ | │ contents │ ║
| ║ | │ │ ║
| ║ | │ │ ║
| ║ v └──────────────────────┘ ║
| ║ ║
| ║ ║
v ╚════════════════════════════════════════════════════════════╝
Size नाम का एक स्ट्रक्ट बनाइए जिसमें Int टाइप की दो प्रॉपर्टी हों, width और height, जो विंडो की मौजूदा चौड़ाई और ऊँचाई रखती हैं।
शुरुआती चौड़ाई और ऊँचाई क्रमशः 80 और 60 होनी चाहिए।
इसमें एक resize(newWidth:newHeight:) मेथड भी रखिए जो नई चौड़ाई और ऊँचाई के पैरामीटर लेता है और नए साइज़ के अनुसार प्रॉपर्टी बदल देता है।
let size1080x764 = Size(width: 1080, height: 764)
// Returns Size
var size1200x800 = size1080x764
// Returns Size
size1200x800.resize(newWidth: 1200, newHeight: 800)
size1200x800.height
// Returns 800
Position नाम का एक स्ट्रक्ट बनाइए जिसमें Int टाइप की दो प्रॉपर्टी हों, x और y, जो विंडो के ऊपरी बाएँ कोने की मौजूदा आड़ी और खड़ी स्थिति रखती हैं।
x और y की शुरुआती वैल्यू 0 होनी चाहिए। स्थिति (0, 0) स्क्रीन का ऊपरी बाएँ कोना है। जैसे-जैसे आप दाएँ जाते हैं, x की वैल्यू बढ़ती जाती हैं, और जैसे-जैसे आप नीचे जाते हैं, y की वैल्यू बढ़ती जाती हैं।
इसमें एक moveTo(newX:newY:) मेथड भी रखिए जो नए x और y पैरामीटर लेता है और नई स्थिति के अनुसार प्रॉपर्टी बदल देता है।
var point = Position(x: 10, y: 20)
// Returns Position
point.moveTo(newX: 100, newY: -100)
point.y
// Returns -100
एक विंडो क्लास बनाइए जिसमें ये प्रॉपर्टी हों:
title : String, शुरुआती वैल्यू "New Window" हैscreenSize : Size, स्थिर वैल्यू, जिसमें width = 800 और height = 600size : Size, शुरुआती वैल्यू Size स्ट्रक्ट की डिफॉल्ट वैल्यू हैposition : Position, शुरुआती वैल्यू Position स्ट्रक्ट की डिफॉल्ट वैल्यू हैcontents : String?, शुरुआती वैल्यू nil हैआपको इस क्लास के लिए एक खाली इनिशियलाइज़र भी बनाना चाहिए।
let window = Window()
window.title
// Returns "New Window"
resize(to:) : (Size) -> () - यह मेथड इनपुट में एक Size स्ट्रक्ट लेता है और विंडो का साइज़ बताए गए साइज़ तक बदलने की कोशिश करता है। लेकिन नया साइज़ कुछ सीमाओं से बड़ा नहीं हो सकता।x = 400, y = 300 पर है और height = 400, width = 300 का साइज़ माँगा जाता है, तो विंडो का साइज़ height = 300, width = 300 कर दिया जाएगा, क्योंकि y दिशा में स्क्रीन इतनी बड़ी नहीं है कि वह इस माँग को पूरी तरह पूरा कर सके।move(to:) : (Position) -> () - यह resize(to:) जैसा ही है, लेकिन यह मेथड साइज़ के बजाय विंडो की स्थिति को माँगी गई वैल्यू पर बदल देता है। resize की तरह यहाँ भी नई स्थिति कुछ सीमाओं से आगे नहीं जा सकती।x और y दोनों के लिए सबसे छोटी स्थिति 0 है।x = 250, y = 100 पर है और x = 600, y = 200 पर जाने की माँग की जाती है, तो विंडो को x = 550, y = 200 पर ले जाया जाएगा, क्योंकि x दिशा में स्क्रीन इतनी बड़ी नहीं है कि वह इस माँग को पूरी तरह पूरा कर सके।update(title:) : (String) -> () - यह मेथड title प्रॉपर्टी को उस स्ट्रिंग की वैल्यू पर सेट कर देता है जो पास की गई थी।update(text:) : (String?) -> () - यह मेथड contents प्रॉपर्टी को उस ऑप्शनल स्ट्रिंग की वैल्यू पर सेट कर देता है जो पास की गई थी।display() : () -> String - यह मेथड एक स्ट्रिंग लौटाता है जो बताती है कि विंडो में इस समय क्या-क्या है। जैसे, अगर विंडो का title "My First Window" है, position: x = 10, y = 100; size: width = 200, height = 150; और contents: "I 😍 my window" है, तो उसे यह स्ट्रिंग लौटानी चाहिए: "My First Window\nPosition: (10, 100), Size: (200 x 150)\nI 😍 my window\n" - अगर contents nil है, तो आखिरी लाइन "[This window intentionally left blank]" होनी चाहिएविंडो सिस्टम में एक इनिशियलाइज़र होना चाहिए ताकि विंडो को अपने इनपुट के साथ बनाया जा सके।
Window क्लास के लिए एक और इनिशियलाइज़र बनाइए।
इस इनिशियलाइज़र को ये पैरामीटर लेने चाहिए: title, contents, और दो ऑप्शनल पैरामीटर size और position।
size और position पैरामीटर की डिफॉल्ट वैल्यू Size और Position स्ट्रक्ट की डिफॉल्ट वैल्यू होनी चाहिए।
let window = Window(title: "My First Window", contents: "I 😍 my window")
window.display()
// Returns "My First Window\nPosition: (0, 0), Size: (80 x 60)\nI 😍 my window\n"
let window2 = Window(title: "My Second Window", contents: "I 😍 my window", size: Size(width: 200, height: 150), position: Position(x: 10, y: 100))
window2.display()
// Returns "My Second Window\nPosition: (10, 100), Size: (200 x 150)\nI 😍 my window\n"
Exercism पर साइन अप कीजिए और Swift को 35 कॉन्सेप्ट116 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।