با عرض سلام و وقت بخیر خدمت کاربران سایت پی وی لرن . و کاربرانی که دوره کامل آموزش Swift را دنبال می کنند. گاهی نتایج برخی عملکردها ، جستجوها، توابع و … در Swift ممکن است ‘nil’ یا صفر باشد، در این صورت Swift بنا بر حالت های مختلف مقایری را به کاربران باز می گرداند، این فرآیندها ممکن است شامل پرس و جوها ، خصوصیات ، زیر مجموعه ها و متدها باشد که می توانند مقدار صفر برگردانند، اینگونه موارد در واقع به عنوان Optional chaining یا مفهوم زنجیره اختیاری در Swift شناخته می شوند، در ادامه این مبحث شما را بیشتر با این موضوع آشنا خواهیم کرد.
در این اموزش شما با مفهوم زنجیره اختیاری در Swift و مثال هایی از آن آشنا خواهید شد.
Optional chaining (زنجیره اختیاری در Swift) می تواند یکی از دو مقدار زیر را برگرداند:
از آنجایی که پرس و جوهای چندگانه به متدها،خصوصیات و زیر مجموعه ها با هم گروه بندی شده اند،خطا در یک زنجیره در کل زنجیره تاثیر می گذارد.
در نتیجه مقدار “nil” بازگردانده می شود.
Optional chaining بعد از مقدار اختیاری با کاراکتر ‘?’ برای فراخوانی یک ویژگی،متد یا زیرمجموعه زمانیکه مقدار اختیاری یک مقادیر یکسان را بازمی گردااند مشخص شده است.
Optional Chaining ‘?’ | دسترسی به روش ها، خواص ها و زیرمجموعه های Chaining اختیاری ‘!’ |
؟ بعد از مقدار اختیاری به نام property، method، or index قرار می گیرد. | ! بعد از مقدار اختیاری برای فراخوانی ویژگی، متد یا زیرنویس به مجبور کردن برای باز کردن مقدار ارزش داده می شود. |
زمانیکه مقدار اختیاری ‘nil’ باشد خطا رخ می دهد. | زمانیکه مقدار اختیاری ‘nil’ باشد خطا رخ داده است. |
1 2 3 4 5 6 7 8 9 10 | class ElectionPoll { var candidate: Pollbooth? } lass Pollbooth { var name = "MP" } let cand = ElectionPoll() let candname = cand.candidate!.name |
نتیجه ی کامپایل و اجرای مثال فوق :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | fatal error: unexpectedly found nil while unwrapping an Optional value 0 Swift 4 0x0000000103410b68 llvm::sys::PrintStackTrace(__sFILE*) + 40 1 Swift 4 0x0000000103411054 SignalHandler(int) + 452 2 libsystem_platform.dylib 0x00007fff9176af1a _sigtramp + 26 3 libsystem_platform.dylib 0x000000000000000b _sigtramp + 1854492939 4 libsystem_platform.dylib 0x00000001074a0214 _sigtramp + 1976783636 5 Swift 4 0x0000000102a85c39 llvm::JIT::runFunction(llvm::Function*, std::__1::vector > const&) + 329 6 Swift 4 0x0000000102d320b3 llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*, std::__1::vector<std::__1::basic_string, std::__1::allocator >, std::__1::allocator<std::__1::basic_string, std::__1::allocator > > > const&, char const* const*) + 1523 7 Swift 4 0x000000010296e6ba Swift 4::RunImmediately(Swift 4::CompilerInstance&, std::__1::vector<std::__1::basic_string, std::__1::allocator >, std::__1::allocator<std::__1::basic_string, std::__1::allocator > > > const&, Swift 4::IRGenOptions&, Swift 4::SILOptions const&) + 1066 8 Swift 4 0x000000010275764b frontend_main(llvm::ArrayRef, char const*, void*) + 5275 9 Swift 4 0x0000000102754a6d main + 1677 10 libdyld.dylib 0x00007fff8bb9e5c9 start + 1 11 libdyld.dylib 0x000000000000000c start + 1950751300 Stack dump: 0. Program arguments: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/ usr/bin/Swift 4 -frontend -interpret - -target x86_64-apple-darwin14.0.0 - target-cpu core2 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/ SDKs/MacOSX10.10.sdk -module-name main /bin/sh: line 47: 15672 Done cat <<'SWIFT 4' import Foundation </std::__1::basic_string</std::__1::basic_string</std::__1::basic_string</std:: __1::basic_string |
در برنامه ی فوق ‘election poll’ به عنوان نام کلاس اعلام شده و همچنین شامل یک تابع عضور با نام ‘candidate’ نیز می باشد.
یک زیر کلاس با نام ‘poll booth’ و یک زیر تابع با نام ‘name’ و مقداردهی اولیه ی ‘MP’ اعلام شده است.
فراخوانی ابر کلاس با ایجاد یک نمونه ‘cand’ با اختیاری ‘!’ مقداردهی اولیه شده است.
از آنجا که مقادیر در کلاس پایه خود اعلام نشده است، مقدار ‘nil’ ذخیره می شود.
بنابراین یک خطا رخ خواهد داد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class ElectionPoll { var candidate: Pollbooth? } class Pollbooth { var name = "MP" } let cand = ElectionPoll() if let candname = cand.candidate?.name { print("Candidate name is \(candname)") } else { print("Candidate name cannot be retreived") } |
نتیجه ی کامپایل و اجرای مثال فوق :
1 | Candidate name cannot be retreived |
در برنامه ی فوق، ‘election poll’ به عنوان نام کلاس و ‘candidate’ نیر به عنوان تابع عضو آن تعریف شده است.
زیر کلاس با نام ‘poll booth’ و ‘name’ نیز به عنوان تابع عضو آن با ‘MP’ مقداردهی اولیه شده است.
فراخوانی ابر کلاس با ایجاد یک نمونه ‘cand’ با اختیاری ‘؟’ مقداردهی اولیه شده است.
از آنجا که مقادیر در کلاس پایه اعلام نشده است، مقدار “nil” ذخیره شده و در بلوک تحریر توسط کنسول چاپ می شود.
optional chaining در Swift 4 امکان تعریف بیشتر از یک زیر کلاس را به عنوان مد کلاس ها فراهم می کند.
این مفهوم برای تعریف مدل های پیچیده و دسترسی به خواص، متد ها و زیر مجموعه های زیر مفید خواهد بود:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | class rectangle { var print: circle? } class circle { var area = [radius]() var cprint: Int { return area.count } subscript(i: Int) -> radius { get { return area[i] } set { area[i] = newValue } } func circleprint() { print("The number of rooms is \(cprint)") } var rectarea: circumference? } class radius { let radiusname: String init(radiusname: String) { self.radiusname = radiusname } } class circumference { var circumName: String? var circumNumber: String? var street: String? func buildingIdentifier() -> String? { if circumName != nil { return circumName } else if circumNumber != nil { return circumNumber } else { return nil } } } let rectname = rectangle() if let rectarea = rectname.print?.cprint { print("Area of rectangle is \(rectarea)") } else { print("Rectangle Area is not specified") } |
نتیجه ی کامپایل و اجرای مثال فوق :
1 | Rectangle Area is not specified |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | class rectangle { var print: circle? } class circle { var area = [radius]() var cprint: Int { return area.count } subscript(i: Int) -> radius { get { return area[i] } set { area[i] = newValue } } func circleprint() { print("Area of Circle is: \(cprint)") } var rectarea: circumference? } class radius { let radiusname: String init(radiusname: String) { self.radiusname = radiusname } } class circumference { var circumName: String? var circumNumber: String? var circumarea: String? func buildingIdentifier() -> String? { if circumName != nil { return circumName } else if circumNumber != nil { return circumNumber } else { return nil } } } let circname = rectangle() if circname.print?.circleprint() != nil { print("Area of circle is specified)") } else { print("Area of circle is not specified") } |
نتیجه ی کامپایل و اجرای مثال فوق :
1 | Area of circle is not specified |
تابع ()circleprint داخل() circle تعریف شده است.
زیر کلاس با ایجاد یک نمونه با نام ‘circname’ فراخوانی شده است.
تابع اگر شامل همان مقدار باشد آن را بازمی گرداند در غری این صورت با چک کردن کد ‘if circname.print?.circleprint() != nil’ یک پیغام نشان می دهد.
Optional chaining برای تنظیم و بازیابی یک مقدار زیرمجموعه برای اعتبار سنجی استفاده می شود که آیا فراخوانی به این شاخص یک مقدار را نشان می دهد.
‘؟’ قبل از زیرمجموعه های زیر، برای دسترسی به مقدار اختیاری در زیرساخت خاص قرار می گیرد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | class rectangle { var print: circle? } class circle { var area = [radius]() var cprint: Int { return area.count } subscript(i: Int) -> radius { get { return area[i] } set { area[i] = newValue } } func circleprint() { print("The number of rooms is \(cprint)") } var rectarea: circumference? } class radius { let radiusname: String init(radiusname: String) { self.radiusname = radiusname } } class circumference { var circumName: String? var circumNumber: String? var circumarea: String? func buildingIdentifier() -> String? { if circumName != nil { return circumName } else if circumNumber != nil { return circumNumber } else { return nil } } } let circname = rectangle() if let radiusName = circname.print?[0].radiusname { print("The first room name is \(radiusName).") } else { print("Radius is not specified.") } |
نتیجه ی کامپایل و اجرای مثال فوق :
1 | Radius is not specified. |
در برنامه ی فوق مقادیر نمونه برای تابع عضو ‘radiusName’ شناخته نشده است.
از این رو فراخوانی برنامه به تابع فقط بخش دیگری را بازمی گرداند.
در حالی که برای بازگشت مقادیر ما باید برای تعریف مقادیر برای تابع عضویت مشخص کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | class rectangle { var print: circle? } class circle { var area = [radius]() var cprint: Int { return area.count } subscript(i: Int) -> radius { get { return area[i] } set { area[i] = newValue } } func circleprint() { print("The number of rooms is \(cprint)") } var rectarea: circumference? } class radius { let radiusname: String init(radiusname: String) { self.radiusname = radiusname } } class circumference { var circumName: String? var circumNumber: String? var circumarea: String? func buildingIdentifier() -> String? { if circumName != nil { return circumName } else if circumNumber != nil { return circumNumber } else { return nil } } } let circname = rectangle() circname.print?[0] = radius(radiusname: "Diameter") let printing = circle() printing.area.append(radius(radiusname: "Units")) printing.area.append(radius(radiusname: "Meter")) circname.print = printing if let radiusName = circname.print?[0].radiusname { print("Radius is measured in \(radiusName).") } else { print("Radius is not specified.") } |
نتیجه ی کامپایل و اجرای مثال فوق :
1 | Radius is measured in Units. |
در برنامه ی فوق مقادیر نمونه برای تابع عضو ‘radiusName’ شناخته شده است.
از این رو فراخوانی برنامه به تابع مقدار ارزش ها را باز می گرداند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | class rectangle { var print: circle? } class circle { var area = [radius]() var cprint: Int { return area.count } subscript(i: Int) -> radius { get { return area[i] } set { area[i] = newValue } } func circleprint() { print("The number of rooms is \(cprint)") } var rectarea: circumference? } class radius { let radiusname: String init(radiusname: String) { self.radiusname = radiusname } } class circumference { var circumName: String? var circumNumber: String? var circumarea: String? func buildingIdentifier() -> String? { if circumName != nil { return circumName } else if circumNumber != nil { return circumNumber } else { return nil } } } let circname = rectangle() circname.print?[0] = radius(radiusname: "Diameter") let printing = circle() printing.area.append(radius(radiusname: "Units")) printing.area.append(radius(radiusname: "Meter")) circname.print = printing var area = ["Radius": [35, 45, 78, 101], "Circle": [90, 45, 56]] area["Radius"]?[1] = 78 area["Circle"]?[1]-- print(area["Radius"]?[0]) print(area["Radius"]?[1]) print(area["Radius"]?[2]) print(area["Radius"]?[3]) print(area["Circle"]?[0]) print(area["Circle"]?[1]) print(area["Circle"]?[2]) |
نتیجه ی کامپایل و اجرای مثال فوق :
1 2 3 4 5 6 7 | Optional(35) Optional(78) Optional(78) Optional(101) Optional(90) Optional(44) Optional(56) |
مقادیر اختیاری برای زیر مجموعه ها می تواند با اشاره به مقادیر آن زیر مجموعه ها در دسترس باشند.
این می تواند به عنوان [subscript[0], subscript[1 قابل دسترس باشد.
مقادیر پیش فرض زیر مجموعه ها برای ‘radius’ برای اولین بار به صورت [۳۵, ۴۵, ۷۸, ۱۰۱] اختصاص یافت.
و برای ‘Circle’ نیز [۹۰, ۴۵, ۵۶] مقادیر تخصیص یافته است.
سپس مقادیر زیر را به عنوان شعاع [۰] تا ۷۸ و برای دایره نیز از [۱] به ۴۵ تغییر دهید.
زیر کلاس های چند گانه همچنین می تواند با متدها ، خصوصیات و زیر مجموعه های ابر کلاس با استفاده از optional chaining لینک شود.
چندین زنجیره ی اختیاری می تواند لینک شود.
اگر نوع بازیابی اختیاری نیست، زنجیره اختیاری یک مقدار اختیاری را نشان می دهد.
برای مثال اگر رشته در طول optional chaining قرار بگیرد این مقدار ?String را برمی گرداند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | class rectangle { var print: circle? } class circle { var area = [radius]() var cprint: Int { return area.count } subscript(i: Int) -> radius { get { return area[i] } set { area[i] = newValue } } func circleprint() { print("The number of rooms is \(cprint)") } var rectarea: circumference? } class radius { let radiusname: String init(radiusname: String) { self.radiusname = radiusname } } class circumference { var circumName: String? var circumNumber: String? var circumarea: String? func buildingIdentifier() -> String? { if circumName != nil { return circumName } else if circumNumber != nil { return circumNumber } else { return nil } } } let circname = rectangle() if let radiusName = circname.print?[0].radiusname { print("The first room name is \(radiusName).") } else { print("Radius is not specified.") } |
نتیجه ی کامپایل و اجرای مثال فوق :
1 | Radius is not specified. |
در برنامه ی فوق، مقادیر نمونه برای تابع عضو ‘radiusName’ مشخص نشده است.
به این ترتیب، فراخوانی برنامه به تابع فقط بخش دیگری را بازمی گرداند.
در حالی که ما باید برای تعریف مقادیر برای تابع عضو مشخص کنیم.
اگر نوع بازیابی اختیاری نیست، زنجیره اختیاری یک مقدار اختیاری را نشان می دهد.
برای مثال اگر رشته در طول optional chaining قرار بگیرد این مقدار ?String را برمی گرداند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | class rectangle { var print: circle? } class circle { var area = [radius]() var cprint: Int { return area.count } subscript(i: Int) -> radius { get { return area[i] } set { area[i] = newValue } } func circleprint() { print("The number of rooms is \(cprint)") } var rectarea: circumference? } class radius { let radiusname: String init(radiusname: String) { self.radiusname = radiusname } } class circumference { var circumName: String? var circumNumber: String? var circumarea: String? func buildingIdentifier() -> String? { if circumName != nil { return circumName } else if circumNumber != nil { return circumNumber } else { return nil } } } let circname = rectangle() circname.print?[0] = radius(radiusname: "Diameter") let printing = circle() printing.area.append(radius(radiusname: "Units")) printing.area.append(radius(radiusname: "Meter")) circname.print = printing if let radiusName = circname.print?[0].radiusname { print("Radius is measured in \(radiusName).") } else { print("Radius is not specified.") } |
نتیجه ی کامپایل و اجرای مثال فوق :
1 | Radius is measured in Units. |
در برنامه ی فوق، مقادیر نمونه برای تابع عضو ‘radiusName’ مشخص نشده است.
از این رو، برنامه فراخوانی شده به تابع مقدار ارزش ها را باز می گرداند.
Optional chaining برای دسترسی به متدهای تعریف شده ی زیر کلاس نیز استفاده می شود:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | class rectangle { var print: circle? } class circle { var area = [radius]() var cprint: Int { return area.count } subscript(i: Int) -> radius { get { return area[i] } set { area[i] = newValue } } func circleprint() { print("Area of Circle is: \(cprint)") } var rectarea: circumference? } class radius { let radiusname: String init(radiusname: String) { self.radiusname = radiusname } } class circumference { var circumName: String? var circumNumber: String? var circumarea: String? func buildingIdentifier() -> String? { if circumName != nil { return circumName } else if circumNumber != nil { return circumNumber } else { return nil } } } let circname = rectangle() if circname.print?.circleprint() != nil { print("Area of circle is specified)") } else { print("Area of circle is not specified") } |
نتیجه ی کامپایل و اجرای مثال فوق :
1 | Area of circle is not specified |
مفهوم زنجیره اختیاری در Swift نشان می دهد که برنامه نویس می تواند برای مواردی که بخش های مختلف برنامه از جمله کلاس ها ، زیر مجموعه ها ، توابع و… مقدار تهی یا صفر را برمی گردانند ، دستورالعمل های مناسب را تعریف کند.