[ios] View Controller간에 데이터 전달

iOS 및 Objective-C와 전체 MVC 패러다임에 익숙하지 않으며 다음과 같은 문제가 있습니다.

데이터 입력 양식으로 작동하는보기가 있으며 사용자에게 여러 제품을 선택할 수있는 옵션을 제공하려고합니다. 제품이 다른보기로 표시되고 UITableViewController여러 선택을 활성화했습니다.

내 질문은 데이터를 한 뷰에서 다른 뷰로 어떻게 전송합니까? UITableView배열에서 선택 항목을 유지 하지만 양식을 제출할 때 다른 데이터와 함께 Core Data에 저장할 수 있도록 이전 데이터 입력 양식보기로 다시 전달하는 방법은 무엇입니까?

나는 서핑을하고 일부 사람들이 앱 델리게이트에서 배열을 선언하는 것을 보았습니다. 싱글 톤에 대해서는 읽었지만 이것이 무엇인지 이해하지 못하고 데이터 모델을 만드는 것에 대해 읽었습니다.

이것을 수행하는 올바른 방법은 무엇이며 어떻게해야합니까?



답변

이 질문은 stackoverflow에서 매우 인기가있는 것 같습니다. 저와 같은 iOS 세계에서 시작하는 사람들을 돕기 위해 더 나은 답변을 줄 것이라고 생각했습니다.

이 답변이 사람들이 이해하기에 충분하고 내가 놓친 부분이 없기를 바랍니다.

데이터 전달

다른 뷰 컨트롤러에서 뷰 컨트롤러로 데이터를 전달합니다. 탐색 스택으로 푸시 할 수있는 한 뷰 컨트롤러에서 다른 뷰 컨트롤러로 객체 / 값을 전달하려는 경우이 방법을 사용합니다.

이 예를 들어, 우리가해야합니다 ViewControllerAViewControllerB

패스하려면 BOOL에서 값 ViewControllerAViewControllerB우리는 다음을 할 것입니다.

  1. ViewControllerB.h대한 속성을 만들BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. ViewControllerA당신이 그것을 말할 필요가에 대한 ViewControllerB그래서를 사용

    #import "ViewControllerB.h"

    그런 다음 뷰를로드하려는 위치입니다 (예 : didSelectRowAtIndex또는 일부 IBAction는 속성 ViewControllerB을 탐색 스택에 푸시하기 전에 속성을 설정해야합니다 .

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.isSomethingEnabled = YES;
    [self pushViewController:viewControllerB animated:YES];

    이 설정됩니다 isSomethingEnabledViewControllerBBOOLYES.

Segues를 사용하여 데이터 전달

스토리 보드를 사용하는 경우 segue를 사용하고있을 가능성이 높으며 데이터를 전달하려면이 절차가 필요합니다. 이것은 위와 비슷하지만 뷰 컨트롤러를 푸시하기 전에 데이터를 전달하는 대신

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

그래서 통과 BOOL에서 ViewControllerAViewControllerB다음을 수행 할 우리를 :

  1. ViewControllerB.h대한 속성을 만들BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. ViewControllerA당신이 그것을 말할 필요가에 대한 ViewControllerB그래서를 사용

    #import "ViewControllerB.h"
  3. 에서 a를 SEGUE 만들기 ViewControllerAViewControllerB스토리 보드 및 그것을 식별자를 제공,이 예에서 우리는 그것을 전화 할게"showDetailSegue"

  4. 다음으로, ViewControllerAsegue가 수행 될 때 호출 되는 메소드를 추가해야합니다 . 이로 인해 호출 된 segue를 감지 한 후 무언가를 수행해야합니다. 이 예에서 우리는 점검 "showDetailSegue"하고 그것이 수행되면 우리의 BOOL가치를ViewControllerB

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
            controller.isSomethingEnabled = YES;
        }
    }

    네비게이션 컨트롤러에 뷰가 내장되어 있으면 위의 방법을 다음과 같이 약간 변경해야합니다.

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
            ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
            controller.isSomethingEnabled = YES;
        }
    }

    이 설정됩니다 isSomethingEnabledViewControllerBBOOLYES.

데이터 전달

데이터를 다시 합격 ViewControllerBViewControllerA사용해야하는 프로토콜 및 대리인을 또는 블록을 , 후자는 콜백 느슨하게 결합 메커니즘으로 사용할 수 있습니다.

이를 위해 ViewControllerA의 대표를 만들 것 입니다 ViewControllerB. 이것은 허용ViewControllerBViewControllerA 데이터를 다시 보낼 수 있도록 메시지를 다시 보낼 .

들어 ViewControllerA수의 대리인 ViewControllerB이 준수해야 ViewControllerB우리가 지정해야의 프로토콜입니다. ViewControllerA구현해야하는 메소드를 알려줍니다 .

  1. 에서 ViewControllerB.h, 아래 #import,하지만 위의 @interface당신은 프로토콜을 지정합니다.

    @class ViewControllerB;
    
    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
  2. 다음으로 여전히 속성 ViewControllerB.h을 설정 delegate하고 합성 해야합니다 .ViewControllerB.m

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
  3. 에서 ViewControllerB우리 온 메시지를 호출 delegate우리가보기 컨트롤러를 팝업 때.

    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
  4. 그게 다야 ViewControllerB. 지금의 ViewControllerA.h말할 ViewControllerA수입에 ViewControllerB그 프로토콜을 준수합니다.

    #import "ViewControllerB.h"
    
    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
  5. 에서하는 것은 ViewControllerA.m우리의 프로토콜에서 다음 메소드를 구현

    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
        NSLog(@"This was returned from ViewControllerB %@",item);
    }
  6. viewControllerB네비게이션 스택 으로 넘어 가기 전에 델리게이트가 델리게이트 ViewControllerB임을 알려야 합니다 ViewControllerA. 그렇지 않으면 에러가 발생합니다.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];

참고 문헌

  1. View Controller 프로그래밍 가이드 에서 위임을 사용하여 다른 View Controller와 통신
  2. 델리게이트 패턴

NSNotification center
데이터를 전달하는 또 다른 방법입니다.

// add observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];

-(void) handleDeepLinking:(NSNotification *) notification {
    id someObject = notification.object // some custom object that was passed with notification fire.
}

// post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];

한 클래스에서 다른 클래스로 데이터 전달 (클래스는 컨트롤러, 네트워크 / 세션 관리자, UIView 서브 클래스 또는 다른 클래스 일 수 있음)

블록은 익명 함수입니다.

이 예는 컨트롤러 B 에서 컨트롤러 A로 데이터를 전달합니다 .

블록을 정의하다

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h


값이 필요한 곳에 블록 핸들러 (리스너)를 추가하십시오 (예 : ControllerA에서 API 응답이 필요하거나 A에서 ContorllerB 데이터가 필요함)

// in ContollerA.m

- (void)viewDidLoad {
    [super viewDidLoad];
    __unsafe_unretained typeof(self) weakSelf = self;
    self.selectedVoucherBlock = ^(NSString *voucher) {
        weakSelf->someLabel.text = voucher;
    };
}

컨트롤러 B로 이동

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
    [self.navigationController pushViewController:vc animated:NO];

화재 차단

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath {
    NSString *voucher = vouchersArray[indexPath.row];
    if (sourceVC.selectVoucherBlock) {
        sourceVC.selectVoucherBlock(voucher);
    }
    [self.navigationController popToViewController:sourceVC animated:YES];
}

블록의 또 다른 작업 예


답변

빠른

여기저기서 StackOverflow에 대한 많은 설명이 있지만, 초보자가 작업을 위해 기본적인 것을 얻으려고 노력하는 경우이 YouTube 자습서를 시청하십시오 (마침내 방법을 이해하는 데 도움이되었습니다).

다음 View Controller로 데이터 전달

다음은 비디오를 기반으로 한 예입니다. 아이디어는 문자열을 첫 번째보기 컨트롤러의 텍스트 필드에서 두 번째보기 컨트롤러의 레이블로 전달하는 것입니다.

여기에 이미지 설명을 입력하십시오

인터페이스 빌더에서 스토리 보드 레이아웃을 작성하십시오. segue를 만들기 위해, 당신은 단지Control 버튼을 클릭하고 두 번째 뷰 컨트롤러로 드래그하십시오.

퍼스트 뷰 컨트롤러

First View Controller의 코드는

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // get a reference to the second view controller
        let secondViewController = segue.destination as! SecondViewController

        // set a variable in the second view controller with the String to pass
        secondViewController.receivedString = textField.text!
    }

}

두 번째 뷰 컨트롤러

그리고 두 번째 View Controller의 코드는

import UIKit

class SecondViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    // This variable will hold the data being passed from the First View Controller
    var receivedString = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // Used the text from the First View Controller to set the label
        label.text = receivedString
    }

}

잊지 마세요

  • 의 콘센트 시켜줘 UITextField과를UILabel .
  • 첫 번째 및 두 번째 View Controller를 IB에서 적절한 Swift 파일로 설정하십시오.

이전 View Controller로 데이터 전달

두 번째 뷰 컨트롤러에서 첫 번째 뷰 컨트롤러로 데이터를 다시 전달하려면 프로토콜과 델리게이트 를 사용 합니다 . 이 비디오는 그 과정을 통해 매우 분명합니다.

다음은 비디오를 기반으로 한 몇 가지 예입니다.

여기에 이미지 설명을 입력하십시오

인터페이스 빌더에서 스토리 보드 레이아웃을 작성하십시오. 다시 한번 말하지만, Control버튼에서 두 번째 뷰 컨트롤러로 드래그하면됩니다. segue 식별자를showSecondViewController . 또한 다음 코드의 이름을 사용하여 콘센트와 작업을 연결하는 것을 잊지 마십시오.

퍼스트 뷰 컨트롤러

First View Controller의 코드는

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showSecondViewController" {
            let secondViewController = segue.destination as! SecondViewController
            secondViewController.delegate = self
        }
    }

    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

맞춤형 DataEnteredDelegate프로토콜 사용에 유의하십시오 .

두 번째 뷰 컨트롤러 및 프로토콜

두 번째 뷰 컨트롤러의 코드는

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: AnyObject {
    func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {

    // making this a weak variable so that it won't create a strong reference cycle
    weak var delegate: DataEnteredDelegate? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(sender: AnyObject) {

        // call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(info: textField.text!)

        // go back to the previous view controller
        _ = self.navigationController?.popViewController(animated: true)
    }
}

참고 protocol 뷰 컨트롤러 클래스의 외부이다.

그게 다야. 이제 앱을 실행하면 두 번째 뷰 컨트롤러에서 첫 번째 컨트롤러로 데이터를 다시 보낼 수 있습니다.


답변

MVC의 M은 “모델”을위한 것이며 MVC 패러다임에서 모델 클래스의 역할은 프로그램의 데이터를 관리하는 것입니다. 모델은 뷰의 반대입니다. 뷰는 데이터를 표시하는 방법을 알고 있지만 데이터로 수행 할 작업에 대해서는 아무것도 모르지만 모델은 데이터 작업 방법에 대해서는 모든 것을 알고 있지만 표시하는 방법에 대해서는 전혀 모릅니다. 모델은 복잡 할 수 있지만 반드시 그럴 필요는 없습니다. 앱의 모델은 문자열이나 사전 배열처럼 간단 할 수 있습니다.

컨트롤러의 역할은 뷰와 모델을 중재하는 것입니다. 따라서 하나 이상의 뷰 객체와 하나 이상의 모델 객체에 대한 참조가 필요합니다. 모델이 사전의 배열이며 각 사전이 테이블의 한 행을 나타냅니다. 앱의 루트보기는 해당 테이블을 표시하며 파일에서 배열을로드해야 할 수도 있습니다. 사용자가 테이블에 새 행을 추가하기로 결정하면 일부 버튼을 탭하면 컨트롤러가 새 (변경 가능) 사전을 작성하여 배열에 추가합니다. 행을 채우기 위해 제어기는 상세보기 제어기를 작성하여 새 사전을 제공합니다. 상세도 제어기가 사전을 채우고 리턴합니다. 사전은 이미 모델의 일부이므로 다른 작업은 필요하지 않습니다.


답변

iOS에서 다른 클래스로 데이터를 수신하는 방법에는 여러 가지가 있습니다. 예를 들어-

  1. 다른 클래스 할당 후 직접 초기화
  2. 위임-데이터를 다시 전달
  3. 알림-한 번에 여러 클래스로 데이터 브로드 캐스트
  4. 에 저장 NSUserDefaults -나중에 액세스
  5. 싱글턴 수업
  6. plist 등과 같은 데이터베이스 및 기타 스토리지 메커니즘

그러나 현재 클래스에서 할당이 수행되는 다른 클래스에 값을 전달하는 간단한 시나리오의 경우 가장 일반적이고 선호되는 방법은 할당 후 값을 직접 설정하는 것입니다. 이것은 다음과 같이 수행됩니다.

Controller1과 Controller2의 두 컨트롤러를 사용하여 이해할 수 있습니다.

Controller1 클래스에서 Controller2 객체를 생성하고 전달되는 문자열 값으로 푸시한다고 가정합니다. 이것은 다음과 같이 수행 할 수 있습니다 :-

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj passValue:@"String"];
    [self pushViewController:obj animated:YES];
}

Controller2 클래스의 구현에는 다음과 같은 기능이 있습니다.

@interface Controller2  : NSObject

@property (nonatomic , strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

- (void) passValue:(NSString *)value {

    _stringPassed = value; //or self.stringPassed = value
}

@end

다음과 비슷한 방식으로 Controller2 클래스의 속성을 직접 설정할 수도 있습니다.

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj setStringPassed:@"String"];
    [self pushViewController:obj animated:YES];
}

여러 값을 전달하려면 다음과 같은 여러 매개 변수를 사용할 수 있습니다.

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1 andValues:objArray withDate:date]; 

또는 공통 기능과 관련된 3 개 이상의 매개 변수를 전달해야하는 경우 값을 모델 클래스에 저장하고 해당 모델 오브젝트를 다음 클래스에 전달할 수 있습니다.

ModelClass *modelObject = [[ModelClass alloc] init];
modelObject.property1 = _property1;
modelObject.property2 = _property2;
modelObject.property3 = _property3;

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passmodel: modelObject];

원한다면 짧게-

1) set the private variables of the second class initialise the values by calling a custom function and passing the values.
2) setProperties do it by directlyInitialising it using the setter method.
3) pass more that 3-4 values related to each other in some manner , then create a model class and set values to its object and pass the object using any of the above process.

도움이 되었기를 바랍니다


답변

더 많은 연구를 한 후에는 프로토콜과 대리인이 올바른 방법 / 애플이 선호하는 방법 인 것 같습니다.

이 예제를 사용하여 끝났습니다.

뷰 컨트롤러와 다른 객체간에 데이터 공유iPhone Dev SDK

잘 작동하여 뷰와 뷰 사이에서 문자열과 배열을 앞뒤로 전달할 수있었습니다.

모든 도움을 주셔서 감사합니다


답변

통과 블록이있는 가장 단순하고 가장 우아한 버전을 찾습니다. 리턴 된 데이터를 “A”로 대기하고 뷰 제어기를 “B”로 리턴하는 뷰 제어기의 이름을 지정하십시오. 이 예에서는 첫 번째는 Type1의 두 번째와 두 번째는 Type2의 두 가지 값을 가져옵니다.

Storyboard를 사용한다고 가정하면 첫 번째 컨트롤러는 예를 들어 segue 준비 중에 콜백 블록을 설정합니다.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController isKindOfClass:[BViewController class]])
    {
        BViewController *viewController = segue.destinationViewController;

        viewController.callback = ^(Type1 *value1, Type2 *value2) {
            // optionally, close B
            //[self.navigationController popViewControllerAnimated:YES];

            // let's do some action after with returned values
            action1(value1);
            action2(value2);
        };

    }
}

“B”뷰 컨트롤러는 콜백 속성 BViewController.h를 선언해야합니다.

// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);

콜백을 반환하기 위해 원하는 값을 얻은 후 구현 파일 BViewController.m보다 다음을 호출해야합니다.

if (self.callback)
    self.callback(value1, value2);

기억해야 할 것은 블록을 사용하면 종종 여기에 설명 된 것처럼 강력하고 __ 약한 참조를 관리해야한다는 것입니다


답변

주어진 많은 답변에 좋은 정보가 있지만 질문을 완전히 다루지는 않습니다.

이 질문은 뷰 컨트롤러간에 정보를 전달하는 방법을 묻습니다. 주어진 특정 예제는 뷰 사이에 정보를 전달하는 것에 대해 묻지 만 자체적으로 새로운 기능을 iOS에 제공하면 원래 포스터는 viewController가 아닌 viewController가 아닌 viewControllers 사이를 의미했을 가능성이 높습니다. 모든 답변이 두 개의 뷰 컨트롤러에 중점을 둔 것처럼 보이지만 앱이 정보 교환에 두 개 이상의 뷰 컨트롤러를 포함해야한다면 어떻게 될까요?

원래 포스터는 또한 싱글 톤AppDelegate 의 사용에 대해 물었습니다. . 이 질문에 답해야합니다.

완전한 답변을 원하는이 질문을보고있는 다른 사람을 돕기 위해 나는 그것을 제공하려고 노력할 것입니다.

응용 프로그램 시나리오

매우 가설적이고 추상적 인 토론을하는 대신 구체적인 적용을 염두에 두는 데 도움이됩니다. 2 뷰 컨트롤러 상황과 2 뷰 이상의 컨트롤러 상황을 정의하는 데 도움이되도록 두 가지 구체적인 애플리케이션 시나리오를 정의하겠습니다.

시나리오 1 : 최대 2 개의 뷰 컨트롤러가 정보를 공유해야합니다. 다이어그램 1을 참조하십시오.

원래 문제의 다이어그램

응용 프로그램에는 두 개의 뷰 컨트롤러가 있습니다. ViewControllerA (데이터 입력 양식) 및 View Controller B (제품 목록)가 있습니다. 제품 목록에서 선택한 항목은 데이터 입력 양식의 텍스트 상자에 표시된 항목과 일치해야합니다. 이 시나리오에서 ViewControllerA와 ViewControllerB는 다른 뷰 컨트롤러가 아닌 서로 직접 통신해야합니다.

시나리오 2 : 개 이상의 뷰 컨트롤러가 동일한 정보를 공유해야합니다. 다이어그램 2를 참조하십시오.

주택 재고 응용 프로그램 다이어그램

애플리케이션에는 4 개의 뷰 컨트롤러가 있습니다. 주택 재고 관리를위한 탭 기반 응용 프로그램입니다. 세 개의 뷰 컨트롤러는 동일한 데이터에 대해 다르게 필터링 된 뷰를 제공합니다.

  • ViewControllerA-럭셔리 아이템
  • ViewControllerB-비보험 품목
  • ViewControllerC-집 전체 재고
  • ViewControllerD-새 항목 양식 추가

개별 항목을 생성하거나 편집 할 때마다 다른 뷰 컨트롤러와도 동기화해야합니다. 예를 들어, ViewControllerD에 보트를 추가했지만 아직 보험에 가입되지 않은 경우 사용자가 ViewControllerA (고급 품목)로 이동하면 ViewControllerC (전체 주택 재고)로 이동해야하지만 보트는 출국하지 않아야합니다. ViewControllerB (비보험 품목). 새 항목 추가뿐만 아니라 항목 (4 개의보기 컨트롤러 중 하나에서 허용 될 수 있음)을 삭제하거나 기존 항목 ( “새 항목 추가”에서 허용 될 수 있음)을 변경하여 동일한 항목을 변경하는 것에 대해 걱정할 필요가 있습니다. 편집 용).

모든 뷰 컨트롤러가 동일한 데이터를 공유해야하므로 4 개의 뷰 컨트롤러는 모두 동기화 상태를 유지해야하므로 단일 뷰 컨트롤러가 기본 데이터를 변경할 때마다 다른 모든 뷰 컨트롤러와 통신해야합니다. 이 시나리오에서는 각 뷰 컨트롤러가 서로 다른 뷰 컨트롤러와 직접 통신하는 것을 원하지 않습니다. 분명하지 않은 경우, 4 개가 아닌 20 개의 서로 다른 뷰 컨트롤러가 있는지 고려하십시오. 하나의 뷰 컨트롤러가 변경 될 때마다 다른 19 개의 뷰 컨트롤러 각각에 알리는 것이 얼마나 어렵고 오류가 발생합니까?

솔루션 : 대리인과 관찰자 패턴 및 싱글 톤

시나리오 1에는 다른 답변과 같이 몇 가지 실용적인 솔루션이 있습니다.

  • segues
  • 대의원
  • 뷰 컨트롤러에서 직접 속성 설정
  • NSUserDefaults (실제로 좋지 않은 선택)

시나리오 2에는 다른 실행 가능한 솔루션이 있습니다.

  • 관찰자 패턴
  • 싱글 톤

싱글은 인스턴스의 수명 동안 존재하는 유일한 경우 인 것을, 클래스의 인스턴스입니다. 싱글 톤은 단일 인스턴스라는 사실에서 이름을 얻습니다. 일반적으로 싱글 톤을 사용하는 개발자는 액세스 할 수있는 특수 클래스 메소드를 갖습니다.

+ (HouseholdInventoryManager*) sharedManager; {
    static dispatch_once_t onceQueue;
    static HouseholdInventoryManager* _sharedInstance;

    // dispatch_once is guaranteed to only be executed once in the
    // lifetime of the application
    dispatch_once(&onceQueue, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

이제 싱글 톤이 무엇인지 이해 했으므로 싱글 톤이 옵저버 패턴에 어떻게 맞는지 논의하겠습니다. 관찰자 패턴은 한 객체가 다른 객체의 변경에 응답하는 데 사용됩니다. 두 번째 시나리오에는 기본 데이터의 변경 사항에 대해 알고 싶은 4 개의 서로 다른 뷰 컨트롤러가 있습니다. “기본 데이터”는 단일 인스턴스 인 싱글 톤에 속해야합니다. “변경에 대한 정보”는 싱글 톤의 변경 사항을 관찰함으로써 달성됩니다.

주택 재고 응용 프로그램에는 재고 품목 목록을 관리하도록 설계된 클래스의 단일 인스턴스가 있습니다. 관리자는 가정 용품 컬렉션을 관리합니다. 다음은 데이터 관리자에 대한 클래스 정의입니다.

#import <Foundation/Foundation.h>

@class JGCHouseholdInventoryItem;

@interface HouseholdInventoryManager : NSObject
/*!
 The global singleton for accessing application data
 */
+ (HouseholdInventoryManager*) sharedManager;


- (NSArray *) entireHouseholdInventory;
- (NSArray *) luxuryItems;
- (NSArray *) nonInsuredItems;

- (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item;
@end

주택 재고 항목의 수집이 변경 될 때 뷰 컨트롤러는이 변경을 인식해야합니다. 위의 클래스 정의는 이것이 어떻게 일어날 지 명확하게하지 않습니다. 관찰자 패턴을 따라야합니다. 뷰 컨트롤러는 공식적으로 sharedManager를 관찰해야합니다. 다른 물체를 관찰하는 두 가지 방법이 있습니다 :

  • 키-값-관찰 (KVO)
  • NSNotificationCenter.

시나리오 2에서는 KVO를 사용하여 관찰 할 수있는 가정용 인벤토리 관리자의 단일 속성이 없습니다. 관찰하기 쉬운 단일 속성이 없으므로이 경우 관찰자 패턴은 NSNotificationCenter를 사용하여 구현해야합니다. 4 개의보기 컨트롤러 각각은 알림을 구독하고, sharedManager는 적절한 경우 알림 센터에 알림을 보냅니다. 재고 관리자는 재고 항목의 콜렉션이 변경되는시기를 알고 싶어 할 수있는 다른 클래스의보기 제어기 또는 인스턴스에 대해 아무것도 알 필요가 없습니다. NSNotificationCenter는 이러한 구현 세부 사항을 처리합니다. View Controller는 단순히 알림을 구독하고 데이터 관리자는 단순히 알림을 게시합니다.

많은 초보자 프로그래머는 항상 정확히 하나의 응용 프로그램 위임 이 있다는 사실을 이용합니다. 는 응용 프로그램 수명 동안 이 있으며 이는 전 세계적으로 액세스 할 수 이용합니다. 초보자는이 사실을 사용하여 응용 프로그램의 어느 곳에서나 액세스 할 수있는 편의상 개체와 기능을 appDelegate에 넣습니다. AppDelegate가 싱글 톤이라고해서 다른 모든 싱글 톤을 교체해야한다는 의미는 아닙니다. 한 클래스에 너무 많은 부담을 주면서 좋은 객체 지향 사례를 깨뜨리는 것은 나쁜 습관입니다. 각 클래스는 종종 클래스 이름으로 쉽게 설명 할 수있는 명확한 역할을 가져야합니다.

Application Delegate가 부풀어 오르기 시작할 때마다 기능을 싱글 톤으로 제거하기 시작하십시오. 예를 들어, 핵심 데이터 스택은 AppDelegate에 남겨 두지 말고 대신 자체 클래스 인 coreDataManager 클래스에 배치해야합니다.

참고 문헌