본문 바로가기
iOS/미분류

[iOS] .a Library 만들고 사용해보기

by Mildwhale 2012. 9. 15.
1. 시작하기

  개발을 하다보면 '어? 이거 저번에 만들었던 것 같은데....?' 하는 생각이 들 때가 많이 있다. 그리고는 이전에 작업했던 소스를 뒤져서 찾아내고, 지금 개발하는 프로그램에 맞게 살짝 수정하여 재사용한다.

  이처럼 어느정도 개발을 하다보면 요구사항들을 이전의 경험으로 최대한 효율적으로 처리하려한다. 이 때, 두가지 정도의 방법이 있는데 하나는 Copy & Paste이고, 다른 하나는 자신만의 라이브러리를 만드는 것이다. 물론 클래스로 분리하여 사용해도 문제는 없지만 이를 배포할 때 문제가 생길 수도 있다.

  따라서 여기에서는 간단하게 Xcode를 이용하여 간단한 라이브러리를 만들고, 사용하는 방법을 알아보려고 한다.


2. Xcode 프로젝트 생성하기

  먼저 라이브러리 제작을 위해 프로젝트를 생성해야한다. 새 프로젝트 생성 창을 띄우고, Framework & Library 탭을 선택하여 'Cocoa Touch Static Library' 프로젝트를 생성한다.


  프로젝트를 생성하면 아무런 내용이 없는 header와 m파일이 포함된 프로젝트가 생성이 된다. 이제 이곳에 구현하고자 하는 기능을 구현하여 원하는 라이브러리를 만들면 되는 것 이다.



3. 원하는 기능 구현하기

  프로젝트의 생성이 끝나면 원하는 기능을 구현하면 된다. 여기에서는 NSLog를 이용하여 Hello World를 출력하는 간단한 기능을 가지고 있는 라이브러리를 생성해 볼 것이다.

  먼저 헤더파일에 구현하고자 하는 함수의 원형을 선언한다. 편의상 클래스 함수로 테스트 하기위해 + 로 선언해주었다.



  다음으로 함수를 구현해주어야 한다. 초라하지만 NSLog 한줄만 입력해준다.



 작업이 완료되었으면 빌드를 누른다.

※ Xcode는 디바이스와 시뮬레이터용 라이브러리를 따로 빌드하고, 사용해야한다. 따라서 테스트나 배포 시 매우 번거롭다. 따라서 이를 하나로 합쳐줄 수 있는 방법을 알아보자.

- Targets -> LibraryTest -> Build Phases Add Build Phase -> Add Run Script 를 클릭한다.

- 스크립트에 다음과 같은 내용을 써 넣는다.

# Version 2.0 (updated for Xcode 4, with some fixes) # Changes: # - Works with xcode 4, even when running xcode 3 projects (Workarounds for apple bugs) # - Faster / better: only runs lipo once, instead of once per recursion # - Added some debugging statemetns that can be switched on/off by changing the DEBUG_THIS_SCRIPT variable to "true" # - Fixed some typos # # Purpose: # Create a static library for iPhone from within XCode # Because Apple staff DELIBERATELY broke Xcode to make this impossible from the GUI (Xcode 3.2.3 specifically states this in the Release notes!) # ...no, I don't understand why they did this! # # Author: Adam Martin - http://twitter.com/redglassesapps # Based on: original script from Eonil (main changes: Eonil's script WILL NOT WORK in Xcode GUI - it WILL CRASH YOUR COMPUTER) # # More info: see this Stack Overflow question: http://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4 #################[ Tests: helps workaround any future bugs in Xcode ]######## # DEBUG_THIS_SCRIPT="false" if [ $DEBUG_THIS_SCRIPT = "true" ] then echo "########### TESTS #############" echo "Use the following variables when debugging this script; note that they may change on recursions" echo "BUILD_DIR = $BUILD_DIR" echo "BUILD_ROOT = $BUILD_ROOT" echo "CONFIGURATION_BUILD_DIR = $CONFIGURATION_BUILD_DIR" echo "BUILT_PRODUCTS_DIR = $BUILT_PRODUCTS_DIR" echo "CONFIGURATION_TEMP_DIR = $CONFIGURATION_TEMP_DIR" echo "TARGET_BUILD_DIR = $TARGET_BUILD_DIR" fi #####################[ part 1 ]################## # First, work out the BASESDK version number (NB: Apple ought to report this, but they hide it) # (incidental: searching for substrings in sh is a nightmare! Sob) SDK_VERSION=$(echo ${SDK_NAME} | grep -o '.\{3\}$') # Next, work out if we're in SIM or DEVICE if [ ${PLATFORM_NAME} = "iphonesimulator" ] then OTHER_SDK_TO_BUILD=iphoneos${SDK_VERSION} else OTHER_SDK_TO_BUILD=iphonesimulator${SDK_VERSION} fi echo "XCode has selected SDK: ${PLATFORM_NAME} with version: ${SDK_VERSION} (although back-targetting: ${IPHONEOS_DEPLOYMENT_TARGET})" echo "...therefore, OTHER_SDK_TO_BUILD = ${OTHER_SDK_TO_BUILD}" # #####################[ end of part 1 ]################## #####################[ part 2 ]################## # # IF this is the original invocation, invoke WHATEVER other builds are required # # Xcode is already building ONE target... # # ...but this is a LIBRARY, so Apple is wrong to set it to build just one. # ...we need to build ALL targets # ...we MUST NOT re-build the target that is ALREADY being built: Xcode WILL CRASH YOUR COMPUTER if you try this (infinite recursion!) # # # So: build ONLY the missing platforms/configurations. if [ "true" == ${ALREADYINVOKED:-false} ] then echo "RECURSION: I am NOT the root invocation, so I'm NOT going to recurse" else # CRITICAL: # Prevent infinite recursion (Xcode sucks) export ALREADYINVOKED="true" echo "RECURSION: I am the root ... recursing all missing build targets NOW..." echo "RECURSION: ...about to invoke: xcodebuild -configuration \"${CONFIGURATION}\" -target \"${TARGET_NAME}\" -sdk \"${OTHER_SDK_TO_BUILD}\" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO" xcodebuild -configuration "${CONFIGURATION}" -target "${TARGET_NAME}" -sdk "${OTHER_SDK_TO_BUILD}" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" ACTION="build" #Merge all platform binaries as a fat binary for each configurations. # Calculate where the (multiple) built files are coming from: CURRENTCONFIG_DEVICE_DIR=${SYMROOT}/${CONFIGURATION}-iphoneos CURRENTCONFIG_SIMULATOR_DIR=${SYMROOT}/${CONFIGURATION}-iphonesimulator echo "Taking device build from: ${CURRENTCONFIG_DEVICE_DIR}" echo "Taking simulator build from: ${CURRENTCONFIG_SIMULATOR_DIR}" CREATING_UNIVERSAL_DIR=${SYMROOT}/${CONFIGURATION}-universal echo "...I will output a universal build to: ${CREATING_UNIVERSAL_DIR}" # ... remove the products of previous runs of this script # NB: this directory is ONLY created by this script - it should be safe to delete! rm -rf "${CREATING_UNIVERSAL_DIR}" mkdir "${CREATING_UNIVERSAL_DIR}" # echo "lipo: for current configuration (${CONFIGURATION}) creating output file: ${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}" lipo -create -output "${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}" "${CURRENTCONFIG_DEVICE_DIR}/${EXECUTABLE_NAME}" "${CURRENTCONFIG_SIMULATOR_DIR}/${EXECUTABLE_NAME}" ######### # # Added: StackOverflow suggestion to also copy "include" files # (untested, but should work OK) # if [ -d "${CURRENTCONFIG_DEVICE_DIR}/usr/local/include" ] then mkdir -p "${CREATING_UNIVERSAL_DIR}/usr/local/include" cp "${CURRENTCONFIG_DEVICE_DIR}/usr/local/include/*" "${CREATING_UNIVERSAL_DIR}/usr/local/include" fi fi



- 스크립트 입력 후 다시 Build를 수행하고, 생성된 경로를 확인하기 위해 로그를 확인하거나, Product에 생성된 Library.a 를 Show in Finder로 열거나, 아래 경로로 찾아간다.

기본적인 위치는 /Users/{username}/Library/Developer/Xcode/DerivedData/{projectname}-{key}/Build/Products에 위치한다.


4. 라이브러리 사용하기

라이브러리의 통합 빌드까지 완료되면 이제 테스트 해볼 일만 남았다. 따라서 테스트를 해볼 프로젝트 하나를 생성한다. 여기서는 'LibraryAppTest' 라는 프로젝트를 생성하였다.

아래 스크린샷은 기본 프로젝트를 생성 후 라이브러리 파일과 해당하는 헤더파일을 추가한 상태이다.



다음 스크린샷은 viewDidLoad 함수에 Library의 클래스 함수인 libHelloWorld를 호출하는 구문을 삽입한 상태이다.



시뮬레이터에서 실행을 하면 아래와 같이 로그창에 'Hello World'가 출력된 것을 볼 수 있다.



5. 마치며

위에서 간단하게나마 라이브러리의 제작, 통합 빌드, 사용방법 등을 알아보았다. 실 사용에서도 자신이 사용하고자 하는 기능을 범용적으로 만들어 놓는다면 매우 유용한 방법이 되지 않을까 싶다.