รับทําเว็บไซต์ รับทําseo
 
รับทําเว็บไซต์ รับทําseo
บทความที่น่าสนใจ

บทความ ที่น่าสนใจ

Objective-C : Archiving (ตอนที่1)

    Archiving อาร์ไคฟ์ (archiving) คือกระบวนการในการเก็บข้อมูลโดยการนำอ็อบเจ็กเดียวหรือหลายอ็อบเจ็กมาเข้า สู่กระบวนการจัดเก็บ จากนั้นเราสามารถนำอ็อบเจ็กที่ได้จัดเก็บไว้ กลับมาใช้ได้ด้วยการคืนรูปหรือ restore อาจจะมองได้ว่าการ archiving นั้นเหมือนการนำเอาของต่างๆมาบรรจุลงในกล่อง เมื่อต้องการจะใช้งานเราก็เปิดกล่องและนำเอาของต่างๆที่ต้องการออกมาใช้นั่น เอง

     

    Property list

    การจัดเก็บในรูปแบบแรกที่เราจะได้เรียนรู้กันคือ property list หรือเรียกสั้นๆว่า plist การจัดเก็บอ็อบเจ็กในแบบนี้เป็นวิธีการที่นิยมใช้กับค่า setting และ preference ต่างๆของ application ทั้งใน iOS และ Mac OS เพราะเป็นวิธีที่ง่ายและรวดเร็ว เนื่องจาก plist ไม่ซับซ้อน และการจัดเก็บเป็นไฟล์ xml ความสะดวกของการใช้ plist อีกอย่างก็คือถ้าหากอ็อบเจ็กที่เราต้องการจะจัดเก็บนั้นเป็นคลาส NSNumber , NSString , NSData , NSArray หรือ NSDictionary เราสามารถจะใช้เมธอด writeToFile:atomically: ในการจัดเก็บอ็อบเจ็กได้อย่างง่ายดาย ดังเช่นตัวอย่างโปรแกรมต่อไปนี้

     

    Program 13.1

    main.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    #import <Foundation/Foundation.h>
     
    int main(int argc, const char * argv[])
    {
     
        @autoreleasepool {
     
            NSMutableDictionary* dict = [NSMutableDictionary dictionary];
            NSMutableArray* fruits = [NSMutableArray array];
            [fruits addObject:@"Mango"];
            [fruits addObject:@"Banana"];
            [fruits addObject:@"Apple"];
            [fruits addObject:@"Lemon"];
     
            NSString* filePath = [NSString stringWithFormat:@"%@/data.plist",NSHomeDirectory()];
     
            [dict setObject:fruits forKey:@"Fruits"];
            [dict setObject:[NSNumber numberWithLong:[fruits count]] forKey:@"Count"];
     
            [dict writeToFile:filePath atomically:YES];
     
        }
        return 0;
    }<span id="more-2575"></span>

     

    จากโปรแกรม เราได้ประกาศดิกชันนารีซึ่งมีสมาชิกเป็นรายชื่อผลไม้ และจำนวนของรายชื่อผลไม้ จากนั้นได้เรียกเมธอด writeToFile:atomically: เพื่อเขียนไฟล์ plist เมื่อโปรแกรมเสร็จสิ้นการทำงานเราจะได้ไฟล์ plist ที่ประกอบด้วยข้อมูลของอ็อบเจ็กที่ได้จัดเก็บไป เมื่อเราเปิดไฟล์ด้วยโปรแกรม text editor ข้อมูลใน plist เป็นเพียง xml ซึ่งมีลักษณะดังนี้

     

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Count</key>
        <integer>4</integer>
        <key>Fruits</key>
        <array>
            <string>Mango</string>
            <string>Banana</string>
            <string>Apple</string>
            <string>Lemon</string>
        </array>
    </dict>
    </plist>

     

    เมื่อพิจารณาจากไฟล์ plist จะเห็นว่าดิกชันนารีจะถูกจัดเก็บในรูปแบบคู่ของคีย์และข้อมูลสลับกันไป เช่น <key>Count</key> และ <interger>4</interger> และในกรณีข้อมูลเป็นอ็อบเจ็กที่เป็น collection อย่างเช่น array ก็จะมีแท็ก <array> และข้อมูลภายในอาร์เรย์นั้นๆ  เมื่อพิจารณาไฟล์ plist จะพบว่าข้อจำกัดอย่างหนึ่งของการใช้ dictionary และ plist คือเราไม่สามารถใช้ key เป็นอ็อบเจ็กอื่นได้นอกจากสตริง ในกรณีที่ดิกชันนารีมีคีย์เป็นอย่างอื่น โปรแกรมจะไม่สามารถบันทึกไฟล์ได้  หลังจาก archiving แล้วเมื่อเราต้องการจะ restore ก็สามารถทำได้ด้วยการเรียกเมธอด dictinaryWithContentOfFile: หรือหากเรา archive อ็อบเจ็กที่เป็นอาร์เรย์ ก็สามารถเรียก arrayWithContentOfFile: ใช้งานได้ เราจะเขียนโปรแกรมอีกตัวเพื่อ restore อ็อบเจ็กที่ได้จัดเก็บไป

     

    Program 13.2

    main.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    #import <Foundation/Foundation.h>
     
    int main(int argc, const char * argv[])
    {
     
        @autoreleasepool {
            NSString* file = [NSString stringWithFormat:@"%@/data.plist",NSHomeDirectory()];
            NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile:file];
     
            NSArray* keys = [dict allKeys];
            for (NSString* key in keys)
     
                NSLog(@"key:%@ obj:%@",key, [dict objectForKey:key]);
     
        }
        return 0;
    }

     

    Program 13.2 Output

    key:Fruits obj:(
    Mango,
    Banana,
    Apple,
    Lemon
    )
    key:Count obj:4

     

    จาก Output ของโปรแกรม 13.2 ที่ได้เขียนไปแสดงให้เห็นว่าโปรแกรมสามารถ restore อ็อบเจ็กจาก plist ได้อย่างถูกต้อง และในการสร้างไฟล์ plist เราไม่จำเป็นต้องเขียนโค้ดเพื่อสร้างไฟล์ plist เราสามารถสร้างได้โดยตรงจาก file template ดังที่แสดงในรูป


    plist

     

    NSKeyedArchiver

    ถึงแม้ว่า plist นั้นใช้งานง่าย แต่ก็ยังมีข้อจำกัดหลายอย่าง เช่น ไม่สามารถจัดเก็บอ็อบเจ็กนอกเหนือจาก Foundation ได้ นอกจากนี้เมื่อเก็บข้อมูลเป็น plist ข้อมูลยังถูกแก้ไขได้โดยง่ายเพราะว่า plist เป็นเพียง xml ในกรณีที่เราต้องการจะปกปิดข้อมูลให้เป็นความลับไม่สามารถทำได้เลย ถึงแม้ว่าเราจะปกปิดข้อมูลใน plist ด้วยการเก็บเป็นไฟล์ไบนารี่ได้โดยผ่านคลาส NSPropertyListSerialization แต่ก็มีเครื่องมือที่สามารถเปลี่ยนไฟล์กลับเป็น xml ได้ ทางออกของปัญหานี้คือการใช้คลาส NSKeyedArchiver โดยคลาสนี้จะทำหน้าที่เปลี่ยนอ็อบเจ็กที่ต้องการจะจัดเก็บให้เป็นไบนารี

     

    การเปลี่ยนอ็อบเจ็กที่ต้องการให้เป็นไบนารี่ไฟล์ทำได้ด้วยการเรียกคลา สเมธอด archiveRootObject:toFile: ดังเช่นโปรแกรมตัวอย่างต่อไปนี้

     

    โปรแกรม 13.3 คล้ายกับโปรแกรม 13.1 แต่สิ่งที่แตกต่างกันคือ เมื่อเปิดไฟล์ข้อมูลด้วย text editor จะไม่สามารถอ่านทำความเข้าใจได้ และหลังจากที่ได้ archive อ็อบเจ็กเรียบร้อย ในการ restore อ็อบเจ็กกลับคืนมา อ่านข้อมูลเราสามารถใช้เมธอด unarchiveObjectWithFile: ดังเช่นโปรแกรมตัวอย่าง

     

    Program 13.3

    main.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    #import <Foundation/Foundation.h>
     
    int main(int argc, const char * argv[])
    {
     
        @autoreleasepool {
     
            NSMutableDictionary* dict = [NSMutableDictionary dictionary];
            NSMutableArray* fruits = [NSMutableArray array];
            [fruits addObject:@"Mango"];
            [fruits addObject:@"Banana"];
            [fruits addObject:@"Apple"];
            [fruits addObject:@"Lemon"];
     
            [dict setObject:fruits forKey:@"Fruits"];
            [dict setObject:[NSNumber numberWithLong:[fruits count]] forKey:@"Count"];
     
            NSString* file = [NSString stringWithFormat:@"%@/data.arch",NSHomeDirectory()];
            [NSKeyedArchiver archiveRootObject:dict toFile:file];        
     
        }
        return 0;
    }

     

    โปรแกรม 13.3 คล้ายกับโปรแกรม 13.1 แต่สิ่งที่แตกต่างกันคือ เมื่อเปิดไฟล์ข้อมูลด้วย text editor จะไม่สามารถอ่านทำความเข้าใจได้ และหลังจากที่ได้ archive อ็อบเจ็กเรียบร้อย ในการ restore อ็อบเจ็กกลับคืนมา อ่านข้อมูลเราสามารถใช้เมธอด unarchiveObjectWithFile: ดังเช่นโปรแกรมตัวอย่าง

     

    Program 13.4

    main.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    #import <Foundation/Foundation.h>
     
    int main(int argc, const char * argv[])
    {
     
        @autoreleasepool {
     
            NSString* file = [NSString stringWithFormat:@"%@/data.arch",NSHomeDirectory()];
            NSMutableDictionary* dict;
            dict = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
     
            NSArray* keys = [dict allKeys];
            for (NSString* key in keys)
     
                NSLog(@"key:%@ obj:%@",key, [dict objectForKey:key]);
     
        }
        return 0;
    }

     

    Program 13.4 Output

    key:Count obj:4
    key:Fruits obj:(
    Mango,
    Banana,
    Apple,
    Lemon
    )

     

    โปรแกรมที่ได้เขียนไปนั้นสามารถจัดเก็บดิกชันนารีตามที่เราต้องการได้ แต่โปรแกรมที่ได้เขียนยังมีข้อจำกัดอยู่ เพราะถ้าหากเราใช้เมธอด arvhiveRootObject:toFile: กับคลาสที่เราเขียนขึ้นมาจะเกิด error เพราะคลาส NSKeyedArchver นั้นไม่รู้จักคลาสที่เรากำหนด ดังเช่นโปรแกรมต่อไปนี้

     

    #import <Foundation/Foundation.h>
    #import "Product.h"
     
    int main(int argc, const char * argv[])
    {
     
        @autoreleasepool {
     
            Product* apple = [[Product alloc] init];
            [apple setName:@"Apple" andPrice:100];
     
            NSString* file = [NSString stringWithFormat:@"%@/data.arch",NSHomeDirectory()];
            [NSKeyedArchiver archiveRootObject:apple toFile:file];
     
            [apple release];
     
        }
        return 0;
    }

     

    Program Output

    -[Product encodeWithCoder:]: unrecognized selector sent to instance 0x10010a650
    *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[Product encodeWithCoder:]: unrecognized selector sent to instance 0x10010a650′

     

    ข้อความ error ที่เกิดขึ้นได้บอกกับเราว่าคลาส Product ไม่มีรองรับเมธอด encodeWithCoder: สาเหตุเป็นเพราะว่าการที่คลาส NSKeyedArchvier เปลี่ยนอ็อบเจ็กให้เป็นไฟล์ จะเข้าสู่กระบวนการที่เรียกว่า Keyed Archive ซึ่งเป็นวิธีการเก็บข้อมูลโดยการใช้ key เข้ารหัสและการถอดรหัส ส่วนคลาสที่เป็นคลาสพื้นฐานอย่าง NSDictionary , NSString , NSData หรือคลาส NSArray นั้นมีเมธอดในการเข้ารหัสและถอดรหัสอยู่แล้ว เราจึงไม่ต้องเขียนเมธอดนี้ แต่สำหรับคลาสที่เขียนขึ้นเราต้องเป็นผู้ที่เขียนเมธอดในการเข้ารหัสและถอด รหัส

     

บทความที่น่าสนใจ

บทความ ล่าสุด

บทความ ความรู้ด้านไอที, คอมพิวเตอร์ Techonlogy, Gadget, ความรู้เกี่ยวกับคอมพิวเตอร์ กับทาง SoftMelt.com