3. Framework: weak linking

3. Framework: weak linking

이번 포스트에서는 저번 포스트에 이어서 Framework의 weak linking에 대해서 알아보려고 합니다.

weak linking이란?

먼저, 상황을 먼저 설명드리겠습니다. 최신 OS가 출시되면서 새로운 기능이 추가되었습니다. 이 기능은 당연히 이전 버전OS에서는 없는 기능이기 때문에 동작할 수 없습니다. 이전 버전 OS와 최신OS를 지원하는 framework를 제작하는데 최신OS에서 동작하는 새로운 기능도 지원하고 싶을 때 문제가 발생합니다. 새로운 기능은 이전 버전 OS에서는 동작하지 않기 때문에 오류가 발생합니다. 이 문제를 해결하기 위해서 나온 해결책이 weak linking입니다. weak linking은 심볼이 framework에 존재하지 않으면 null로 변경하고 심볼이 존재하면 동작을 수행합니다. weak linking을 통해 이전 OS의 호환성을 유지하면서 새로운 OS에 기능 추가를 할 수 있는 유연성을 발휘할 수 있습니다.

weak linking이 어떻게 동작하는지 예제를 통해서 알아보겠습니다.

WeakFramework를 Objective-C용 framework로 생성한 후 WeakTest.h와 WeakTest.c파일을 아래와 같이 생성합니다.

// WeakTest.h 파일

#ifndef WeakTest_h
#define WeakTest_h

#include <stdio.h>

#endif /* WeakTest_h */




// WeakTest.c 파일

#include "WeakTest.h"

void WeakFunc(void) {
    printf("Weak Func가 호출되었습니다.\n");
}

Sample App 프로젝트를 생성하고 ViewController.m파일에 아래와 같이 코드를 작성합니다.

#import "ViewController.h"

extern void WeakFunc(void) WEAK_IMPORT_ATTRIBUTE;

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    if (WeakFunc != NULL) {
        WeakFunc();
    } else {
        NSLog(@"WeakFunc가 존재하지 않습니다.");
    }    
}

@end

WeakFramework.framework를 Sample 프로젝트에 추가하고 아래와 같이 'Frameworks, Libraries, and Embeded Content’항목을 설정합니다.
enter image description here

샘플 프로젝트를 실행하면 아래와 같은 결과를 볼 수 있습니다.

// WeakFunc가 존재하지 않습니다.

이번에는 WeakFramework.framework를 아래와 같이 embed로 설정하겠습니다.
enter image description here

샘플 프로젝트를 실행하면 아래와 같은 결과를 볼 수 있습니다.

// Weak Func가 호출되었습니다.

WeakFramework를 linking만 하고 embed를 하지 않으면 런타임 시에는 WeakFunc 심볼이 존재하지 않기 때문에 WeakFunc는 NULL이 됩니다. 그래서 'WeakFunc가 존재하지 않습니다.'는 문구가 출력되었습니다. WeakFramework를 embed하면 런타임 시에 WeakFunc 심볼이 존재하기 때문에 해당함수를 동작시킬 수 있었습니다.


여기서 한발 더 나아가보겠습니다. 샘플프로젝트에서 WeakFramework를 linking하기 때문에 컴파일 타임에 WeakFunc심볼 관련 오류가 발생하지 않았습니다.
만약, WeakFramework가 아직 개발되지 않은 나중에 추가되는 기능인데 현재 작업하는 프로젝트에 미리 대비해서 추가하고 싶다면 어떻게 해야할가요?
일단, Sample 프로젝트에서 WeakFramework를 제거하고 빌드해보겠습니다.
enter image description here
그러면 위와 같은 빌드오류 메시지를 볼 수 있습니다. _WeakFunc 심볼이 정의되어 있지않아서 빌드오류가 발생합니다.
weak linking은 이전OS버전에서는 없는 새로운 기능을 추가할 때 유연함을 부여하는 기술인데 컴파일 타임에서도 가능하게 하는 방법은 없을가요?


먼저, xcode의 linker ld에 대해서 man 명령어로 알아보겠습니다.

man ld
-U symbol_name
Specified that it is ok for symbol_name to have no defini-
tion. With -two_levelnamespace, the resulting symbol will be
marked dynamic_lookup which means dyld will search all loaded
images.

-U옵션이 있는데 이 옵션을 사용하면 정의되지 않은 심볼을 사용할 수 있습니다.
한가지 더 알아야하는 명령어가 있는데 Objective-C 컴파일러인 clang입니다.

man clang
-Wl,
Pass the comma separated arguments in args to the linker.

clang을 man 명령어로 확인하면 linker에 인자 값을 전달할 수 있는 '-Wl’옵션이 있습니다.
이 옵션을 조합하면 linker에 정의되지 않은 심볼 정보를 전달할 수 있습니다.

-Wl,-U,<정의되지 않은 심볼이름>

그럼 이제 Sample 프로젝트가 빌드될 수 있도록 빌드 설정을 아래와 같이 ’-Wl,-U,_WeakFunc’ 값을 설정합니다.
enter image description here

Sample App을 실행하면 빌드가 성공하고 아래과 같이 출력됩니다.

// WeakFunc가 존재하지 않습니다.

weak framework

framework자체를 weak_framework로 만들수 있습니다.
framework 자체를 weak linking하는 방식으로 framework전체를 weak linking하여 해당 framework가 존재할 때 동작하고 없으면 동작하지 않는 방식입니다.
예제를 통해 확인해보겠습니다.

먼저, weak framework가 될 framework를 작성하겠습니다.
framework이름은 WeakFramework로 설정하고 WeakClass.h와 WeakClass.m파일을 아래와 같이 추가합니다.

// WeakClass.h 파일

#import <Foundation/Foundation.h>

@interface WeakClass : NSObject

- (void)execute;

@end




// WeakClass.m 파일

#import "WeakClass.h"

@implementation WeakClass

- (void)execute {
    NSLog(@"WeakClass의 execute메소드가 호출되었습니다.");
}
@end

이번에는 WeakFramework를 사용할 ReferenceFramework를 생성하겠습니다.
ReferenceFramework 프로젝트에 WeakFramework를 추가하고 'Build Phases’에서 아래와 같이 Link Binary With Libraries설정을 Optional로 변경합니다.
enter image description here
Status를 Optional로 변경하면 해당 framework를 weak_framework로 사용하겠다는 의미입니다.

ReferenceClass.h와 ReferenceClass.m파일을 추가하고 아래와 같이 코드를 작성합니다.

// ReferenceClass.h 파일

#import <Foundation/Foundation.h>

@interface RefereneClass : NSObject

- (void)execute;

@end



// ReferenceClass.m 파일

#import "RefereneClass.h"
@import WeakFramework;

@implementation RefereneClass

- (void)execute {
    NSLog(@"RefereneClass의 execute가 실행되었습니다.");
    if ([WeakClass class]) {
        [[[WeakClass alloc] init] execute];
    } else {
        NSLog(@"RefereneClass에서 WeakClass를 사용할 수 없습니다.");
    }
}

@end

WeakClass는 WeakFramework를 weak_framework로 설정했기때문에 weak linking으로 동작하며 따라서 실제 WeakClass를 사용할 때는 WeakClass가 동작가능한지 확인 후 사용해야합니다.

이제 ReferenceFramework를 사용하는 MySample App 프로젝트를 생성하겠습니다. ReferenceFramework를 추가하고 ‘Frameworks, Libraries, and Embedded Content’ 항목에서 Embed로 설정합니다.

그 다음 ViewController.m에서 아래처럼 ReferenceFramework를 사용하도록 코드를 작성합니다.

#import "ViewController.h"
@import ReferenceFramework;

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [[[RefereneClass alloc] init] execute];
}


@end


// 실행결과
// RefereneClass의 execute가 실행되었습니다.
// RefereneClass에서 WeakClass를 사용할 수 없습니다.

App을 실행해보면 RefereneClass는 동작하지만 WeakFramework를 추가하지 않았기 때문에 RefereneClass에서 WeakClass를 사용할 수 없습니다.

MySample App 프로젝트에 WeakFramework를 'Embed’로 추가고 실행하면 이번에는 RefereneClass안에서 WeakClass가 동작합니다.

// 실행결과
// RefereneClass의 execute가 실행되었습니다.
// WeakClass의 execute메소드가 호출되었습니다.

weak framework 기능이 잘 동작하는 것을 확인해봤습니다.
만약, ReferenceFramework에서 WeakFramework를 Optional이 아닌 Required로 설정하면 어떻게 될가요?
enter image description here
WeakFramework를 런타임에 로드해야하는데 로드하지 못해 오류가 발생합니다.

지금까지의 예제를 통해 weak_framework동작을 이해할 수 있습니다.
한 framework에서 다른 framework를 사용해서 기능을 확장하면 다른 framework를 사용하지 않더라도 필요하기 때문에 의존관계가 형성됩니다. 그런데 weak_framework를 사용하면 다른 framework의 기능을 사용하지 않을 때는 다른 framework를 추가하지 않고 사용할 수 있기 때문에 의존관계를 끊을 수 있습니다. weak_framework를 사용하면 다른 framework의 기능을 유연하게 추가할 수 있습니다.




weak linking은 새로운 기능을 추가할 때 유연하게 처리할 수 있도록 도와줍니다. 특히, weak_framework를 이용하면 사용할 framework의 추가만으로 기능을 확장할 수 있습니다.





참고
https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html#//apple_ref/doc/uid/10000183-SW1

댓글

이 블로그의 인기 게시물

dismiss에 대해서 알아봅시다

1. Framework: framework란?

closure에서는 왜 self를 사용해야 할까?