مساحة اعلانية

آخر المواضيع

إنشاء Swift استخدام Initialization و Initializer

إنشاء Swift : استخدامInitialization  و  Initializer

قمنا في دروس سابقة بإنشاء تطبيق مهام to-do . في هذا الدرس سنقوم بإعادة هيكلة نموذج البيانات وذلك من خلال إنشاء صنف نموذجي تقليدي.


1. نموذج البيانات

يحتوي نموذج البيانات الذي سنقوم بالتحدث عنه على صنفين وهما Task class  و ToDo class والذي تم توريثه من صنف المهام. وبينما نقوم بإنشاء وإنجاز أصناف النماذج، سنتابع شرح موضوع البرمجية كائنة التوجه object-oriented programming مع Swift .


صنف المهام

سنبدأ أولاً بإنجاز صنف المهام وذلك بإنشاء ملف سويفت جديد من خلال New > File ثم إختيار ملف سويفت Swift File من خلال iOS > Source قم بتسمية الملف الجديد Task.swift ثم اضغط على Create

إنشاء Swift استخدام Initialization  و Initializer
إنشاء Swift استخدام Initialization  و Initializer 


import Foundation
class Task: NSObject {
    var name: String
     
   convenience override init() {
        self.init(name: "New Task")
    }
     
    init(name: String) {
        self.name = name
    }
}

ولأن الطريقة init أيضاً تم تعريفها من خلال الصنف NSObject ، سنحتاج إلى البادئة initialize مع الكلمة الدلالية وقد قمنا بشرح هذه الأمور في دروس سابقة. باستخدام الطريقة init سنقوم باستخدام الطريقةinit (name:)  وتمرير "مهمة جديدة" كقيمة لاسم المعاملة.

تعتبر الطريقة init (name:) نوع آخر من initialize كما أنها توافق على اسم معاملة واحد في النوع String .

ستكون قيمة اسم المعاملة في initialize محددة من خلال اسم الخاصية وهذا كافي للفهم صحيح؟

التعيين والملائمة للعنصر Initializers
ما هي البادئة الملائمة في طريقة init ؟ يوجد نوعان من الأصناف في initializers وهي التعيين والملائمة. لكن لماذا نستخدم ذلك؟ وما هو الإختلاف بين هذين النوعين من الأصناف؟

تعيين initializers: بداية الصنف بشكل تام وذلك يعني بأن كل خاصية لديها قيمة أولية وإذا نظرنا إلى صنف المهمة على سبيل المثال سنشاهد بأن اسم الخاصية تم تعيينه كقيمة في init (name:) كما أن النتيجة بعد التخصيص هي مهمة أولية بشكل تام.
ملائمة initializers: وهي تعتمد على النوع السابق وتقوم بإنشاء صنف كامل أولي ولهذا فإن init في صنف المهام تزيل  init (name:).

البروتوكول NSCoding

على الرغم من أن تنفيذ طبقة المهمة ليست كاملة. وفي وقت لاحق في هذه المقالة، سوف نكتب مجموعة من الحالات todo إلى القرص. هذا ممكن فقط إذا مثيلات الفئة تودو يمكن ترميز وفك الشفرة.


لا تقلق على الرغم من أن هذه ليست علم الصواريخ. نحن بحاجة فقط لجعل طبقات العمل وتودو مطابقة للبروتوكولNSCoding  هذا هو السبب يرث طبقة العمل وتشكل الطبقةNSObject  منذ بروتوكولNSCoding  لا يمكن تنفيذها من قبل الطبقات وراثة مباشرة أو بشكل غير مباشر منNSObject  مثل الطبقة NSObject ، يتم تعريف بروتوكولNSCoding  في إطار المؤسسة.

إعتماد بروتوكول شيء غطيناه بالفعل في هذه السلسلة، ولكن هناك عدد قليل منgotchas  التي أريد أن أشير إليها. دعونا نبدأ نقول للمترجم أن طبقة العمل تتفق مع بروتوكول NSCoding

class Task: NSObject, NSCoding {
    var name: String

    ...
}

تالياً سنحتاج إلى استخدام طريقتين لإعلان عن بروتوكول NSCoding و init (coder:) وencodeWithCoder (_:) 

import Foundation

class Task: NSObject, NSCoding {
    var name: String
     
    @objc required init(coder aDecoder: NSCoder) {
        name = aDecoder.decodeObjectForKey("name") as! String
    }
     
    @objc func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(name, forKey: "name")
    }
     
    convenience override init() {
        self.init(name: "New Task")
    }
     
    init(name: String) {
        self.name = name
    }
}

الصنف ToDo
عند الإستعداد لإنشاء المهام حان الوقت لإنجاز صنف ToDo وذلك بإنشاء ملف سويفت جديد ثم سنقوم بتسميته ToDo.swift . يمكنك مشاهدة كيف يبدو الكود لهذا الصنف.
import Foundation

class ToDo: Task {
    var done: Bool
     
    @objc required init(coder aDecoder: NSCoder) {
        self.done = aDecoder.decodeObjectForKey("done") as! Bool
        super.init(coder: aDecoder)
    }
     
    @objc override func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(done, forKey: "done")
        super.encodeWithCoder(aCoder)
    }
     
    init(name: String, done: Bool) {
        self.done = done
        super.init(name: name)
    }
}

تقوم ToDo باستلام صنف المهام وتقوم على إعلان خاصية متغير من نوع Bool. وأيضاً الطريقتين المطلوبتين وهمت بروتوكول NSCoding المستخدم مع قائمة المهام وأيضاً تقوم على إنشاء init (name:done:) .

وكما في لغة Objective-C تعيد الكلمات الدلالية إلى superclass . والمهمة المطلوبة في هذا المثال وقبل أن تقوم بإلغاء init (name:) في السوبر كلاس كما أن لكل خاصية يتم إعلانها من خلال صنف ToDo كما أنه يحتاج إلى أن يكون مبدئي. 
 
إنشاء Swift استخدام Initialization  و Initializer
إنشاء Swift استخدام Initialization  و Initializer

ونفس الشيء ينطبق على الطريقة init (coder:) حيث نقوم أولاً بتحديد الطريقة المنتهية قبل إلغاء init (coder:) في السوبركلاس. أيضاً لاحظ بأننا قمنا بتخفيض وبالتفاف النص الإجباري لـنتيجة decodeObjectForKey(_:) .

Initializers  و  Inheritance

عند التعامل مع Initializers  و  Inheritanceهناك عدة قوانين يجب تذكرها. تعتبر تلك القوانين سهلة جداً :

· يجب استدعاءInitializer  من superclass . في صنف ToDo ، على سبيل المثال، طريقة init (coder:) تستدعي طريقة  init (coder:) Superclass. وهذا ما يعرف أيضاً بـ: delegating up .

تعد قوانينconvenience Initializers  أكثر تعقيداً بقليل. هنالك قاعدتان يجب تذكرهما:
· يحتاج convenience Initializers إلى استدعاء Initializer آخر من نفس الصنف المعرف بها. في task class ، مثلاً، تعتبر طريقة init إنها convenience initializer وينوب initialization عن initializer آخر، init (name:) في المثال. و هذا ما يعرف بـ: delegating across .
· على الرغم من أنه لا يجب على convenience initializer أن ينوب عن initialization لـ: designated initializer ، إلا أنه يتوجب عليه أن يستدعي designated initializer في مرحلة ما. يعتبر هذا ضروري لبدأ النموذج الذي تتم معالجته بشكل كامل.
بعد الإنتهاء من تطبيق الأصناف النموذجية، يحين الوقت لإعادة إنشاء أصناف ViewController و AddItemViewController . دعونا نبدأ بالصنف اللاحق.

2. إعادة إنشاء AddItemViewController

الخطوة 1: تحديث بروتوكول AddItemViewControllerDelegate

ترتبط التغيرات الوحيدة التي تطرأ على صنف AddItemViewController بـبروتوكول AddItemViewControllerDelegate . في تصريح البروتوكول، يجب تغيير نوع didAddItem من String إلى ToDo ، الصنف النوذجي الذي أنجزناه مسبقاً.

protocol {AddItemViewControllerDelegate
 controller func (controller: (AddItemViewController, didAddItem: ToDo
}


الخطوة 2: تحديث create Action

هذا يعني أنه يتوجب علينا أيضاً تحديث create Action الذي من خلاله نستدعي طريقة delegate . في التطبيق المحدث، نقوم بإبتكار نموذج ToDo  ، ونقله إلى طريقة delegate .










@IBAction func create(sender: AnyObject) {
    let name = self.textField.text
     
    let item = ToDo(name: name, done: false)
     
    if let delegate = self.delegate {
        delegate.controller(self, didAddItem: item)
    }
}

3. إعادة إنشاء ViewController

الخطوة 1: تحديث مواد Property
يتطلب صنف ViewController عملاً أكثر. نحتاج أولاً إلى تغيير نوع مواد property إلى [ToDo]، نظام نماذج ToDo .








var items: [ToDo] = [] {
    didSet {
        let hasItems = items.count > 0
        self.tableView.hidden = !hasItems
        self.messageLabel.hidden = hasItems
    }
}

الخطوة 2: Table View Data Source Methods

و هذا يعني أيضاً أنه علينا إعادة إنشاء بعض الطرق الأخرى، مثل  طريقة cellForRowAtIndexPath (_:) الموضحة في الأسفل. بإعتبار نظام المواد يحتوي الآن على نماذج ToDo ، فإنه من الأسهل التحقق فيما إذا تم وضع إشارة على المادة التي أنجزت. نستخدم معالج Swift الثلاثي المشروط لتحديث نوع table view cell's 
accessory .














func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    // Fetch Item
    let item = self.items[indexPath.row]
     
    // Dequeue Table View Cell
    let tableViewCell = tableView.dequeueReusableCellWithIdentifier("TableViewCell", forIndexPath: indexPath) as! UITableViewCell
     
    // Configure Table View Cell
    tableViewCell.textLabel?.text = item.name
    tableViewCell.accessoryType = item.done ? .Checkmark : .None
     
    return tableViewCell
}

عندما يحذف المستخدم مادة ما، فإننا نحتاج فقط إلى تحديث مواد property من خلال إزالة نماذج ToDo المطابقة. هذا ما هو موضح بتطبيق بطريقة tableView (_:commitEditingStyle:forRowAtIndexPath:) الموضحة أسفلاً:


func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
        // Fetch Item
        let item = self.items[indexPath.row]
         
        // Update Items
        self.items.removeAtIndex(indexPath.row)
         
        // Save State
        self.saveItems()
         
        // Update Table View
        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Right)
    }
}


الخطوة 3: جدولة طرق Delegate

طريقة tableView (_:didSelectRowAtIndexPath تعالج موضوع تحديث حالة المادة عندما ينقر المستخدم على صف ما. يعتبر تطبيق هذه الطريقة UITableViewDelegate أسهل بكثير ويعود هذا الفضل لصنف ToDo .


func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
     
    // Fetch Item
    let item = self.items[indexPath.row]
     
    // Fetch Table View Cell
    let tableViewCell = tableView.cellForRowAtIndexPath(indexPath)
     
    // Update Item
    item.done = !item.done
     
    // Update Table View Cell
    tableViewCell?.accessoryType = item.done ? .Checkmark : .None
     
    // Save State
    self.saveItems()
}

يتم تحديث نموذج ToDo وهذا التغيير موضح بـ: table view . من أجل حفظ الحالة، نقوم باستدعاء saveItems بدلاً من saveCheckedItems .

الخطوة 4: إضافة طرق مواد View Controller Delegate

لأننا نقوم بتحديث بروتوكول  AddItemViewControllerDelegate، فإننا نحتاج أيضاً لتحديث تطبيق ViewController's التابع لهذا البرتوكول. ومع ذلك يعتبر التغيير سهل جداً. نحتاج فقط إلى تحديث شارة الطريقة.


func controller(controller: AddItemViewController, didAddItem: ToDo) {
    // Update Data Source
    self.items.append(didAddItem)
     
    // Save State
    self.saveItems()
     
    // Reload Table View
    self.tableView.reloadData()
     
    // Dismiss Add Item View Controller
    self.dismissViewControllerAnimated(true, completion: nil)
}


الخطو 5: حفظ المواد


pathForItems

بدلاً من تخزين المواد في قاعدة بيانات المستخدم النموذجية، فإننا سنقوم بتخزينها بدليل التطبيقات. قبل أن نقوم بتحديث طرق loadItems و saveItems ، فإننا سنقوم بتطبيق طريقة مساعدة تدعى pathForItems . تعد هذه الطريقة خاصة وترجع بطريق، موقع المواد في دليل التطبيقات.


private func pathForItems() -> String {
    let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
    return documentsDirectory.stringByAppendingPathComponent("items")
}

نقوم أولاً بإحضار الطريق لدليل التطبيقات من خلال استدعاء  NSSearchPathForDirectoriesInDomains (_:_:_:) . لأن هذه الطريقة ترجع بنظام من الخيوط، فإننا نمسك المادة الأولى، و ننشرها بقوة، ثم نحولها إلى String . تتألف القيمة التي نعيدها من pathForItems من الطريق إلى دليل التطبيقات مرتبطة بخيط "المواد".

loadItems

تتغير طريقة loadItems بعض الشيء. نقوم أولاً بتخزين نتيجة pathForItems بطريق ذات تسمية دائمة. ثم نقوم باستخراج المادة المتضمنة بسجلات ذلك الطريق وتحويلها إلى نظام إختياري من  نماذج ToDo . نقوم باستخدام  الربط الإختياري للنشر الإختياري ونسبة إلى مواد ذات تسمية دائمة. في جملة إذا نقوم بنسب القيمة المخزنة في المواد لمواد property . 


private func loadItems() {
    let path = self.pathForItems()
     
    if let items = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? [ToDo] {
        self.items = items
    }
}


saveItems

تعد طريقة saveItems سهلة وقصيرة. نقوم بتخزين نتيجة pathForItems  بطريق ذات تسمية دائمة ونستدعي archiveRootObject (_:toFile:) على NSKeyedArchiver ، مع تمرير مواد الطريق و property . نقوم بطباعة نتيجة العملية على لوحة المراقبة.


private func saveItems() {
    let path = self.pathForItems()
     
    if NSKeyedArchiver.archiveRootObject(self.items, toFile: path) {
        println("Successfully Saved")
    } else {
        println("Saving Failed")
    }
}

الخطوة 6: التنظيف

دعونا ننهي مرحلة اللعب، حذف الكود. ابدأ بإزالة  property checkedItems في الأعلى علماً بأننا لم نعد بحاجة لها. وكنتيجة لذلك، فإنه يمكننا أيضاً إزالة طرق loadCheckedItems و saveCheckedItems ، وكذلك كل مرجع إلى هذه الطرق في صنف ViewController .

قم ببناء وإدارة التطبيق لكي ترى فيما إذا كان كل شيء ما زال يعمل. يقوم نموذج البيانات بجعل كود التطبيقات أسهل و أكثر ثقة. 

أصبحت إدارة المواد في لائحتنا أسهل بكثير الآن وأقل عرضة للخطأ وذلك بفضل صنف ToDo .

الخاتمة
في هذا الدرس، قمنا بإعادة إنشاء نموذج البيانات التابع لتطبيقاتنا. لقد تعلمت أكثر عن البرمجة و inheritance . يعد Instance initialization أساسياً في مفهوم Swift لذا أحرص على فهم ما أخذته في هذا الدرس.

الكــاتــب

    • مشاركة

ليست هناك تعليقات:

جميع الحقوق محفوظة لــ الشبح للمعلوميات 2019 ©