Swift —რა არის და როგორ მუშაობს Closure-ი ფარდის უკან. Capturing ის იმპლემენტაცია pointer ების დონეზე 0 იდან.

Tornike Gomareli
11 min readJul 24, 2022

არსებობს ესეთი მცნება, infinite-regress, რომელიც წარმოადგენს უსასრულო entity ების სერიას, რომლებიც იმართებიან რეკურსიული პრინციპით სადაც თითოეული რეკურსია ასახავს თუ როგორაა თითოეული entity დამოკიდებული ან წარმოქმნილი, მისი წარმომქმნელისადმი. WTF ?

“Turtles all the way down” არის infinite-regress იის გამოხატულება. Hindu mythology აში სჯეროდათ, რომ სამყაროს სიცოცხლეში ეხმარებოდა ჯაჭვივით გადაბმული უზარმაზარი კუ-ების სერია, სადაც თითოეული კუს ქვევით ისევ კუ იყო. ამ ჯაჭვურ რეაქციაში ყველა კუს, სამყაროს მიმართ საკუთარი პასუხისმგებლობა და მისია გააჩნდა. მათ სჯეროდათ, რომ სულ თავში წარმოქმნილ კუზე, რამოდენიმე სპილო იდგა, რომლებზეც არსებობდა მთელი სამყარო. ზემოთ ხსენებული გამოხატულება ხშირად გამოიყენება, ისეთი პრობლემების ილუსტრაციისთვის როგორიც არის regress argument ი epistemology აში.

კარგით, ეხლა რა შუაშია ეს წინასიტყვაობა Closure ებთან ?

კომპიუტერულ მეცნიერების ყველაზე მნიშვნელოვანი ნაწილი ჩემი აზრით აბსტრაქციაა, რომელსაც ყოველდღე ვეჯახებით. ჩვენ ხშირად გვაქვს შეხება სხვადასხვა layer of abstraction თან. ვმუშაობთ ბიბლიოთეკებთან, რომლებიც დაშენებულია უფრო ქვედა დონის აბსტრაქციაზე, რომლებიც სხვა ენაზეა დაწერილი და ბოლოს კი ყველაფერი binary code ად გარდაიქმნება, რომელიც შემდეგ CPU ზე და GPU ზე ეშვება, რაც თავისმხრივ იყენებს ტრანზისტორებს და logic gate ებს.

ხანდახან ვერც კი ვიაზრებთ ჩვენს ქვევით თუ რამხელა აბსტრაქციაა და ზუსტად ამ ჯაჭვური აბსტრაქციებით ვქმნით “Turtles” და infinite-regress ს სადაც თითოეული აბსტრაქცია, ისევ აბსტრაქციაზეა დამოკიდებული. ხოლო ამ აბსტრაქციების ერთობლიობაზე დგას დღეს Software Development ი, და ამ აბსტრაქციების გააზრება ძალიან მნიშვნელოვანია.

ამ უსასრულო აბსტრაქციაში, ზუსტად ერთი პატრა აბსტრაქციაა Closure -ი, რომელზეც დღეს ვისაუბრებთ.

Swift ის ეკოსისტემაში ჩემი ერთ-ერთი საყვარელი feature ი Closure ია. ხელსაწყოები რომლებსაც ყოველდღიურად ვიყენებთ, third-party ბიბლიოთეკები და კიდევ უამრავი სხვა environment ი, რომელიც ჩვენი ყოველდღიურობის ნაწილია იყენებს Closure ებს. ის ერთ-ერთი feature ია ენის, რომელსაც მართლაც ყოველდღე ვიყენებთ, მაგრამ ხშირ შემთხვევაში არ გვესმის როგორ მუშაობს სიღრმისეულად.

აქვე ვეცდები გავიხსენო სად გვხვდება ყველაზე მეტად Closure ები

  1. RxSwift
  2. Combine
  3. DispatchQueue
  4. High order functions
  5. SwiftUI
  6. SnapKit
  7. Alamofire
  8. URLSession

ეს მწირი ჩამონათვალი, საკმარისია იმის გასააზრებლად თუ რამდენად მნიშვნელოვანია Closure ების ცოდნა სიღრმისეულად. ზუსტად ამ მიზეზის გამო გადავწყიტე გაგიზიაროთ ჩემი ცოდნა ამ საკითხთან დაკავშირებით, სიღრმისეულად გავარჩიოთ, განვიხილოთ და ვეცადოთ ჩვენით შევქმნათ რაიმე imaginary პროგრამირების ენაში closure ის ფუნქციონალი და ღრმად ჩავშალოთ მისი აბსტრაქცია.

ინტერნეტ სამყაროში ხშირად მხვდება Closure ის ახსნა მსგავსი განმარტებით

“Closure is just a function which is passed as an argument” — ანუ ეს მხოლოდ ფუნქციაა, რომელიც პარამეტრად გადაეცემა სხვა ფუნქციას. არადა რეალურად სულაც არაა ესე.

როგორ მუშაობს სიღრმისეულად ფუნქცია ? ფუნქციის დაძახების შემდეგ, ფუნქციის მისამართი და არგუმენტები ინახება stack ში, ხოლო return ის შემდეგ stack იდან ამ პარამეტრების POP ი ხდება, LIFO მიდგომით. (თუ Stack არ იცით რა არის, ამ ბმულს ეწვიეთ, ჩემი ძალიან ძველი სტატიაა, მაგრამ ზოგად ცოდნას მოგცემთ) ანუ გამოდის, რომ ჩვენი ფუნქციის შესახებ ყველანაირი ინფორმაცია ერთ stack ში ინახება. თუმცა CPU-მ რომ ჩვენი ფუნქცია გაუშვას, სჭირდება ფუნქციის მისამართი, რომელიც უბრალო ციფრებია 16 ობით სისტემაში. ყველა ფუნქცია, რომელიც იქმნება გამოყოფს მიზერულ მეხსიერებას, სადაც ამ კონკრეტული ფუნქციის მისამართი ინახება რომელიც შეგვიძლია ცვლადში შევინახოთ, ანუ გავაკეთოთ ფუნქციის პოინტერი, რომელიც ფუნქციის მისამართზე მიუთითებს. ლოგიკურად თუ გვაქვს, ცვლადი ეს გვაძლევს საშვალებას რომ სხვა ფუნქციას, პარამეტრის სახითაც ჩავაწოდოთ.

ესე გამოიყურება ზევით მოყვანილი მაგალითის იმპლემენტაცია Swift ში

takesAnotherFunction ს პარამეტრად ზუსტად სხვა ფუნქციის მისამართი გადავეცით, და შემდეგ ამ ფუნქციამ შეძლო მისამართის წყალობით ამავე ფუნქციის გამოძახება.

ხომ არ გახსენდებათ სად ვიყენებთ ამ მიდგომას iOS ში ?

როდესაც გვინდა, რომ ღილაკზე დაჭერისას რაიმე კონკრეტული ფუნქცია გამოვიძახოთ, გვჭირდება რომ შევქმნათ @objc ატრიბუტით ფუნქცია, რომელსაც selector ში ჩავაწვდით.

რატომ ?

UIKit ი როდესაც დააფიქსირებს მის ღილაკზე .touchUpInside ის event ს, მას ამ დროს ექნება ჩვენი ფუნქციის მისამართი და შეძლებს რომ დაუძახოს მას. ამ მექანიზმის დახმარებით ჩვენ შეგვიძლია ამ ღილაკის click ზე ჩვენს საკუთარ ფუნქციაში, ჩვენი საკუთარი ლოგიკა დავწეროთ.

რა მინუსი აქვს ფუნქციის უბრალოდ პარამეტრად ჩაწოდებას ?

ერთი და ყველაზე დიდი მინუსი არის ის, რომ მას არ აქვს წვდომა გარე სამყაროსთან, მისი კონტექსტი მხოლოდ საკუტარი stack frame იდან რეზულტატის დაბრუნებამდეა.

და ზუსტად აქ შემოდის თამაშში Closure ი, რომელიც გვაძლევს საშვალებას გარე სამყაროსთან კავშირი გვქონდეს და ვიყენებდეთ ისე როგორც ფუნქცია, თუმცა ასევე ბევრად უფრო საინტერესო რაღაცეები შეეძლოს ვიდრე უბრალოდ ჩვეულებრივ მეთოდს.

Swift ში იმისთვის, რომ ფუნქციას callback ი გავაყოლოთ ვიყენებთ Closure ს. Closure არის კოდის ბლოკი, რომელსაც საკუთარი ფუნქციონალი გააჩნია. Closure ებია იგივეა რაც მაგალითად block ები C ში, Obj-C ში და lambda ფუნქციები ები სხვა ენებში.

Closure ების ძალა იმალება Capturing ში. Capturing ი არის პროცესი როცა ზემოთ ხსენებულს შეუძლია შეინახოს ყველა reference, კონსტანტები და ჩვეულებრივ ცვლადები, რომელიც არსებობს იმ კონტექსტში სადაც Closure ი შეიქმნა.

ვნახოთ პატარა მაგალითი

ზემოთ მოყვანილ მაგალითში ვხედავთ f closure ს, რომელიც Capturing ს უკეთებს Environment ს სადაც firstVariable ცვლადი არსებობს.

  • Capturing - არის პროცესი როდესაც ინახავს context ში მყოფ ცვლადებს.
  • Environment - Closure ის გარეთ მყოფი context ი, ყველა ცვლადი, რეფერენსი და ობიექტი რომლის Capturing იც შესაძლებელია.

closure ის გამოძახება მხოლოდ მაშინ ხდება, როდესაც f ს დავუძახებთ, რაც იმას ნიშნავს რომ Captured ცვლადი ყოველთვის დინამიური იქნება და ნებისმიერ დროს შეიძლება შეიცვალოს, გამომდინარე აქედან Closure ის პასუხიც ყოველთვის განსხვავებული იქნება.

Closure ებს reference სემანტიკა აქვს და არა value. ანუ Closure ის შექმნისას მონაცემები heap ში გამოიყოფა და არა stack ში, რა მონაცემები ? ყველაფერი რაც Environment ში იქნება, წავა heap ში Closure ის სახით.

ზემოთ ხსენებულიდან გამომდინარე, ხშირია იმის რისკი რომ Closure ბთან მუშაობის დროს გაგვეპაროს Memory leak ი. ამის პრევენციისთვის რამოდენიმე ხერხი არსებობს, თუმცა მანამდე ვისაუბროთ Closure ების ტიპებზე Swift ში.

სულ ორი ტიპის Closure ი გვაქვს.

@nonescaping Closure ები

ესეთია Closure რომელიც სხვა ფუნქციას პარამეტრად გადაეცემა და მისი გამოძახება ამავე function body ში ხდება და რეზულტატიც მაშინვე ბრუნდება. ფუნქციის დასრულების შემდეგ, გადაწოდებული Closure ი out of scope აღმოჩნდება, მისი reference counter ი 0 გახდება და ARC წაშლის მას მეხსიერებიდან.

nonescaping Closure ის სიცოცხლის უნარიანობა ესეთია:
1. გადაეცემა პარამეტრად ფუნქციას (heap ში გამოიყოფა ადგილი)
2. Environment capturing ის აკეთებს და შემდეგ რაიმე ფუნქციონალსაც ასრულებს.
3. ფუნქცია ეძახის Closure ს.
4. ფუნქცია აბრუნებს პარამეტრს, Closure გასცდება scope ს და წაიშლება მეხსიერებიდან.

ამ მაგალითში completionHandler ი nonescaping Closure ია, ატრიბუტის მიწერა საჭირო არ არის, რადგან ყველა Closure ი, რომელიც Environment ფუნქციის დასასრულამდე გამოიძახება default ად არის nonescaping ი.

რომელია Environment ფუნქცია ?

  • getSumOfArray

გამოიძახება completioHandler ი იქამდე სანამ getSumOfArray დასრულდება ?

  • კი

გამოდის, რომ სანერვიულო არაფერი მაქვს, დარწმუნებული ვარ რომ დროებით შექმნილი reference ობიექტი იქამდე წაიშლება სანამ Environment ი ცოცხალი იქნება.

@escaping Closure ები

როდესაც Closure ს პარამეტრად გადავცემთ და ვიცით, რომ function body უფრო მალე დასრულდება და არ ვიცით Closure ი ზუსტად როდის გამოიძახება ესეთ დროს გვიწევს რომ Closure მოვნიშნოთ @escaping ად.

როდესაც ფუნქციის execution ი მორჩება, Closure მაინც იარსებებს მეხსიერებაში იქამდე სანამ მისი გამოძახება არ მოხდება.

როდის გვჭირდება escaping Closure ი?

  • ასინქრონული Execution ის დროს.
  1. როდესაც Closure ს ვატანთ DispatchQueue ს ან ნებისმიერ ისეთ გარემოს, რომელიც რაიმე სახის ოპერაციას უშვებს background thread ზე და შემდეგ აპირებს ჩვენი Closure ის გამოძახებას, რომ შესრულებული სამუშაოს რეზულტატი დაგვიბრუნოს.
  2. ან ჩვენს გადაცემულ Closure ს უშვებს background thread ზე და აქაც არ ვიცით ოპერაცია როდის დასრულდება.

ყველა ესეთ შემთხვევაში Environment ფუნქცია აპრიორში დასრულდება იქამდე სანამ ჩვენი closure ი გამოიძახება, ამიტომ არ ვიცით ეს როდის მოხდება. ასეთ შემთხვევებში Compiler ი სხვადასხვა ოპტიმიზაციებს აკეთებს, აქედან გამომდინარე მას ჭირდება წინასწარ, რომ იცოდეს რომელი Closure ისა escaping და nonescapig ი.

ამ ატრიბუტით ხვდება compiler ი თუ რომელი ოპტიმიზაციით აამუშაოს ჩვენი კოდი.

თუ Closure ს რაიმე ასინქრონულ ოპერაციაში გამოვიყენებთ და მას @escaping ს არ მივუწერთ, Compile time error ი გვექნება.

მაგალითი სადაც escaping closure ის გამოყენებაა საჭირო.

რა პრობლემები შეიძლება გამოიწვიოს Closure ებმა ?

როგორც ავღნიშნეთ Closure ი reference type ია, და მისი მეხსიერება heap ში გამოიყოფა. Capturing ის დროს ყველაფერს ინახავს, მაგალითად თუ შენ self ს გამოიყენებ closure ში, ის self ის strong reference ს შეინახავს მისი სიცოცხლის განმავლობაში. თუ self ს ასევე დაჭირდება რომ შეინახოს closure ის რეფერენსი (იმ შემთხვევაში, თუ მის გამოძახებას მოგვიანებით გეგმავს) მაშინ მათ შორის retain cycle ანუ circular reference ი მოხდება.

retain cycle ი ავტომატურად memory leak ს ნიშნავს.

როგორ უნდა დავიცვათ თავი ? მადლობა Chris lattner ს, რომ გვაქვს weak და unowned keyword ები.

მარტივად რომ ვთქვათ weak და unowned ი ეუბნება ARC ის, რომ კონკრეტული ობიექტის შექმნის შემდეგ reference counter ი არ გაიზარდოს და ის მუდამ 1 იყოს. ეს აღარ გამოიწვევს strong circular reference cycle ს closure სა და self ს შორის. (უფრო კონკრეტულად ARC ზე და memory management ზე, შემდეგ სტატიებში ვისაუბრებ)

განსხვავება weak სა და unowned შორის ძალიან მცირეა

  1. weak ი ავტომატურად შეფუთავს self ს optional ში, რაც საჭიროა მეტი უსაფრთხოებისთვის.
  2. unowned ი ისე მოიქცევა როგორც force unwrap ი, რაც საკმაოდ სახიფათოა.

ზუსტად ვიცით, რომ ამ შემთხვევაში memory leak ისგან დაზღვეულები ვართ, თუმცა ამას შეიძლება კიდევ ახალი პრობლემა მოყვეს.

წარმოვიდგინოთ ესეთი შემთხვევა, რომ Closure ი ფუნქციაში ასინქრონული ოპერაციის იქნება გამოძახებული, მაგრამ არავინ ვიცით ეს ოპერაცია როდის მოხდება. სავსებით შესაძლებელია, როდესაც ეს ოპერაცია მოხდება self ი მთლიანად წაშლილი იყოს memory დან, ხოლო ჩვენი closure ის body იყენებს ამ self ს და მისი მთელი Environment ის Capturing ი აქვს გაკეთებული.

ასეთ შემთხვევაში აპრიორი ქრაში გვაქვს თუ:

  1. არ ვიყენებთ პირდაპირ self ს შემოწმების გარეშე.
  2. ვიყენებთ unowned self ს
  3. ან ვიყენებთ weak self ს, მაგრამ self ის unwrap ს force ით ვაკეთებთ.

საუკეთესო გამოსავალია guard ის გამოყენება ან optional chaining ოპერატორის გამოყენება > ?

ქვევით მოყვანილ მაგალითში ყველანაირად დაზღევულები ვართ

  1. memory leak ისგან
  2. nil pointer exception ისგან

თუ self ი მკვდარი იქნება, closure იდან გამოსვლა პირველივე ხაზზე მოხდება.

ხოლო cycle reference არ მოხდება მათ შორის რადგან closure ში პარამეტრად weak self ი შემოდის და არა strong ი.

guard ით შექმნილი strongSelf ი, რომელიც მაშინვე წაიშლება როგორც კი closure ი მორჩება executions

Closure - ის იმპლემენტაცია lower level ში

მემგონი საკმარისზე მეტი ვისაუბრე Closure ების მუშაობის პრინციპზე, მაგრამ მაინც მგონია, რომ ყველაზე ნათლად მაშინ ვიგებთ აბსტრაქციას, როდესაც ჩვენითვე ვქმნით მათ.

Closure ების შექმნა ორ აზროვანია და მოდით ესე გავანაწილოთ.

  1. პირველ ნაწილში შევქმნათ ძალიან აბსტრაქტულად და მარტივად, ამას Easy part დავარქვათ.
  2. მეორე ნაწილში, ნამდვილად გავიმეოროთ ის რაც Memory ში ხდება, Capturing ის დროს და კომპლექსური იმპლემენტაცია გავაკეთოთ, ამას კი Complex part

Closure ის იმპლემენტაცია, Easy part

წარმოვიდგინოთ, რომ რაღაც წარმოდგენით ენაში ვმოღვაწეობთ სადაც Closure ი ტექნიკურად არ არსებობს.

  1. ფუნქციებს არ შეუძლიათ Capturing ი.
  2. არ იციან მათი Environment ი რა არის.
  3. ფუნქციის პარამეტრად გადაწოდება, მხოლოდ სტანდარტული მიდგომით შეიძლება, სადაც ფუნქციამ მხოლოდ საკუთარი context ი იცის.

(იმისთვის, რომ Closure ის აბსტრაქცია შევქმნათ, პრე-რეკვიზიტად არ გვჭირდება Compiler ის შესახებ რაიმეს ცოდნა)

ზემოთ აღწერილ ენაში, წარმოვიდგინოთ ესეთი კოდი.

წარმოდგენით ენაში ვართ, სადაც closure ები არ გვაქვს.

ასეთ შემთხვევაში compile ერორი გვექნება.

კომპილატორი გვეტყვის, რომ f მა არ იცის number ი რა არის, რადგან არ გვაქვს Capturing ი. Capturing ი იმიტომ ვერ მოხდა, რომ არ გვაქვს Environment და არ ვიცით ჩვენი კონტექსტი რა არის.

Closure ების იმპლემენტაციისთვის რა თქმა უნდა, რომ დაგვჭირდება class ები, რომ მივიღოთ reference type ი. ასევე გამოსადეგი იქნება თუ Closure ს ჯერ ავღწერთ როგორც პროტოკოლს, რადგან ის აბსტრაქციად წარმოვიდგინოთ.

Closure პროტოკოლი

მოდით ეხლა ამ Closure პროტოკოლის იმპლემენტაცია დავწეროთ, კონკრეტულ კლასში.

F კლასი, რომელიც იმპლემენტაციას უკეთებს Closure პროტოკოლს.
  • F ი Environment ის კლასს ქმნის, სადაც შიგნით state ს ინახავს, ამ შემთხვევაში Int ცვლადს
  • F ს env ცვლადი აქვს, რომელიც Environment ტიპისაა
  • ინიციალიზატორით ხდება Environment ის injection ი კლასში.
  • გვაქვს willRun ფუნქცია, რომელიც პარამეტრად იღებს x ს ხოლო აბრუნებს x +environment ში შენახული ინტეჯერი ანუ x + env.a

ჩვენ უკვე ჩვენი საკუთარი Closure ი შევმქენით, რომელიც envrionment ს ქმნის, მის Capturing ს ანხორციელებს და რაღაც კონკრეტულ ფუნქციას იძახებს. რა თქმა უნდა ჩვენს Closure ს მხოლოდ Int ის შენახვა შეუძლია, მხოლოდ ერთის და ძალიან მწირი ფუნქციონალი აქვს, მაგრამ სასწავლო გარემოსთვის სავსებით საკმარისია.

როგორ გამოვიყენოთ ?

ჩვენივე შექმნილი Closure ის, წარმოდგენითი გამოყენება.

რა თქმა უნდა ნამდვილ Closure ს არ გავს, რომელსაც Swift ში ვიყენებთ, მაგრამ პრინციპი ერთი და იგივეა.

  1. ისეტავს Environment ს რომელიც მის გარშემო არსებობს.
  2. Capturing ს უკეთებს ველა ინფორმაციას, რომელიც Environment ში არსებობს.
  3. ასრულებს რაღაც ბრძანებას, სადაც Environment ი ყოველთვის დინამიურია და ნებისმიერ დროს შეიძლება შეიცვალოს.

დაახლოებით ესეთ პეტერნს იყენებდა C++ იქამდე სანამ C++11 ში lambda არ დაამატეს. თუ რომელიმე სხვა ენაში ტერმინი ანონიმურ ფუნქციები მოგისმენია, შეგიძლია წარმოიდგინო რომ Closure ი ამის syntactic sugar ია.

Closure ის იმპლემენტაცია, Complex part

ფარდის უკან რეალურად, closure ისთვის კომპილატორი ბევრად უფრო კომპლექსურ და რთულ პრობლემებს აგვარებს.

მოდი ვცადოთ და წოტა უფრო ჩავშალოთ აბსტრაქციას და უფრო კომპლექსურად, რეალობასთან მიახლოებული Closure ის იმპლემენტაცია გავაკეთოთ.

მაგალითისთვის წარმოვიდგინოთ, რომ გვაქვს Closure ი, რომელმაც მხოლოდ 1 value ს Capturing ი უნდა მოახდინოს.

Single value capturing ი Box სტრუქტურის სახით.

მოდით ეხლა შევამოწმოთ, რამდენად მოერგება ჩვენი დაწერილი აბსტრაქცია რეალურ გამოყენებას და შევქმნათ კიდევ ერთი აბსტრაქცია.

სიმულაცია ჩვენი შექმნილი ThickFunction ის თუ როგორ აკეთებს რეალურად Capturing ს გარე Environment ის და ამ დროს რა ხდება რეალურად Memory ში.

ზემოთ მოყვანილ კოდის მაგალითს თუ კარგად წაიკითხავთ, ნახავთ რომ ჩვენი აწყობილი აბსტრაქცია მშვენივრად მუშაობს. ზუსტად გამოყოფთ memory ში ადგილს და Capturing ი სადაც მხოლოდ ერთი value ს შენახვა გვინდა მშვენივრად მუშაობს.

სიმულაციური Closure ისთვის ზუსტად აფდეითდება Box ში შენახული გარე სამყაროს ცვლადი.

კოდს თუ კარგად დააკვირდით ყველა ხაზი დაკომენტირებულია გარდა ერთისა, რომელიც ყველაზე მნიშვნელოვან საქმეს, Capturing ს აკეთებს 1 memory დან მეორეზე.

withMemoryRebound ს შეუძლია გაუშვას გადაწოდებული closure ი, საკუთარ კონტექსტში ისე რომ ThickFunction ში გამოყოფილი პოინტერის მისამართს binding ი გაუკეთოს და საკუთარად წარმოიდგინოს. ზუსტად ესე ხდება Capturing ი ფარდის უკან. ერთი Environment იდან მეორე მემორიში ინფორმაციის შენახვას ჯერ ჭირდება ორი მეხსიერების binding ი, ხოლო შემდეგ შესაძლებელი ხდება მეხსიერების გამოყოფა და შენახვა ერთი ადგილიდან მეორეში.

კარგი ეხლა გავიაზროთ binding ი რა არის ?

C/C++ ში memory ობიექტი მეხსიერების ისეთ ნაწილს ეწოდება რომელში ჩაწერილი მნიშვნელობა რაღაც ცვლადში წერია. C/C++ის შემთხვევაში ცვლადის შექმნა თავისთავად ტიპის მინიჭებასაც გულისხმობს, იმიტო რო staticly typed ენაა. უფრო დეტალურად, მეხსიერების რაღაც მონაკვეთში წერია ბაიტები და ამ ბაიტების ინტერპრეტირებისთვის საჭიროა მონაცემის ტიპის ცოდნა. ზუსტად ერთიდაიგივე ბიტების მწკრივი შეიძლება ორ სხვადასხვა რაღაცას ნიშნავდეს იმის მიხედვით თუ რა ტიპში ჩაწერ. თან, ტიპის ცოდნის გარეშე ოპერაციას ვერ ჩაატარებ მაგ მონაზემზე.

ანუ, C/C++ში ობიექტს აქვს ორი რაღაცა: ბაიტები და ტიპი. ბაიტები რო value იცოდეს კომპილერმა და ტიპი რო ამ value-ზე ოპერაციები ჩაატაროს.

binding ის საშვალებით დინამიურად(ტიპის მითითების გარეშე) შეგიძლია რამე ცვლადის შექმნა, binding ის გარეშე ამას ვერ ვიზავთ, რადგან რაღაც ოპერაციამდე(თუნდაც მიმატებამდე) აუცილებელია რო ტიპი დადგინდეს.

ხოდა, swift-ში ამ სისტემური პროცედურის ჩატარებას binding ს ვეძახით, და ეს ფუნქციაც ზუსტად ამას აკეთებს ქვედა დონეზე.

ეს მაგალითი მხოლოდ მოერგება ისეთ Capturing ს, სადაც 1 value გვაქვს, რეალურად მრავალ value იან Capturing ში ბევრად უფრო რთული იმპლემენტაციები ხდება და თუ ამ კომპლექსურობას ზემოდან დავუმატებთ Generic ებს მაშინ ერთი-ორად იზრდება სირთულე, ამ ყველაფრის იმპლემენტაციისთვის.

კონცეპტუალურად ბოლო საკითხი, ყველაზე რთული გასაგებია, თუ გაინტერესებთ, გირჩევთ swift ის mailing list ი გამოიწეროთ, ბევრ საინტერესო საკითხს წაიკითხავთ და საინტერესო დისკუსიებს დაესწრებით. დასაწყისისთვის კი ქვევით მოცემულ ბმულს ეწვიეთ, რომ უკეთესად გაიგოთ რას აკეთებს withMemoryRebound ფუნქცია.

საოცარია, რამდენად დიდი და ღრმა აბსტრაქციები იმალება მინიმალისტური ფუნქციონალის უკან, რომელსაც მართლა ყოველდღიურად ვიყენებთ მაგრამ ბოლოდე მაინც არ გვესმის როგორ მუშაობს ის და რამდენ შრომას და ცოდნავს მოითხოვს იმ კომფორტის შექმნა, რომელიც ჩვენთვის არის შექმნილი.

მადლობა ჩემ მეგობარს Amiko Malania -ს დახმარებისთვის და დეტალების დაზუსტებისთივს, როდესაც რამეს ვერ ვიგებ system ურ დონეზე, მასთან გადავამოწმებ ხოლმე ყველაფერს.

იმედი მაქვს მინიმალურად მაინც დაგეხმარებათ ჩემი სტატია, პროგრამირების ამ ფანტაზიურ სამყაროში 🚀

--

--

Tornike Gomareli

Specialising in iOS and System Programming. Always trying to learn how to think better. twitter / @tornikegomareli