[Objective-C] edwith-1003

봄바야·2021년 3월 2일
0

Objective-C

목록 보기
3/3

MVC & Universal Application

MVC Overview

  • MVC 모델은 코코아 터치 프레임워크의 근간을 이루는 디자인 패턴
    • View : 화면에 보이는 모든 것
    • Model : logic, Data
    • Controller : 징검다리 역할! (view, model 사이가 물리적인 공간으로 분리가 된다.)
  • Universal Application : iphone & ipad 둘 다 지원
  • App Delegate 가 Controller 역할 느낌, UIWindow가 View 느낌, CandleModel(Model) 이런식으로 구성.

Example

Light the candle to MVC with ARC

  • CandleModel.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface CandleModel : NSObject{
  UIImage *imageCandleOn;
  UIImage *imageCandleOff;
  BOOL nowStatus;
}
- (UIImage *) imageCandleOn;
- (void) setImageCandleOn:(UIImage *)newImage;

- (UIImage *) imageCandleOff;
- (void) setImageCandleOff:(UIImage *)newImage;

- (BOOL) nowStatus;
- (void) setNowStatus:(BOOL)newStatus;

- (NSString *) stringNowStatus;
@end
  • CandleModel.m
#import "CandleModel.h"

@implementation CandleModel
//id면 컴파일 할 때 알 수 있는데(리턴 후에 값이 받아드려질 때) 
//instancetype이면 캔들모델이구나 하고 인식을 할 수 있음
- (instancetype) init{
    self = [super init];
    if (self){
        imageCandleOn = [UIImage imageNamed:@"candle on.jpg"];
        imageCandleOff = [UIImage imageNamed:@"candle off.jpg"];
        
        _strong UIImage *temp = [[UIImage alloc] init];
        //temp를 강하게 잡고 있음. if 문이 끝나면 사라지니까 내부적으로 인타임 모듈이 보내버려서 사라져버림.
        _weak UIImage *tempweak = temp;
        //temp 사라지면 다 사라짐
    }
    return self;
}

//ARC 사용하고 있어 컴파일러가 알아서 다 해줌 그래서 dealloc 안 씀..
//그래서 init이라는 생성자만 있고 소멸자를 안써줌
// - (void) deaaloc{
//     [super dealloc];
// }
@end
  • 클래스 인스턴스를 생성하는 방법
  1. 팩토리 메소드를 이용해서 생성하거나
  2. alloc이라고 하는 클래스 메소드를 이용해서 메모리에 인스턴스 자체를 만들고 init으로 초기화하는 방법이 있다.
  • ARC 에서는 객체를 관리하는 방법이 있다. 생성은 내 맘대로 해도 되는데 생성을 잡는 레퍼런싱 변수가 강하게(strong) 몇개를 잡고 있는지가 중요함. 객체가 죽지말라고 강하게 잡고 있는게 Strong 임!!
    • _strong 은 생략 가능함. 앞에 숨겨져 있는 것임.
    • 객체의 라이프사이클에 관여하지 않고 단순 참조 역할만 수행한다.
  • strong : 넘어 오는 아이를 강하게 잡음. 언제 사라지냐면 자신을 강하게 잡아 놓은 레퍼런스가 다 제거질 때 사라짐.
  • dangling pointers(댕글링) : 우리가 열심히 앱을 개발하다가 배드 억세스하다가 죽음. 어떤 객체가 필요해서 어떤 포인터/레퍼런스를 통해서 대상 객체에 갔는데 없어. 메모리에서 제거됐거나 해가지고. 그래서 주소는 있어서 갔는데 내가 원하는 개체가 없네 하면서 크래쉬가 발생함!!! 이게 댕글링포인터~!
    • weak라는 친구는 제로잉 위크 포인터라는 재밌는 기능이 있음.
    • strong은 자기가 레퍼런스하는 객체의 라이플 사이클에 관여를 한다!

strong & weak 예시 코드

@interface CandleModel(){
    _weak UIImage *tempWeak;
}
@end
@implementation CandleModel
- (instancetype) init{
    self = [super init];
    if (self){
        imageCandleOn = [UIImage imageNamed:@"candle on.jpg"];
        imageCandleOff = [UIImage imageNamed:@"candle off.jpg"];
        
        _strong UIImage *temp = [[UIImage alloc] init];
        tempWeak = temp;
        NSLog(@"%@", tempWeak); 
        //이미지 객체의 주소가 찍히겠지. 지역변수 안에서 strong하게 잡고 있는 temp를 가리키고 있으니까
    }
    NSLog(@"%@", tempWeak);
    //근데 If 문 구간 벗어나면 temp가 pop된다. 그럼 if문 바깥의 tempWeak는 nil값이 들어옴
    return self;
}
@end
  • zeroing weak pointer(제로잉) : 객체는 strong에서 pop해서 없어짐. 객체는 사라짐. weak으로 하면 컴파일러가 럼타임 모듈이 알아서 판단함. 없는걸 알고 nil값을 넣어줌. dangling pointers를 알아서 해결해주는 것임.

header에 대해서 getter, setter 를 구현하는 코드 (m파일 implement 뒤에 추가)

- (UIImage *) imageCandleOn{
    return imageCandleOn;
}
- (void) setImageCandleOn:(UIImage *)newImage{
    imageCandleOn = newImage;
}

- (UIImage *) imageCandleOff{
    return imageCandleOff;
}
- (void) setImageCandleOff:(UIImage *)newImage{
    imageCandleOff = newImage;
}

- (BOOL) nowStatus{
    return nowStatus;
}
- (void) setNowStatus:(BOOL)newStatus{
    nowStatus = newStatus;
}

- (NSString *) stringNowStatus{
    NSString *rValue = nil;
    if (nowStatus == FALSE){
        rValue = @"Candle is Off..";
    }
    else{
        rValue = @"Candle is On!!";
    }
    return rValue;
}
  • getter는 return을 하고 setter 멤버에다가 넘어온거에 대해서 레퍼런싱 혹은 어사인을 해주는걸 하고 있음.
    • 멤버에 대해서 , 외부에 대해서 접점을 만들어주고 있음. 그래서 getter 는 그냥 멤버변수를 리턴하고 setter에 대해서 넘어온 아이를 그냥 set 해주고 있음.

이제 기존에 있었던 App delegate 와 연결해서 붕어빵을 찍어보자!

  • AppDelegate.h
#import <UIKit/UIKit.h>
@class CandleModel;

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
    IBOutlet UIImageView *viewImage;
    IBOutlet UILabel *labelStatus;
    IBOutlet UISwitch *switchNow;
    
    CandleModel *modelCandle;
}
@property (strong, nonatomic) IBOutlet UIWindow *window;
- (IBAction)touchSwitch:(id)sender;
@end
  • 모델을 사용할 때 import를 보통 사용하지만 헤더의 사이즈도 줄이고 import가 중복포함도 막아주지만, 중복포함 없이 객체를 좀 더 명확하게 물리적으로 분리하기 위해서 @class 라고 선언한다.

  • @class (앳 클래스) : 캔들 모델에 클래스의 정의는 우리 프로젝트의 어딘가에 정의 되어 있으니까(somewhere), 컴파일 시에 있다고 퉁치고 넘어가자. 그럼 언제 실질적으로 여기에 대한 자료형을 지정하느냐! , 런타임 모듈상에서 모델 캔들의 객체가 레퍼런스 되었을 때 그때 명시하자. (캔들 모델의 존재함을 나타냄 )

  • AppDelegate.m

#import "CandleModel.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    modelCandle = [[CandleModel alloc] init];//캔들모델 인스턴스 생성
}
  • AppDelegate에 엔트리 포인트는 didfinishlaunchingwithoptions 임. 그래서 여기에 캔들모델을 인스턴스 생성한다.

App 실행 follow up

  1. AppDelegate.h 에서 didFinishLaunchingWithOptions 추가적인 작업을 수행한다. 초기화 수행하면서
  2. CandleModel.m 의 init 부분이 수행. self = [super init]; 가 부모인 NSObject 에 이니셜라이저를 실행하고 제대로 받아왔다. → 자기 자신에 대한 초기화 진행
  3. AppDelegate.m 에서 첫번째 메소드 안에 실행 순서대로 CandleModel.m과 왔다갔다 하면서 실행 된다.
    (ex. AppDelegate.m의 [modelCandle setNowStatus:nowStatus]; → CandleModel.m의 setNowStatus 메소드)
  • __unsafe_unretained : zeroing weak pointer가 적용이 안 된 과거의 포인터 선언이라고 보면 된다. 그럼 zeroing weak pointer 가 실행 안돼. → 그래서! dangling pointers가 발생해
    (weak지만 zeroing weak pointer없는 포인터!)

Property with Collection Object

property

  • property :
    클래스의 멤버변수들에 대해서 property로 재선언할 수 있다. 그럼 property는 property 내부의 또다른 속성인 attribute에 의해서 getter, setter가 자동으로 선언된다.

    • 클래스의 멤버변수에 대해서 모든 property는 attribute가 존재한다.
  • CandleModel.h

- (UIImage *) imageCandleOn;
- (void) setImageCandleOn:(UIImage *)newImage;
//위를 프로퍼티로 선언하면 아래 코드가 됨!!!
@property (nonatomic, strong) UIImage *imageCandleOn; //getter, setter가 자동으로 선언

- (BOOL) nowStatus;
- (void) setNowStatus:(BOOL)newStatus;
//위 아래 같은 의미
@property (nonatomic, assign) BOOL nowStatus; // bool은 스칼라 값이라 assign
  • property 장점 :
    property로 선언된 멤버변수에서 synthesize선언하면 property의 attribute에 맞춰서 getter, setter 억세스 메서드가 자동으로 구현된다.
  • CandleModel.m
//직접생성
- (UIImage *) imageCandleOn{
    return imageCandleOn;
}
- (void) setImageCandleOn:(UIImage *)newImage{
    imageCandleOn = newImage;
}

//위에 구현 코드를 안쓰고 synthesize로 선언만 해주면 된다. 같은 의미임.
@synthesize imageCandleOn; //getter, setter의 상태에 따라 자동으로 구현 가능
  • 기능 추가하기 위해서 자동생성 쓰기 싫으면 (위에 코드처럼)직접 생성하면 된다!
  • 장점 2 (모던 런타임 모듈로 넘어오면서 생김):
    • 2-1 . 클래스의 멤버변수를 생성하지 않고 그냥 프로퍼티로만 생성하면 자동으로 알아서 클래스 안에 멤버변수도 선언해주고 getter, setter 억세스 메서드도 알아서 선언을 해준다…
    • 2-2. synthesize도 알아서 생성해준다…
      이렇게 프로퍼티로만 적어두면 내부적으로는 _ (언더바)를 앞에 붙여줘서 선언해줘야한다!

그래서 위에 코드들이 어떻게 바뀌냐면

@interface CandleModel : NSObject{
  UIImage *imageCandleOn;
  UIImage *imageCandleOff;
  BOOL nowStatus;
}
- (UIImage *) imageCandleOn;
- (void) setImageCandleOn:(UIImage *)newImage;
@end

↓👇

@interface CandleModel : NSObject
@property (nonatomic, strong) UIImage *imageCandleOn;
@end
@implementation CandleModel
- (instancetype) init{
    self = [super init];
    if (self){
        _imageCandleOn = [UIImage imageNamed:@"candle on.jpg"]; //언더바 추가
        _imageCandleOff = [UIImage imageNamed:@"candle off.jpg"];
    }
}
@end

그런데..! 굳이 내가 _가 붙은 이름을 쓰고 싶지 않아 🤔 → 이럴 때 @synthesize 를 사용해용

  • CandleModel.m
    @synthesize imageCandleOn = qwerty; //그럼 멤버변수의 이름에 대해서 내부적으로는 qwerty 써줘
  • CandleModel.h
    @interface CadleModel : NSObject{ UIImage *qwerty; }

property Attribute

  • property attribute에 맞춰서 getter, setter Access Method가 만들어진다.
  • strong / weak / retain / copy / assign :
    setter를 어떻게 만들까 이다. 근데 ARC에서는 하나만 쓴다.
    • strong을 쓰면 → 멤버변수를 strong으로 해라!
      • __strong UIImage *_imageCandleOff;
    • weak은 __weak UIImage *_imageCandleOff;
    • retain은 strong이랑 똑같음
    • assign도 strong으로 동작하게 되어있음.
    • copy :
      멤버는 똑같이 strong으로 동작하는데 setter에 대해서 copy를 주게 되어있다. 넘어오는 인자에 대해서 카피를 줌.

COPY

  • 복사하는 법

    만약, b가 얕은 복사(shallow copy)를 했다면?
    일단은 다른 객체를 갖는 것처럼 보임. 근데 a가 멤버변수를 수정하면 b도 바뀜.

    이번에는 깊은 복사 (deep copy) 하면?
    모든 걸 메모리 상에 완전히 다른 객체를 만들어버림.

  • assign : 단순할당 일 때는 하나의 객체를 인스턴스를 모두 같이 참조한다.

  • shallow copy : 최상단에 있는 애들만 일단 복사가 되고 내부의 멤버는 모두 공유한다.

  • deep copy : 아이덴티컬하게 아예 분리되서 복사된다.

Objective-C 에서의 Copy

NSCOPYING PROTOCOL

  • copy에 대한 고찰.
    • [NSObject copy];
      -[NSObject copyWithZone:(id)zone];
      • copyWithZone : 에 대한 재정의를 해야 함.
    • if not
      • nil 반환됨
  • NSCOPYING PROTOCOL안에 있는 함수인 copyWithZone을 구현해주면 카피가 된다.

CandleModel.m

- (void) setImageCandleOff:(UIImage *)newImage{
    _imageCandleOff = [newImage copy];
}
  • 근데 UIImage 레퍼런스 들어가보면 NSCopying 만족을 안함. 그래서 nil 반환함. NSString은 만족함.

  • 그래서 @property (nonatomic, copy) UIImage *imageCandleOff; 를 하면 setter가 구현이 된다.

  • getter , setter 이름 바꿔주고 싶어
    @property (nonatomic, strong, getter=hahaha, setter=hohoho:) UIImage *imageCandleOff;

  • readwrite : 기본적인 옵션, getter , setter 모두 다 만들어줌

  • readonly : 그냥 참조만 하세요.

  • nonatomic : setter가 코딩이 된다.

  • atomic : 멤버 접근에 대해서 뮤택스가 설정이 된다. 단일 스레드를 구현할 때는 의미가 없어서 보통 nonatomic 씀.

KVO vs. KVC

key value observing

  • observal가 우리에게 알려줌

key value coding

  • key 와 vlaue를 가지는 것. ex. 이름(key) : 홍길동(vlaue), 성별(key) : 남성(vlaue)
  • 배열도 key value coding이라고 하기도 함.

Collection object

  • 다른 객체를 담아 둘 수 있는 객체
    • Foundation 의 대표적인 collection object
    • NSArray : index, value로 이루어짐 (NSMutableArray : 내부의 값 삭제 가능)
    • NSDictionary : 특정 키에 맞춰서 저장하고 가져오고 (NSMutableDictionary)
    • NSSet : 만물 주머니 상자처럼 객체를 막 넣어놓고 anyobject같이 아무거나 가져와 같이 가져올 수도 있음 (NSMutableSet)
      • 위에 세개의 객체들은 → 한번 만들어지면 immutable 상태임
  • Foundation에서 상태를 두가지로 나눔
    • immutable : 한번 설정되면 바꿀 수 없음
    • mutable : 바꿀 수 있음

Convert to Universal Application

  • ipad 용 window 구현하는 법 설명

출처
https://www.edwith.org/iosbasic_mvc

0개의 댓글