programing

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

padding 2023. 4. 28. 20:19
반응형

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

오브젝티브-c, 코코아, 아이폰 개발은 일반적으로 처음이라 언어와 프레임워크를 최대한 활용하고 싶은 욕구가 강합니다.

제가 사용하고 있는 자료 중 하나는 스탠포드의 CS193P 클래스 노트입니다. 이 노트는 웹에 남겨져 있습니다.강의 노트, 과제 및 샘플 코드가 포함되어 있으며, 이 과정은 애플 개발자의 강의였기 때문에 저는 분명히 "말의 입에서"라고 생각합니다.

클래스 웹 사이트:
http://www..edu/class/cs193p/cgi-bin/index.phphttp ://www.stanford.edu/class/cs193p/cgi-bin/index.php

강의 08은 여러 개의 UIViewController가 UINavigationController 스택에 푸시된 UIViewController 기반 앱을 구축하는 과제와 관련이 있습니다.이것이 바로 UI 내비게이션 컨트롤러의 작동 방식입니다.논리적이네요.그러나 슬라이드에는 UIView 컨트롤러 간의 통신에 대한 몇 가지 엄격한 경고가 있습니다.

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

16/51페이지:

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

  • 전역 변수 또는 단일 단추
    • 여기에는 애플리케이션 대리인이 포함됩니다.
  • 직접적인 종속성으로 인해 코드 재사용이 줄어듭니다.
    • 디버깅 및 테스트가 더 어렵습니다.

좋습니다. 저는 그것에 동의합니다.뷰 컨트롤러 간의 통신에 사용될 모든 메서드를 앱 위임자에게 맹목적으로 던지지 말고 앱 위임자 메서드에서 뷰 컨트롤러 인스턴스를 참조하십시오.좋아요, 좋아요.

조금 더 나아가서, 우리가 무엇을 해야 하는지를 알려주는 슬라이드가 있습니다.

18/51페이지:

데이터 흐름 모범 사례

  • 전달해야 할 사항을 정확히 파악합니다.
  • 컨트롤러에 대한 입력 매개 변수 정의
  • 계층을 백업하기 위해 느슨한 커플링을 사용합니다.
    • 관찰자를 위한 일반 인터페이스 정의(예: 위임)

그런 다음 이 슬라이드는 자리 표시자 슬라이드로 표시되며, 여기서 강사는 UIImagePickerController의 예제를 사용하여 모범 사례를 시연합니다.영상이 있었으면 좋겠어요! :(

좋아요, 그럼...유감스럽게도 제 물건은 그렇게 강하지 않습니다.위 견적서의 마지막 줄도 조금 헷갈립니다.저는 이 문제에 대해 제 몫의 구글링을 해왔고 관찰/통보 기술의 다양한 방법에 대해 이야기하는 괜찮은 기사로 보이는 것을 발견했습니다.
http://cocoawithlove.com/2008/06/.htmlhttp ://cocoawithlove.com/2008/06/five-approaches-to-listening-observing.html

메소드 #5는 메소드로 딜러를 표시하기도 합니다!제외하고는...개체는 한 번에 하나의 대리자만 설정할 수 있습니다.그러면 다중 뷰 컨트롤러 통신을 사용하는 경우 어떻게 해야 합니까?

네, 그게 바로 조직폭력배입니다.앱 대리인의 다중 뷰 컨트롤러 인스턴스를 참조하여 앱 대리인의 통신 방법을 쉽게 수행할 수 있다는 것을 알고 있지만 올바른 방법으로 이러한 작업을 수행하고 싶습니다.

다음 질문에 대답하여 "올바른 일을 할 수 있도록" 도와주십시오.

  1. UI Navigation Controller 스택에서 새 뷰 컨트롤러를 푸시하려고 할 때 누가 이 푸시를 수행해야 합니까? 코드에서 어떤 클래스/파일이 올바른 위치입니까?
  2. 다른 UIViewController에 있을 때 UIViewController 중 하나의 일부 데이터(iVar 값)에 영향을 주고 싶을 때 이를 "적절한" 방법은 무엇입니까?
  3. 한 번에 한 명의 위임자만 개체에 설정할 수 있다고 가정할 때 강사가 "관찰자를 위한 일반 인터페이스 정의(예: 위임)"라고 말할 때 구현은 어떻게 표시됩니다.가능하다면 유사 코드 예제가 여기에 매우 유용할 것입니다.

이 질문들은 좋은 질문들입니다. 여러분이 이 연구를 하고 있다는 것과 함께 해킹하는 것이 아니라 "올바른 방법"을 배우는 것에 관심이 있는 것 같습니다.

먼저, 저는 (MVC 설계 패턴에 따라) 적절한 경우 모델 객체에 데이터를 넣는 것의 중요성에 초점을 맞춘 이전 답변에 동의합니다.일반적으로 상태 정보는 엄격히 "표시" 데이터가 아닌 한 컨트롤러 내에 넣지 않습니다.

둘째, 프로그래밍 방식으로 컨트롤러를 탐색 컨트롤러에 푸시하는 방법에 대한 예는 Stanford 프레젠테이션의 10페이지를 참조하십시오.인터페이스 작성기를 사용하여 "시각적"으로 이 작업을 수행하는 방법의 예를 보려면 자습서를 참조하십시오.

셋째, 그리고 아마도 가장 중요한 것은 스탠포드 프레젠테이션에서 언급한 "우수 관리 기준"을 "의존성 주입" 설계 패턴의 맥락에서 생각해 보면 훨씬 이해하기 쉽다는 점입니다.즉, 컨트롤러가 자신의 작업에 필요한 개체를 "검색"해서는 안 됩니다(예: 글로벌 변수 참조).대신 컨트롤러에 항상 이러한 종속성을 "주입"해야 합니다(즉, 필요한 개체를 메서드를 통해 전달).

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

그게 제 답변의 요지입니다.도움이 될 경우를 대비하여 아래 컨트롤러와 함께 의존성 주입 패턴을 사용하는 예를 포함하겠습니다.

뷰 컨트롤러와 함께 종속성 주입 사용 예제

여러 권의 책이 나열된 화면을 만들고 있다고 가정해 보겠습니다.사용자는 사고 싶은 책을 고른 다음 "체크아웃" 버튼을 눌러 체크아웃 화면으로 이동할 수 있습니다.

이를 구축하려면 GUI/보기 개체를 제어하고 표시하는 BookPickerViewController 클래스를 만들 수 있습니다.그것이 모든 책 데이터를 어디서 얻을 것입니까?이를 위해 BookWarehouse 객체에 의존한다고 가정해 보겠습니다.이제 컨트롤러는 기본적으로 모델 개체(BookWarehouse)와 GUI/View 개체 간에 데이터를 중개합니다.즉, BookPickerViewController는 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];
   ...
}

애플 사람들이 "계층을 백업하기 위해" 위임 패턴을 사용하는 것에 대해 이야기할 때, 그들은 여전히 의존성 주입에 대해 이야기하고 있습니다.이 예에서 사용자가 자신의 책을 선택하고 체크아웃할 준비가 되면 BookPickerView 컨트롤러는 어떻게 해야 합니까?글쎄, 그것은 정말로 그들의 일이 아닙니다.이 작업을 다른 개체에 위임해야 합니다. 즉, 다른 개체에 종속됩니다.따라서 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/View 개체)를 제공할 수 있으며, 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];
}

마지막으로, BookPicker Controller는 재사용 가능할 뿐만 아니라 테스트하기도 쉽습니다.

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

이런 종류의 것은 항상 취향의 문제입니다.

그렇기는 하지만 저는 항상 모델 객체를 통해 조정(#2)하는 것을 선호합니다.최상위 뷰 컨트롤러는 필요한 모델을 로드하거나 생성하며, 각 뷰 컨트롤러는 하위 컨트롤러에 속성을 설정하여 작업해야 하는 모델 개체를 알려줍니다.대부분의 변경 사항은 NS Notification Center를 사용하여 계층을 백업합니다. 알림 실행은 일반적으로 모델 자체에 내장되어 있습니다.

예를 들어 계정 및 트랜잭션이 있는 앱이 있다고 가정합니다.또한 AccountListController, AccountController("모든 트랜잭션 표시" 단추로 계정 요약 표시), TransactionListController 및 TransactionController가 있습니다.AccountListController는 모든 계정의 목록을 로드하고 표시합니다.목록 항목을 누르면 해당 계정 컨트롤러의 .account 속성이 설정되고 계정 컨트롤러가 스택에 푸시됩니다.모든 트랜잭션 표시 단추를 누르면 계정 컨트롤러가 트랜잭션 목록을 로드하고 트랜잭션 목록 컨트롤러의 .transactions 속성에 저장한 다음 트랜잭션 목록 컨트롤러를 스택에 밀어넣습니다.

트랜잭션 컨트롤러가 트랜잭션을 편집하는 경우 트랜잭션 개체를 변경한 다음 '저장' 메서드를 호출합니다.'save'는 트랜잭션 변경 알림을 보냅니다.트랜잭션이 변경될 때 자체적으로 새로 고쳐야 하는 다른 컨트롤러는 알림을 확인하고 자체적으로 업데이트합니다.TransactionListController는 아마도 그럴 것입니다. AccountController와 AccountListController는 그들이 하려는 일에 따라 그렇게 할 수 있습니다.

#1의 경우, 초기 앱에서 일종의 디스플레이 모델: with NavigationController: 하위 컨트롤러에서 컨트롤러를 스택에 밀어넣는 방식을 사용했습니다.하지만 SDK가 더 편해지면서, 저는 그것으로부터 멀어졌고, 지금은 보통 부모가 아이를 밀도록 합니다.

#3의 경우, 이 예를 생각해 보십시오.여기서는 트랜잭션의 두 가지 속성을 편집하기 위해 MountEditor와 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

다음은 트랜잭션 컨트롤러의 몇 가지 방법입니다.

- (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];
}

여기서 주목해야 할 점은 편집자가 자신의 컨트롤러와 통신하는 데 사용할 수 있는 일반 프로토콜을 정의했다는 점입니다.이렇게 하면 응용프로그램의 다른 부분에서 편집기를 재사용할 수 있습니다. (아마도 계정에도 노트가 있을 수 있습니다.)물론 EditorDelegate 프로토콜에는 둘 이상의 메소드가 포함될 수 있습니다. 이 경우에는 메소드만 필요합니다.

당신의 문제를 이해합니다.

어떤 사람이 MVC 아키텍처에 대해 혼동을 일으켰다는 것입니다.

MVC는 세 부분으로 구성되어 있습니다.모델, 뷰 및 컨트롤러...언급된 문제는 이유 없이 두 가지를 결합한 것으로 보입니다.뷰와 컨트롤러는 별개의 논리입니다.

여러 뷰 컨트롤러를 사용하지 않으려는 것입니다.

여러 뷰와 그 중에서 선택하는 컨트롤러를 사용하려고 합니다.(여러 개의 애플리케이션이 있는 경우 여러 개의 컨트롤러를 사용할 수도 있습니다.)

보기가 결정을 내리지 않아야 합니다.컨트롤러가 이 작업을 수행해야 합니다.따라서 업무, 논리, 삶을 더 쉽게 만드는 방법의 분리.

그러니까.. 당신의 뷰가 그렇게 하는지 확인하고, 데이터에 대한 멋진 뷰를 보여줍니다.컨트롤러가 데이터에 대해 수행할 작업과 사용할 보기를 결정하도록 합니다.

(데이터에 대해 이야기할 때 모델에 대해 이야기합니다.)저장, 액세스, 수정할 수 있는 좋은 표준 방법.우리가 정리하고 잊어버릴 수 있는 또 다른 논리 조각)

클래스 A와 B가 두 개 있다고 가정합니다.

클래스 A의 인스턴스는

A 인스턴스;

클래스 A는 클래스 B의 인스턴스를 만듭니다.

Bb 인스턴스;

그리고 클래스 B의 논리에서, 당신은 클래스 A의 메소드를 전달하거나 트리거해야 합니다.

잘못된 방법

aInstance를 bInstance로 전달할 수 있습니다.이제 bInstance의 원하는 위치에서 원하는 메서드 [aInstance 메서드 이름]을 호출합니다.

이렇게 하면 사용자의 목적에 부합할 수 있지만 릴리스 시 메모리가 잠기고 해제되지 않을 수 있습니다.

어떻게?

aInstance를 bInstance로 전달했을 때, 우리는 aInstance의 보유 개수를 1개 늘렸습니다.bInstance 할당 해제 시 bInstance 자체가 인스턴스의 개체이기 때문에 bInstance에 의해 인스턴스가 0개의 retaincount가 될 수 없기 때문에 메모리가 차단됩니다.

또한 인스턴스가 고착되기 때문에 bInstance의 메모리도 고착(누설)됩니다.따라서 나중에 인스턴스 자체의 할당을 해제한 후에도 bInstance는 해제될 수 없고 bInstance는 인스턴스의 클래스 변수이기 때문에 메모리도 차단됩니다.

바른길

인스턴스를 bInstance의 대리자로 정의하면 인스턴스의 보유 카운트 변경이나 메모리 얽힘이 발생하지 않습니다.

b 인스턴스는 aInstance에 있는 대리자 메서드를 자유롭게 호출할 수 있습니다.bInstance의 할당 취소 시 모든 변수가 자체적으로 생성되고 할당 취소 시 해제됩니다. bInstance에서 인스턴스의 얽힘이 없기 때문에 깔끔하게 해제됩니다.

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

반응형