วิธีที่ดีที่สุดในการสื่อสารระหว่างตัวควบคุมมุมมองคืออะไร


165

ด้วยความใหม่ในวัตถุประสงค์ -c, cocoa และ iPhone dev โดยทั่วไปฉันมีความปรารถนาอย่างแรงกล้าที่จะใช้ภาษาและกรอบการทำงานให้เกิดประโยชน์สูงสุด

หนึ่งในแหล่งข้อมูลที่ฉันใช้คือบันทึกย่อของชั้นเรียน CS193P ของ Stanford ที่พวกเขาทิ้งไว้บนเว็บ ประกอบด้วยบันทึกการบรรยายงานที่มอบหมายและรหัสตัวอย่างและเนื่องจากหลักสูตรได้รับการพัฒนาโดย Apple dev's ฉันจึงคิดว่าเป็น "จากปากม้า" แน่นอน

เว็บไซต์ชั้นเรียน:
http://www.stanford.edu/class/cs193p/cgi-bin/index.php

การบรรยาย 08 เกี่ยวข้องกับการมอบหมายให้สร้างแอปที่ใช้ UINavigationController ซึ่งมี UIViewControllers หลายรายการที่ถูกส่งไปยังสแต็ก UINavigationController นั่นคือวิธีที่ UINavigationController ทำงาน นั่นเป็นเหตุผล อย่างไรก็ตามมีคำเตือนที่เข้มงวดในสไลด์เกี่ยวกับการสื่อสารระหว่าง UIViewControllers ของคุณ

ฉันจะอ้างอิงจากเรื่องร้ายแรงของสไลด์:
http://cs193p.stanford.edu/downloads/08-NavigationTabBarControllers.pdf

หน้า 16/51:

วิธีที่จะไม่แบ่งปันข้อมูล

  • ตัวแปรทั่วโลกหรือซิงเกิลตัน
    • ซึ่งรวมถึงตัวแทนแอปพลิเคชันของคุณ
  • การพึ่งพาโดยตรงทำให้รหัสของคุณใช้ซ้ำได้น้อยลง
    • และยากต่อการดีบัก & ทดสอบ

ตกลง. ฉันลงกับที่ อย่าสุ่มโยนวิธีการทั้งหมดของคุณที่จะใช้สำหรับการสื่อสารระหว่าง viewcontroller ไปยังผู้แทนแอพของคุณและอ้างอิงอินสแตนซ์ viewcontroller ในวิธีการมอบหมายแอพ พอใช้ '

อีกเล็กน้อยเราได้รับสไลด์นี้บอกเราว่าเราควรทำอย่างไร

หน้า 18/51:

แนวทางปฏิบัติที่ดีที่สุดสำหรับการไหลของข้อมูล

  • คิดออกว่าสิ่งที่ต้องมีการสื่อสาร
  • กำหนดพารามิเตอร์อินพุตสำหรับตัวควบคุมมุมมองของคุณ
  • สำหรับการสื่อสารสำรองลำดับชั้นให้ใช้การเชื่อมต่อหลวม
    • กำหนดอินเทอร์เฟซทั่วไปสำหรับผู้สังเกตการณ์ (เช่นการมอบหมาย)

จากนั้นสไลด์นี้จะตามด้วยสิ่งที่ดูเหมือนจะเป็นตัวยึดตำแหน่งซึ่งอาจารย์จะแสดงให้เห็นถึงแนวปฏิบัติที่ดีที่สุดโดยใช้ตัวอย่างกับ UIImagePickerController ฉันหวังว่าวิดีโอจะพร้อมใช้งาน! :(

ตกลงดังนั้น ... ฉันกลัวว่า objc-fu ของฉันไม่แข็งแรงนัก ฉันยังสับสนเล็กน้อยจากบรรทัดสุดท้ายในข้อความข้างต้น ฉันได้ทำส่วนแบ่งที่ยุติธรรมของ googling เกี่ยวกับเรื่องนี้และฉันพบสิ่งที่ดูเหมือนจะเป็นบทความที่ดีพูดคุยเกี่ยวกับวิธีการต่าง ๆ ของเทคนิคการสังเกต / ประกาศ:
http://cocoawithlove.com/2008/06/five-approaches-to -listening-observing.html

วิธีที่ # 5 ระบุผู้ได้รับมอบหมายเป็นวิธี! ยกเว้น ... วัตถุสามารถตั้งผู้รับมอบสิทธิ์ได้ครั้งละหนึ่งคนเท่านั้น ดังนั้นเมื่อฉันมีการสื่อสารผ่านผู้ควบคุมวิวเวอร์หลายคนฉันต้องทำอย่างไร

ตกลงนั่นคือการตั้งค่าแก๊ง ฉันรู้ว่าฉันสามารถทำวิธีการสื่อสารของฉันในตัวแทนแอพได้อย่างง่ายดายโดยการอ้างอิงของอินสแตนซ์ตัวควบคุมหลายตัวในแอพของฉัน แต่ฉันต้องการทำสิ่งที่ถูกต้องทาง

โปรดช่วยฉัน "ทำสิ่งที่ถูกต้อง" โดยตอบคำถามต่อไปนี้:

  1. เมื่อฉันพยายามที่จะผลักดัน viewcontroller ใหม่บนสแต็ก UINavigationController ที่ควรจะทำการผลักดันนี้ ซึ่งระดับ / แฟ้มในรหัสของฉันเป็นสถานที่ที่ถูกต้องหรือไม่
  2. เมื่อฉันต้องการที่จะส่งผลกระทบต่อชิ้นส่วนบางส่วนของข้อมูล (ค่าของ Ivar) หนึ่งใน UIViewControllers ของฉันเมื่อฉันอยู่ในที่แตกต่างกัน UIViewController สิ่งที่เป็น "สิทธิ" วิธีที่จะทำเช่นนี้?
  3. ให้ว่าเราสามารถมีเพียงชุดผู้ร่วมประชุมในช่วงเวลาหนึ่งในวัตถุสิ่งที่จะดำเนินการลักษณะเช่นเมื่ออาจารย์กล่าวว่า"กำหนดอินเตอร์เฟซทั่วไปสำหรับผู้สังเกตการณ์ (เช่นคณะผู้แทน)" ตัวอย่าง pseudocode จะเป็นประโยชน์อย่างมากที่นี่ถ้าเป็นไปได้

บางส่วนได้รับการกล่าวถึงในบทความนี้จาก Apple - developer.apple.com/library/ios/#featuredarticles/ …
James Moore

เพียงแค่คำพูดอย่างรวดเร็ว: วิดีโอสำหรับชั้นสแตนฟอ CS193P นี้สามารถใช้ได้ผ่านทาง iTunes U. ล่าสุด (2012-13) สามารถมองเห็นที่itunes.apple.com/us/course/coding-together-developing/...และผมคาดว่า วิดีโอและสไลด์ในอนาคตจะมีการประกาศในcs193p.stanford.edu
Thomas Watson

คำตอบ:


224

นี่เป็นคำถามที่ดีและยอดเยี่ยมมากที่ได้เห็นว่าคุณกำลังทำวิจัยนี้และดูเหมือนจะกังวลกับการเรียนรู้วิธี "ทำให้ถูกต้อง" แทนที่จะแค่แฮ็คมันด้วยกัน

ครั้งแรกฉันเห็นด้วยกับคำตอบก่อนหน้าซึ่งมุ่งเน้นไปที่ความสำคัญของการวางข้อมูลในวัตถุแบบจำลองตามความเหมาะสม (ตามรูปแบบการออกแบบ MVC) โดยปกติคุณต้องการหลีกเลี่ยงการใส่ข้อมูลสถานะไว้ในคอนโทรลเลอร์เว้นแต่ว่าเป็นข้อมูล "งานนำเสนอ" อย่างเคร่งครัด

ประการที่สองดูหน้า 10 ของงานนำเสนอของ Stanford สำหรับตัวอย่างของวิธีการส่งคอนโทรลเลอร์ไปยังคอนโทรลเลอร์ระบบนำทางโดยทางโปรแกรม สำหรับตัวอย่างของวิธีการทำสิ่งนี้ "เห็นได้" โดยใช้เครื่องมือสร้างอินเตอร์เฟสลองดูที่บทช่วยสอนนี้

ข้อที่สามและที่สำคัญที่สุดโปรดทราบว่า "แนวปฏิบัติที่ดีที่สุด" ที่กล่าวถึงในงานนำเสนอของ Stanford นั้นง่ายต่อการเข้าใจถ้าคุณคิดถึงพวกเขาในบริบทของรูปแบบการออกแบบ "การพึ่งพา" โดยสรุปแล้วนั่นหมายความว่าคอนโทรลเลอร์ของคุณไม่ควร "ค้นหา" วัตถุที่ต้องการทำงาน (เช่นอ้างอิงตัวแปรทั่วโลก) คุณควร "ฉีด" การพึ่งพาเหล่านั้นลงในคอนโทรลเลอร์เสมอ (เช่นส่งผ่านวัตถุที่ต้องการผ่านวิธีการ)

ถ้าคุณทำตามรูปแบบการฉีดพึ่งพาตัวควบคุมของคุณจะเป็นแบบแยกส่วนและนำมาใช้ใหม่ และถ้าคุณคิดว่าผู้นำเสนอของสแตนฟอร์ดมาจากที่ใด (เช่นพนักงานของ Apple คือการสร้างคลาสที่สามารถนำกลับมาใช้ใหม่ได้ง่าย) การใช้ซ้ำและโมดุลเป็นสิ่งที่มีความสำคัญสูง แนวทางปฏิบัติที่ดีที่สุดทั้งหมดที่พวกเขากล่าวถึงการแบ่งปันข้อมูลเป็นส่วนหนึ่งของการฉีดที่ต้องพึ่งพา

นั่นคือส่วนสำคัญของการตอบสนองของฉัน ฉันจะรวมตัวอย่างของการใช้รูปแบบการฉีดพึ่งพากับตัวควบคุมด้านล่างในกรณีที่เป็นประโยชน์

ตัวอย่างของการใช้การฉีดอ้างอิงกับตัวควบคุมมุมมอง

สมมติว่าคุณกำลังสร้างหน้าจอที่มีหนังสือหลายเล่มอยู่ในรายการ ผู้ใช้สามารถเลือกหนังสือที่ต้องการซื้อแล้วแตะปุ่ม "ชำระเงิน" เพื่อไปที่หน้าจอชำระเงิน

ในการสร้างสิ่งนี้คุณอาจสร้างคลาส BookPickerViewController ที่ควบคุมและแสดงวัตถุ GUI / มุมมอง จะรับข้อมูลหนังสือทั้งหมดได้จากที่ใด สมมติว่ามันขึ้นอยู่กับวัตถุ BookWarehouse สำหรับสิ่งนั้น ดังนั้นตอนนี้คอนโทรลเลอร์ของคุณจึงเป็นข้อมูลการรวบรวมข้อมูลระหว่างแบบจำลองวัตถุ (BookWarehouse) และวัตถุ GUI / มุมมอง ในคำอื่น ๆ BookPickerViewController DEPENDS บนวัตถุ BookWarehouse

อย่าทำสิ่งนี้:

@implementation BookPickerViewController

-(void) doSomething {
   // I need to do something with the BookWarehouse so I'm going to look it up
   // using the BookWarehouse class method (comparable to a global variable)
   BookWarehouse *warehouse = [BookWarehouse getSingleton];
   ...
}

แต่ควรพึ่งพาการพึ่งพาเช่นนี้:

@implementation BookPickerViewController

-(void) initWithWarehouse: (BookWarehouse*)warehouse {
   // myBookWarehouse is an instance variable
   myBookWarehouse = warehouse;
   [myBookWarehouse retain];
}

-(void) doSomething {
   // I need to do something with the BookWarehouse object which was 
   // injected for me
   [myBookWarehouse listBooks];
   ...
}

เมื่อพวก Apple กำลังพูดถึงการใช้รูปแบบการมอบหมายเพื่อ "สื่อสารสำรองลำดับชั้น" พวกเขายังคงพูดถึงการฉีดพึ่งพา ในตัวอย่างนี้ BookPickerViewController ควรทำอย่างไรเมื่อผู้ใช้เลือกหนังสือของตนและพร้อมที่จะเช็คเอาท์ นั่นไม่ใช่งานของมันเลย ควรมอบสิทธิ์ที่ทำงานกับวัตถุอื่น ๆ ซึ่งหมายความว่ามันขึ้นอยู่กับวัตถุอื่น ดังนั้นเราจึงอาจปรับเปลี่ยนวิธีการเริ่มต้น BookPickerViewController ดังนี้:

@implementation BookPickerViewController

-(void) initWithWarehouse:    (BookWarehouse*)warehouse 
        andCheckoutController:(CheckoutController*)checkoutController 
{
   myBookWarehouse = warehouse;
   myCheckoutController = checkoutController;
}

-(void) handleCheckout {
   // We've collected the user's book picks in a "bookPicks" variable
   [myCheckoutController handleCheckout: bookPicks];
   ...
}

ผลลัพธ์สุทธิของทั้งหมดนี้คือคุณสามารถมอบคลาส BookPickerViewController ของคุณ (และวัตถุ GUI / มุมมองที่เกี่ยวข้อง) ให้ฉันและฉันสามารถใช้งานได้อย่างง่ายดายในแอปพลิเคชันของฉันเองโดยสมมติว่า BookWarehouse และ CheckoutController เป็นอินเตอร์เฟสทั่วไป (เช่นโปรโตคอล) :

@interface MyBookWarehouse : NSObject <BookWarehouse> { ... } @end
@implementation MyBookWarehouse { ... } @end

@interface MyCheckoutController : NSObject <CheckoutController> { ... } @end
@implementation MyCheckoutController { ... } @end

...

-(void) applicationDidFinishLoading {
   MyBookWarehouse *myWarehouse = [[MyBookWarehouse alloc]init];
   MyCheckoutController *myCheckout = [[MyCheckoutController alloc]init];

   BookPickerViewController *bookPicker = [[BookPickerViewController alloc] 
                                         initWithWarehouse:myWarehouse 
                                         andCheckoutController:myCheckout];
   ...
   [window addSubview:[bookPicker view]];
   [window makeKeyAndVisible];
}

สุดท้ายไม่เพียง แต่ BookPickerController ของคุณสามารถนำมาใช้ใหม่ได้ แต่ยังง่ายต่อการทดสอบ

-(void) testBookPickerController {
   MockBookWarehouse *myWarehouse = [[MockBookWarehouse alloc]init];
   MockCheckoutController *myCheckout = [[MockCheckoutController alloc]init];

   BookPickerViewController *bookPicker = [[BookPickerViewController alloc] initWithWarehouse:myWarehouse andCheckoutController:myCheckout];
   ...
   [bookPicker handleCheckout];

   // Do stuff to verify that BookPickerViewController correctly called
   // MockCheckoutController's handleCheckout: method and passed it a valid
   // list of books
   ...
}

19
เมื่อฉันเห็นคำถาม (และคำตอบ) เช่นนี้สร้างขึ้นด้วยความระมัดระวังฉันก็อดไม่ได้ที่จะยิ้ม ขอชื่นชมความรุ่งโรจน์ของผู้ถามที่กล้าหาญและคุณ !! ในขณะเดียวกันฉันต้องการแชร์ลิงก์ที่อัปเดตสำหรับลิงก์ invasivecode.com ที่มีประโยชน์ซึ่งคุณอ้างถึงในจุดที่สองของคุณ: invasivecode.com/2009/09/… - ขอขอบคุณอีกครั้งสำหรับการแบ่งปันข้อมูลเชิงลึกและแนวทางปฏิบัติที่ดีที่สุดของคุณ
Joe D'Andrea

ฉันเห็นด้วย. คำถามนั้นถูกสร้างขึ้นมาอย่างดีและคำตอบนั้นยอดเยี่ยมมาก แทนที่จะมีคำตอบทางเทคนิคมันยังรวมถึงจิตวิทยาบางส่วนที่อยู่เบื้องหลังว่าจะใช้งานอย่างไรโดยใช้ DI ขอบคุณ! +1 ขึ้นไป
Kevin Elliott

จะทำอย่างไรถ้าคุณต้องการใช้ BookPickerController ในการเลือกหนังสือสำหรับรายการสินค้าที่ต้องการหรือหนึ่งในหลายเหตุผลที่เป็นไปได้ คุณจะยังคงใช้วิธีการอินเทอร์เฟซของ CheckoutController (อาจเปลี่ยนชื่อเป็น BookSelectionController) หรืออาจใช้ NSNotificationCenter
Les

นี่ยังคงเป็นคู่ที่ค่อนข้างแน่น การเพิ่มและการบริโภคกิจกรรมจากสถานที่ส่วนกลางจะเป็นการผ่อนคลาย
Neil McGuigan

1
ลิงก์ที่อ้างอิงในจุดที่ 2 ดูเหมือนว่าจะมีการเปลี่ยนแปลงอีกครั้ง - นี่คือลิงค์การทำงานที่invasivecode.com/blog/archives/322
vikmalhotra

15

สิ่งนี้เป็นเรื่องของการลิ้มรสเสมอ

ต้องบอกว่าฉันมักจะชอบประสานงาน (# 2) ผ่านวัตถุแบบ ตัวควบคุมมุมมองระดับบนสุดโหลดหรือสร้างแบบจำลองที่ต้องการและตัวควบคุมมุมมองแต่ละชุดจะตั้งค่าคุณสมบัติในตัวควบคุมลูกเพื่อบอกให้พวกเขาทราบว่าวัตถุแบบจำลองใดที่พวกเขาต้องการใช้งาน การเปลี่ยนแปลงส่วนใหญ่มีการสื่อสารสำรองข้อมูลลำดับชั้นโดยใช้ NSNotificationCenter การยิงการแจ้งเตือนมักจะอยู่ในตัวของมันเอง

ตัวอย่างเช่นสมมติว่าฉันมีแอพที่มีบัญชีและธุรกรรม ฉันยังมี AccountListController, AccountController (ซึ่งแสดงข้อมูลสรุปบัญชีด้วยปุ่ม "แสดงธุรกรรมทั้งหมด"), TransactionListController และ TransactionController AccountListController โหลดรายการบัญชีทั้งหมดและแสดง เมื่อคุณแตะที่รายการมันจะตั้งค่าคุณสมบัติ. Account ของ AccountController และดัน AccountController ไปยังสแต็ก เมื่อคุณแตะปุ่ม "แสดงธุรกรรมทั้งหมด" AccountController จะโหลดรายการธุรกรรมวางไว้ในคุณสมบัติ .transactions ของ TransactionListController และกด TransactionListController ลงในสแต็กและอื่น ๆ

ถ้าพูดว่า TransactionController แก้ไขธุรกรรมมันทำให้เกิดการเปลี่ยนแปลงในวัตถุการทำธุรกรรมของมันแล้วเรียกวิธีการ 'บันทึก' 'บันทึก' ส่ง TransactionChangedNotification ตัวควบคุมอื่น ๆ ที่จำเป็นต้องรีเฟรชตัวเองเมื่อการเปลี่ยนแปลงการทำธุรกรรมจะสังเกตเห็นการแจ้งเตือนและปรับปรุงตัวเอง TransactionListController AccountController และ AccountListController อาจขึ้นอยู่กับสิ่งที่พวกเขาพยายามทำ

สำหรับ # 1 ในแอพแรก ๆ ของฉันฉันมี displayModel: withNavigationController: เมธอดในตัวควบคุมลูกที่จะติดตั้งและผลักคอนโทรลเลอร์ลงบนสแต็ก แต่เมื่อฉันคุ้นเคยกับ SDK มากขึ้นฉันก็ลอยไปจากสิ่งนั้นและตอนนี้ฉันมักจะให้ผู้ปกครองผลักเด็ก

สำหรับ # 3 ให้พิจารณาตัวอย่างนี้ ที่นี่เราใช้สองคอนโทรลเลอร์ AmountEditor และ TextEditor เพื่อแก้ไขคุณสมบัติสองรายการ ผู้แก้ไขไม่ควรบันทึกธุรกรรมที่กำลังแก้ไขจริง ๆ เนื่องจากผู้ใช้สามารถตัดสินใจยกเลิกธุรกรรมได้ ดังนั้นพวกเขาทั้งคู่จึงควบคุมพาเรนต์ของพวกเขาเป็นตัวแทนและเรียกใช้วิธีการในการบอกว่าพวกเขาเปลี่ยนแปลงอะไร

@class Editor;
@protocol EditorDelegate
// called when you're finished.  updated = YES for 'save' button, NO for 'cancel'
- (void)editor:(Editor*)editor finishedEditingModel:(id)model updated:(BOOL)updated;  
@end

// this is an abstract class
@interface Editor : UIViewController {
    id model;
    id <EditorDelegate> delegate;
}
@property (retain) Model * model;
@property (assign) id <EditorDelegate> delegate;

...define methods here...
@end

@interface AmountEditor : Editor
...define interface here...
@end

@interface TextEditor : Editor
...define interface here...
@end

// TransactionController shows the transaction's details in a table view
@interface TransactionController : UITableViewController <EditorDelegate> {
    AmountEditor * amountEditor;
    TextEditor * textEditor;
    Transaction * transaction;
}
...properties and methods here...
@end

และตอนนี้วิธีการบางอย่างจาก TransactionController:

- (void)viewDidLoad {
    amountEditor.delegate = self;
    textEditor.delegate = self;
}

- (void)editAmount {
    amountEditor.model = self.transaction;
    [self.navigationController pushViewController:amountEditor animated:YES];
}

- (void)editNote {
    textEditor.model = self.transaction;
    [self.navigationController pushViewController:textEditor animated:YES];
}

- (void)editor:(Editor*)editor finishedEditingModel:(id)model updated:(BOOL)updated {
    if(updated) {
        [self.tableView reloadData];
    }

    [self.navigationController popViewControllerAnimated:YES];
}

สิ่งที่ควรสังเกตคือเราได้กำหนดโปรโตคอลทั่วไปซึ่งบรรณาธิการอาจใช้เพื่อสื่อสารกับผู้ควบคุมที่เป็นเจ้าของ ด้วยการทำเช่นนั้นเราสามารถนำ Editors กลับมาใช้ใหม่ในส่วนอื่นของแอปพลิเคชัน (บัญชีอาจมีบันทึกย่อได้เช่นกัน) แน่นอนว่าโปรโตคอล EditorDelegate อาจมีมากกว่าหนึ่งวิธี ในกรณีนี้เป็นสิ่งเดียวที่จำเป็น


1
นี่ควรจะทำงานได้ดีไหม ฉันมีปัญหากับEditor.delegateสมาชิก ในของฉันวิธีการที่ฉันได้รับviewDidLoad Property 'delegate' not found...ฉันไม่แน่ใจว่าฉันทำผิดพลาดอย่างอื่นหรือไม่ หรือถ้านี่เป็นเรื่องย่อสำหรับความกะทัดรัด
Jeff

นี่คือรหัสเก่าที่ค่อนข้างเก่าเขียนในสไตล์ที่เก่ากว่าพร้อมกับอนุสัญญาที่เก่ากว่า ฉันจะไม่คัดลอกและวางลงในโครงการของคุณโดยตรง ฉันแค่พยายามเรียนรู้จากรูปแบบ
Brent Royal-Gordon

Gotcha นั่นคือสิ่งที่ฉันอยากรู้ ฉันได้มันมาพร้อมกับการดัดแปลงบางอย่าง แต่ฉันกังวลเล็กน้อยว่ามันไม่ตรงกับคำต่อคำ
Jeff

0

ฉันเห็นปัญหาของคุณ ..

สิ่งที่เกิดขึ้นคือมีบางคนสับสนความคิดของสถาปัตยกรรม MVC

MVC มีสามส่วน .. แบบจำลองมุมมองและตัวควบคุม .. ปัญหาที่ระบุดูเหมือนจะรวมสองส่วนเข้าด้วยกันโดยไม่มีเหตุผลที่ดี มุมมองและตัวควบคุมเป็นส่วนแยกของตรรกะ

ดังนั้น ... คุณไม่ต้องการมีตัวควบคุมมุมมองหลายตัว ..

คุณต้องการมีหลายมุมมองและตัวควบคุมที่เลือกระหว่างมุมมองเหล่านั้น (คุณอาจมีคอนโทรลเลอร์หลายตัวหากคุณมีหลายแอพพลิเคชั่น)

มุมมองที่ไม่ควรตัดสินใจ ผู้ควบคุมควรทำเช่นนั้น ดังนั้นการแยกงานและตรรกะและวิธีการทำให้ชีวิตของคุณง่ายขึ้น

ดังนั้น .. ตรวจสอบให้แน่ใจว่ามุมมองของคุณทำอย่างนั้นทำให้ข้อมูลที่ดี ให้ผู้ควบคุมของคุณตัดสินใจว่าจะทำอย่างไรกับข้อมูลและมุมมองที่จะใช้

(และเมื่อเราพูดถึงข้อมูลเรากำลังพูดถึงโมเดล ... วิธีมาตรฐานที่ดีในการเก็บเข้าถึงเข้าถึงแก้ไข .. อีกส่วนหนึ่งของตรรกะที่เราสามารถแยกเก็บและลืม)


0

สมมติว่ามีสองคลาส A และ B

อินสแตนซ์ของคลาส A คือ

aInstance;

คลาส A ทำและอินสแตนซ์ของคลาส B เป็น

B bInstance;

และในตรรกะของคลาส B คุณจำเป็นต้องสื่อสารหรือทริกเกอร์วิธีการเรียน A

1) วิธีที่ไม่ถูกต้อง

คุณสามารถส่ง aInstance ไปที่ bInstance ตอนนี้ทำการเรียกเมธอดที่ต้องการ [aInstance methodname] จากตำแหน่งที่ต้องการใน bInstance

สิ่งนี้จะเป็นการตอบสนองวัตถุประสงค์ของคุณ แต่ในขณะที่การเปิดตัวจะทำให้หน่วยความจำถูกล็อคและไม่ได้ทำให้เป็นอิสระ

อย่างไร?

เมื่อคุณส่ง aInstance ไปยัง bInstance เราได้เพิ่มจำนวนของ aInstance เพิ่มขึ้น 1 เมื่อทำการยกเลิกการจัดสรร bInstance เราจะมีหน่วยความจำที่ถูกบล็อกเพราะ aInstance ไม่สามารถถูกนำมาที่ 0 เก็บไว้ด้วยเหตุผล bInstance เพราะ bInstance นั้นเป็นวัตถุของ

นอกจากนี้เนื่องจาก aInstance ติดอยู่หน่วยความจำของ bInstance ก็จะติด (รั่วไหล) ดังนั้นแม้หลังจากการจัดสรรคืน aInstance เองเมื่อเวลาในภายหลังหน่วยความจำของมันก็จะถูกบล็อกเพราะ bInstance ไม่สามารถปล่อยให้เป็นอิสระและ bInstance เป็นตัวแปรคลาสของ aInstance

2) วิธีที่ถูกต้อง

โดยการกำหนด aInstance ในฐานะผู้รับมอบอำนาจของ bInstance จะไม่มีการเปลี่ยนแปลงจำนวนการเก็บรักษาหรือการพัวพันของหน่วยความจำของ aInstance

bInstance จะสามารถเรียกใช้วิธีการมอบหมายอย่างอิสระใน aInstance ในการจัดสรรคืนของ bInstance ตัวแปรทั้งหมดจะถูกสร้างขึ้นเองและจะถูกปล่อยออกจากการจัดสรรคืนของ aInstance เนื่องจากไม่มีสิ่งกีดขวางของ aInstance ใน bInstance จึงจะถูกปล่อยอย่างหมดจด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.