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

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

Objective-C : More on classes & Property (ตอนที่3)

    Holding other objects

    ที่ผ่านมาเราได้เขียนคลาสที่มีตัวแปร ต่างๆมากมาย แต่ก็เป็นเพียงแค่ตัวแปรแบบธรรมดาทั่วๆไปเช่น ทศนิยม จำนวนเต็ม แต่ในความเป็นจริงแล้ว คลาสต่างๆมักจะมีสามาชิกเป็นออบเจ็กต์ด้วย เราจะสร้างโปรเจคขึ้นมาใหม่โดยใช้คลาสจากโปรแกรม 7.3 ที่ผ่านมาและเพิ่มคลาส harddisk เข้าไปในโปรแกรมของเรา โดยมีโค้ดดังต่อไปนี้

     

    Program 7.4

    Harddisk.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    #import <Foundation/Foundation.h>
    #import "ElectronicDevice.h"
     
    @interface Harddisk : ElectronicDevice
    {
        int capacity;
    }
    @property int capacity;
    -(void) printCapacity;
    @end

     

    Harddisk.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    #import "Harddisk.h"
     
    @implementation Harddisk
    @synthesize capacity;
    -(void) printCapacity
    {
        NSLog(@"Capacity %d GB.",capacity);
    }
    @end

     

    และให้แก้ไขโค้ดของคลาส computer ดังนี้

     

    Computer.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    @interface Computer : ElectronicDevice
    {
        int speed;
        Harddisk *externalHarddisk;
    }
    @property int speed;
    -(void) printDetails;
    -(void) setExternalHarddisk:(Harddisk*) disk;
    @end

     

    สิ่งที่ได้แก้ไขไปก็คือเพิ่มสมาชิกใหม่ชื่อ externalHarddisk ซึ่งเป็นคลาส Harddisk การประกาศ externalHarddisk จะเห็นว่าเป็นตัวแปรแบบ Harddisk* หรือ pointer นั่นเอง นอกจากนี้เพิ่มเมธอด setExternalHarddisk โดยรับพารามิเตอร์ pointer เข้ามาเช่นกัน

     

    Computer.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    #import "Computer.h"
     
    @implementation Computer
    @synthesize speed;
    -(void) printDetails
    {
        [self printPrice];
        NSLog(@"speed %d mHz",speed);
        [externalHarddisk printCapacity];
    }
     
    -(void) setExternalHarddisk:(Harddisk *)disk
    {
        externalHarddisk = disk;
    }
     
    @end

     

    เมื่อพิจาณาจากโค้ด

     

    -(void) setExternalHarddisk:(Harddisk *)disk
    {
        externalHarddisk = disk;
    }

     

    ตัวแปร externalHarddisk นั้นเป็นเพียงแค่ pointer ที่ชี้ไปยัง ExternalHarddisk เท่านั้นเอง

    สุดท้ายคือแก้ไขตัวโปรแกรมหลักดังนี้


    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
    25
    
    import <Foundation/Foundation.h>
    #import "Computer.h"
    #import "Harddisk.h"
     
    int main (int argc, const char *argv[])
    {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
     
        Computer *computer = [[Computer alloc] init];
        Harddisk *extHDD = [[Harddisk alloc] init];
        extHDD.capacity = 240;
     
        NSLog(@"Computer");
        computer.price = 12000;
        computer.speed = 450;
        [computer setExternalHarddisk:extHDD];
        [computer printDetails];
     
        [extHDD release];
        [computer release];
     
        [pool drain];
        return 0;
     
    }

     

    เมนโปรแกรมประกาศ computer และ extHDD จากนั้นก็กำหนดค่าความจุของ extHDD เท่ากับ 240 พร้อมกับกำหนดราคาและความเร็วให้กับ computer จากนั้นในบรรทัดที่ 17 เรียกเมธอด setExternalHarddisk โดยส่งพารามิเตอร์ extHDD ไปด้วย ดังนั้นแล้วตัวแปร externalHarddisk ของ computer ก็คือ exterHDD นั่นเอง เมื่อให้โปรแกรมทำงานก็จะได้ผลลัพธ์ดังนี้

     

    Program 7.4 Output
    Computer
    price 12000.0
    speed 450 mHz
    capacity 240 GB

     

    ถึง แม้โปรแกรมจะทำงานได้ไม่มีปัญหาใดๆ แต่เมื่อไหร่ก็ตามที่ออบเจ็กต์ extHDD ถูกทำลายปัญหาก็จะเกิดขึ้นทันที เพราะว่าตัวแปร externalHarddisk ในคลาสของ Computer นั้นไม่มีทางรู้ได้เลยว่าออบเจ็กต์นั้นได้ถูกทำลายลงไปแล้ว  เช่นสมมติว่าเราแก้ไขเปลี่ยนลำดับโค้ดให้ extHDD คืนหน่วยความจำ หลังจากนั้นให้ computer เรียกเมธอด printDetails ดังตัวอย่าง

     

     

    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
    
    #import <Foundation/Foundation.h>
    #import "Computer.h"
    #import "Harddisk.h"
     
    int main (int argc, const char *argv[])
    {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
     
        Computer *computer = [[Computer alloc] init];
        Harddisk *extHDD = [[Harddisk alloc] init];
        extHDD.capacity = 240;
     
        NSLog(@"Computer");
        computer.price = 12000;
        computer.speed = 450;
        [computer setExternalHarddisk:extHDD];
     
        [extHDD release];
     
        [computer printDetails];
     
        [computer release];
     
        [pool drain];
        return 0;
     
    }

     

    เมื่อคอมไพล์และรันโปรแกรมก็จะเกิดความผิดพลาด Bad Acesss ทันที เพราะเราได้ทำลาย extHDD โดยการคืนหน่วยความจำไปแล้ว แต่ในเมธอด printDetains ของ computer ยังเรียกใช้อ็อบเจ็กต์นั้นอยู่ จึงเกิด error ขึ้นดังรูป

     

    chapter7_1

     

    แล้ว เราจะแก้ปัญหาได้อย่างไร คำตอบง่ายๆก็คือให้คลาส computer บอกกับ extHDD ว่าใช้งานอยู่ โดยใช้ retain นั่นเอง เราจะแก้ไขเมธอด setExternalHarddisk ให้เป็นดังนี้

     

    -(void) setExternalHarddisk:(Harddisk *)disk
    {
        if( disk != nil)
            exterHarddisk = [disk retain];
    }

     

    เท่านี้เราก็สามารถป้องกันปัญหาได้แล้ว นอกจากนี้เราได้เพิ่มโค้ดไว้ตรวจสอบด้วยกว่าออบเจ็กต์นั้นไม่เป็น nil งานของเรายังไม่จบเพียงเท่านี้ เพราะอย่างที่ได้เคยอธิบายเรื่องของหน่วยความจำว่าการ retain เป็นการเพิ่มค่า counter ดังนั้นเมื่อเลิกใช้งานเราก็ต้อง release เพื่อลด counter ฉะนั้นแล้วเราก็จะเพิ่มเมธอดเพื่อ release หลังจากเลิกใช้งานแล้ว

     

    -(void) releaseExternalHarddisk
    {
        [exterHarddisk release];
        exterHarddisk = nil;
    }

     

    จากนั้นเราก็แก้ไขโปรแกรมใหม่ ก็จะมีโค้ดดังนี้

     

         ...

        [computer setExternalHarddisk:extHDD];
     
        [extHDD release];
     
        [computer printDetails];
        [computer releaseExternalHarddisk];
     
        [computer release];
        
    ...

     

    เมื่อแก้โค้ดเสร็จหากคอมไพล์และลองรันโปรแกรมใหม่ก็จะ เห็นว่าโปรแกรมทำงานได้ถูกต้องเรียบร้อยไม่เกิด error ใดๆ เปรียบเทียบแล้วก็เหมือนกับเราเอาแฟลชไดร์ฟไปเสียบกับเครื่องคอมพิวเตอร์ หลังจากนั้นระบบปฎิบัติการก็จะเตรียมสิ่งต่างๆเพื่อใช้ในการเชื่อมต่อ เมื่อใช้งานเสร็จก็ต้องบอกให้ระบบปฎิบัติการรับรู้ด้วย เพื่อระบบจะได้ลบสิ่งต่างๆที่ได้สร้างไว้น่ันเอง

     

    Owning objects

    สมมติว่าแก้ไขเมธอด setExternalHarddisk โดยเพิ่มปริมาณความจุของ externalHarddisk ของคลาส Computer ให้เป็นสองเท่า ดังนี้

     

    -(void) setExternalHarddisk:(Harddisk *)disk
    {
        if( disk != nil)
            externalHarddisk = [disk retain];
        
    externalHarddisk.capacity *= 2;
    }

     

    และส่วนโปรแกรมหลักก็เปลี่ยนโค้ดให้เป็นดังนี้

     

    Program 7.4 ( Modified )

    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
    
    #import <Foundation/Foundation.h>
    #import "Computer.h"
    #import "Harddisk.h"
     
    int main (int argc, const char *argv[])
    {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
     
        Computer *computer = [[Computer alloc] init];
        Harddisk *extHDD = [[Harddisk alloc] init];
        extHDD.capacity = 240;
     
        NSLog(@"Computer");
        computer.price = 12000;
        computer.speed = 450;
        [computer setExternalHarddisk:extHDD];
        [computer printDetails];
     
        NSLog(@"nExternal Hard disk");
        [extHDD printCapacity];
     
        [extHDD release];
        [computer release];
     
        [pool drain];
        return 0;
     
    }

     

    Program 7.4 (Modified) Output
    Computer
    price 12000.0
    speed 450 mHz
    capacity 480 GB

    External Hard disk
    capacity 480 GB

     

    เมื่อ พิจารณาจากโปรแกรม เราได้เปลี่ยนความจุของ externalHarddisk ในเมธอด setExternalHarddisk เท่านั้น แต่ผลลัพธ์ของโปรแกรมกลับกลายเป็นว่า capacity นั้นได้เปลี่ยนเป็น 480 ทั้ง computer และ extHDD ? คำตอบก็คือ เพราะว่า externalHarddisk นั้นเป็นเพียงแค่ pointer ที่ชี้ไปยังออบเจ็กต์อื่น ดังนั้นเมื่อเราอ้างถึง externalHarddisk ก็เหมือนกับอ้างไปยังออบเจ็กต์ตัวนั้นตรงๆ การเปลี่ยนแปลงค่าก็เท่ากับเปลี่ยนค่าของสิ่งนั้นโดยตรง ถ้าหากเราจะเลี่ยงปัญหานี้ก็สามารถทำได้โดยการ สร้างออบเจ็กต์เป็นของตัวเองเลยดีกว่า ดังเช่นโค้ดตัวอย่าง

     

    -(void) initHarddisk
    {
        externalHarddisk = [[Harddisk alloc] init];
    }
     
    -(void) setExternalHarddisk:(Harddisk *)disk
    {
        if( disk != nil)
            externalHarddisk = disk.capacity * 2;     
    }

     

    จากตัวอย่างเราได้เพิ่มเมธอด initHarddisk เพื่อใช้ในการสร้างออบเจ็กต์ externalHarddisk ที่เหลือก็คือแก้ไขส่วนของโปรแกรมหลักโดยเรียก initHarddisk หลังจากการประกาศออบเจ็กต์ computer

     

       ... 
       Computer *computer = [[Computer alloc] init];
       [computer initHarddisk];
       ...

     

    หลังจากนั้นเมื่อคอมไพล์และรันโปรแกรมที่ได้แก้ไขไปโปรแกรมก็จะมีผลลัพธ์ดังนี้

    Program 7.4 (Fix) Output
    Computer
    price 12000.0
    speed 450 mHz
    capacity 480 GB

    External Hard disk
    capacity 240 GB

     

     

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

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

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