IT박스

뷰 컨트롤러간에 통신하는 가장 좋은 방법은 무엇입니까?

itboxs 2020. 5. 30. 22:26
반응형

뷰 컨트롤러간에 통신하는 가장 좋은 방법은 무엇입니까?


objective-c, cocoa 및 iPhone dev에 익숙하지 않기 때문에 언어와 프레임 워크를 최대한 활용하고자합니다.

내가 사용하는 리소스 중 하나는 웹에 남겨둔 Stanford의 CS193P 클래스 노트입니다. 강의 노트, 과제 및 샘플 코드가 포함되어 있으며 Apple 개발자가 강의를 수강 한 이후로 "말의 입에서 나온 것"으로 간주됩니다.

수업 웹 사이트 :
http://www.stanford.edu/class/cs193p/cgi-bin/index.php

강의 08은 여러 UIViewController를 UINavigationController 스택에 푸시 한 UINavigationController 기반 앱을 빌드하기위한 과제와 관련이 있습니다. 이것이 UINavigationController의 작동 방식입니다. 논리적입니다. 그러나 슬라이드에서 UIViewController 간의 통신에 대한 엄격한 경고가 있습니다.

나는이 심각한 슬라이드에서 인용 할 것입니다 :
http://cs193p.stanford.edu/downloads/08-NavigationTabBarControllers.pdf

16/51 페이지 :

데이터를 공유하지 않는 방법

  • 전역 변수 또는 싱글 톤
    • 여기에는 응용 프로그램 대리인 이 포함됩니다
  • 직접적인 의존성은 코드 재사용을 덜 만든다
    • 그리고 디버깅 및 테스트가 더 어렵습니다.

확인. 나는 그것으로 아래로입니다. 뷰 컨트롤러와 앱 대리자로 통신하는 데 사용될 모든 메서드를 맹목적으로 던지지 말고 앱 대리자 메서드에서 뷰 컨트롤러 인스턴스를 참조하십시오. 페어 넛

조금 더 나아가서, 우리가 해야일을 알려주는이 슬라이드를 얻습니다 .

페이지 18/51 :

데이터 흐름을위한 모범 사례

  • 그림 밖으로 정확히 전달 될 필요가 무엇
  • 뷰 컨트롤러의 입력 매개 변수 정의
  • 계층 구조를 다시 통신 하려면 느슨한 결합을 사용하십시오.
    • 관찰자에 대한 일반 인터페이스 정의 (위임과 같은)

이 슬라이드 다음에는 강사가 UIImagePickerController와 함께 예제를 사용하여 모범 사례를 보여주는 자리 표시 자 슬라이드 인 것처럼 보입니다. 비디오를 사용할 수 있기를 바랍니다! :(

좋아, 그래서 ... 내 objc-fu가 그렇게 강하지 않다는 것을 두려워한다. 또한 위 인용문의 마지막 줄에 약간 혼란 스럽습니다. 나는 이것에 대해 인터넷 검색을 공정하게 수행했으며 다양한 관찰 / 알림 기술 방법에 대해 이야기하는 적절한 기사 인 것으로 나타났습니다 :
http://cocoawithlove.com/2008/06/five-approaches-to -listening-observing.html

방법 # 5는 델리게이트를 방법으로 나타냅니다! 제외 .... 객체는 한 번에 하나의 델리게이트 만 설정할 수 있습니다. 그래서 여러 개의 viewcontroller 통신이있을 때 어떻게해야합니까?

좋아, 그게 설정 조직이야. appdelegate의 여러 viewcontroller 인스턴스를 참조하여 앱 대리자에서 통신 방법을 쉽게 수행 할 수 있다는 것을 알고 있지만 올바른 방법 으로이 작업을하고 싶습니다 .

다음 질문에 답하여 "올바른 일을하도록"도와주세요.

  1. UINavigationController 스택에서 새 viewcontroller를 푸시하려고 할 때 누가이 푸시를 수행해야합니까. 내 코드에서 어떤 클래스 / 파일이 올바른 장소입니까?
  2. 다른 UIViewController 에있을 때 UIViewController 중 하나의 일부 데이터 (iVar 값)에 영향을 주려면 "올바른"방법은 무엇입니까?
  3. 한 개체에 한 번에 하나의 대리자 만 설정할 수 있으며 강사가 "관찰자 (예 : 위임)에 대한 일반 인터페이스 정의" 라고 말하면 구현 방식은 어떻습니까 ? 의사 코드 예제는 가능하다면 여기에 대단히 도움이 될 것입니다.

이것들은 좋은 질문이며, 당신이이 연구를하고 있다는 것을 알기에 훌륭합니다. 단지 그것을 해킹하는 대신 "올바른 방법"을 배우는 것에 관심이있는 것 같습니다.

먼저 , 적절한 경우 (MVC 디자인 패턴에 따라) 모델 객체에 데이터를 넣는 것의 중요성에 초점을 둔 이전 답변에 동의합니다. 일반적으로 컨트롤러는 상태 정보를 엄격히 "표시"하지 않는 한 상태 정보를 컨트롤러에 넣지 않도록합니다.

둘째 , 컨트롤러를 내비게이션 컨트롤러에 프로그래밍 방식으로 푸시하는 방법에 대한 예제는 Stanford 프레젠테이션 10 페이지를 참조하십시오. Interface Builder를 사용하여 "시각적으로"이 작업을 수행하는 방법에 대한 예제는 이 학습서를보십시오 .

셋째 , 그리고 아마도 가장 중요한 것은 스탠포드 프레젠테이션에서 언급 된 "모범 사례"는 "종속성 주입"디자인 패턴의 맥락에서 생각하면 이해하기 훨씬 쉽다는 것입니다. 간단히 말해서, 이것은 컨트롤러가 작업을 수행하는 데 필요한 객체를 "찾아서는"안된다는 것을 의미합니다 (예 : 전역 변수 참조). 대신, 항상 이러한 의존성을 컨트롤러에 "주입"해야합니다 (즉, 메소드를 통해 필요한 객체를 전달).

의존성 주입 패턴을 따르는 경우 컨트롤러는 모듈 식이며 재사용 가능합니다. 그리고 스탠포드 발표자가 어디에서 왔는지에 대해 생각한다면 (즉, Apple 직원으로서 쉽게 재사용 할 수있는 클래스를 만드는 것이 중요합니다) 재사용 성과 모듈성이 우선 순위가 높습니다. 데이터 공유에 대해 언급 한 모든 모범 사례는 종속성 주입의 일부입니다.

그것은 나의 반응의 요지입니다. 도움이 될 수 있도록 아래 컨트롤러와 함께 의존성 주입 패턴을 사용하는 예를 포함하겠습니다.

View Controller에서 종속성 주입을 사용하는 예

여러 권의 책이 나열된 화면을 작성한다고 가정 해 보겠습니다. 사용자는 구매하려는 책을 선택한 다음 "체크 아웃"버튼을 눌러 체크 아웃 화면으로 이동할 수 있습니다.

이를 구축하기 위해 GUI / view 객체를 제어하고 표시하는 BookPickerViewController 클래스를 만들 수 있습니다. 모든 도서 데이터를 어디서 얻을 수 있습니까? 그것을 위해 BookWarehouse 객체에 의존한다고 가정 해 봅시다. 이제 컨트롤러는 기본적으로 모델 객체 (BookWarehouse)와 GUI / view 객체간에 데이터를 중개합니다. 다시 말해 BookWarehouse 개체에 대한 BookPickerViewController DEPENDS입니다.

이 작업을 수행하지 마십시오 :

@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];
   ...
}

애플 직원들은 위임 패턴을 사용하여 "계층 구조를 다시 통신"하는 것에 대해 여전히 의존성 주입에 대해 이야기하고 있습니다. 이 예제에서 BookPickerViewController는 사용자가 책을 고르고 체크 아웃 할 준비가되면 어떻게해야합니까? 글쎄, 그것은 실제로 그 일이 아닙니다. 다른 객체에 작동하는 것을 삭제해야합니다. 즉, 다른 객체에 의존합니다. 따라서 BookPickerViewController init 메소드를 다음과 같이 수정할 수 있습니다.

@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
   ...
}

This sort of thing is always a matter of taste.

Having said that, I always prefer to do my coordination (#2) via model objects. The top-level view controller loads or creates the models it needs, and each view controller sets properties in its child controllers to tell them which model objects they need to work with. Most changes are communicated back up the hierarchy by using NSNotificationCenter; firing the notifications is usually built in to the model itself.

For example, suppose I have an app with Accounts and Transactions. I also have an AccountListController, an AccountController (which displays an account summary with a "show all transactions" button), a TransactionListController, and a TransactionController. AccountListController loads a list of all accounts and displays them. When you tap on a list item, it sets the .account property of its AccountController and pushes the AccountController onto the stack. When you tap the "show all transactions" button, AccountController loads the transaction list, puts it in its TransactionListController's .transactions property, and pushes the TransactionListController onto the stack, and so on.

If, say, TransactionController edits the transaction, it makes the change in its transaction object and then calls its 'save' method. 'save' sends a TransactionChangedNotification. Any other controller that needs to refresh itself when the transaction changes would observe the notification and update itself. TransactionListController presumably would; AccountController and AccountListController might, depending on what they were trying to do.

For #1, in my early apps I had some sort of displayModel:withNavigationController: method in the child controller that would set things up and push the controller onto the stack. But as I've become more comfortable with the SDK, I've drifted away from that, and now I usually have the parent push the child.

For #3, consider this example. Here we are using two controllers, AmountEditor and TextEditor, to edit two properties of a Transaction. The Editors should not actually save the transaction being edited, since the user could decide to abandon the transaction. So instead they both take their parent controller as a delegate and call a method on it saying if they've changed anything.

@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

And now a few methods from 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];
}

The thing to notice is that we've defined a generic protocol which Editors may use to communicate with their owning controller. By doing so, we can reuse the Editors in another part of the application. (Perhaps Accounts can have notes, too.) Of course, the EditorDelegate protocol could contain more than one method; in this case that's the only one necessary.


I see your problem..

What has happened is that someone has confused idea of MVC architecture.

MVC has three parts.. models, views, and controllers.. The stated problem seems to have combined two of them for no good reason. views and controllers are seperate pieces of logic.

so... you do not want to have multiple view-controllers..

you want to have multiple views, and a controller that chooses between them. (you could also have multiple controllers, if you have multiple applications )

views should NOT be making decisions. The controller(s) should do that. Hence the seperation of tasks, and logic, and ways of making your life easier.

So.. make sure your view just does that, puts out a nice veiw of the data. let your controller decide what to do with the data, and which view to use.

(and when we talk about data, we are talking about the model... a nice standard way of being storred, accessed, modified.. another separate piece of logic that we can parcel away and forget about)


Suppose there are two classes A and B.

instance of class A is

A aInstance;

class A makes and instance of class B, as

B bInstance;

And in your logic of class B, somewhere you are required to communicate or trigger a method of class A.

1) Wrong way

You could pass the aInstance to bInstance. now place the call of the desired method [aInstance methodname] from the desired location in bInstance.

This would have served your purpose, but while release would have led to a memory being locked and not freed.

How?

When you passed the aInstance to bInstance, we increased the retaincount of aInstance by 1. When deallocating bInstance, we will have memory blocked because aInstance can never be brought to 0 retaincount by bInstance reason being that bInstance itself is an object of aInstance.

Further, because of aInstance being stuck, the memory of bInstance will also be stuck(leaked). So even after deallocating aInstance itself when its time comes later on, its memory too will be blocked because bInstance cant be freed and bInstance is a class variable of aInstance.

2)Right way

By defining aInstance as the delegate of bInstance, there will be no retaincount change or memory entanglement of aInstance.

bInstance will be able to freely invoke the delegate methods lying in the aInstance. On bInstance's deallocation, all the variables will be its own created and will be released On aInstance's deallocation, as there is no entanglement of aInstance in bInstance, it will be released cleanly.

참고URL : https://stackoverflow.com/questions/569940/whats-the-best-way-to-communicate-between-view-controllers

반응형