^(코딩캣)^ = @"코딩"하는 고양이;
썸네일 이미지
[번역글] View Controller 사이에 데이터를 교환하는 방법(2) - segue
본 게시글은 How To: Pass Data Between View Controllers in Swift를 바탕으로 작성하였습니다. 여러분의 앱이 여러 개의 사용자 인터페이스(UI)를 가지고 있다면 여러분은 하나의 UI에서 다른 UI로 데이터를 전달해야 하는 경우도 생길 것입니다. Swift에서는 View Controller 사이에 어떤 방법으로 데이터를 전달할 수 있을까요? 뷰 컨트롤러(View Controller) 사이에 데이터를 주고 받는 것은 iOS 개발의 중요한 일부입니다. 여러분은 몇 가지 방법으로 이를 해낼 수 있고 각기 다른 이점과 약점을 가지고 있습니다. 뷰 컨트롤러 사이에 쉽게 데이터를 교환하는 방법을 선택하는 것은 여러분이 앱 구조를 어떻게 할 것인지에 달려 있습니다. 앱의 구조(Ap..
Language/Objective-C & Swift
2021. 9. 14. 11:33

[번역글] View Controller 사이에 데이터를 교환하는 방법(2) - segue

Language/Objective-C & Swift
2021. 9. 14. 11:33

본 게시글은 How To: Pass Data Between View Controllers in Swift를 바탕으로 작성하였습니다.

 

여러분의 앱이 여러 개의 사용자 인터페이스(UI)를 가지고 있다면 여러분은 하나의 UI에서 다른 UI로 데이터를 전달해야 하는 경우도 생길 것입니다. Swift에서는 View Controller 사이에 어떤 방법으로 데이터를 전달할 수 있을까요?

뷰 컨트롤러(View Controller) 사이에 데이터를 주고 받는 것은 iOS 개발의 중요한 일부입니다. 여러분은 몇 가지 방법으로 이를 해낼 수 있고 각기 다른 이점과 약점을 가지고 있습니다.

뷰 컨트롤러 사이에 쉽게 데이터를 교환하는 방법을 선택하는 것은 여러분이 앱 구조를 어떻게 할 것인지에 달려 있습니다. 앱의 구조(App architecture)는 여러분이 뷰 컨트롤러 사이에 어떻게 작동이 이루어 질 것인지에 영향을 주고, 반대로 여러분이 뷰 컨트롤러 사이에 데이터 교환을 어떻게 할 것인지에 따라 앱의 구조가 달라집니다.

 

Swift에서 여러분은 뷰 컨트롤러 사이에 다음의 6가지 방법으로써 데이터를 주고 받을 수 있습니다.

  1. 인스턴스 프로퍼티(property)를 사용하는 방법(A → B 방향)
  2. 스토리보드(Storyboard)와 세그웨(segue)를 사용하는 방법
  3. 인스턴스 프로퍼티와 함수를 사용하는 방법(A ← B 방향)
  4. 델리게이션(delegation) 패턴을 사용하는 방법
  5. 클로저(closure) 또는 핸들러(completion handler)를 사용하는 방법
  6. NotificationCenter 또는 Observer 패턴을 사용하는 방법

 

이 게시글에서 여러분은 뷰 컨트롤러 사이에 데이터를 주고 받는 6가지의 각기 다른 방법에 대해 익히게 될 것입니다. 이 방법에는 프로퍼티(property)를 사용하는 방법, 세그웨(segue)를 사용하는 방법 및 NSNotificationCenter를 사용하는 방법도 들어 있습니다. 비교적 간단한 이들 방법을 통해 좀 더 복잡한 응용까지 나아갈 수 있습니다. 준비가 되었다면 이제 시작하겠습니다.

 

스토리보드(Storyboard)와 세그웨(segue)를 사용하는 방법(A → B 방향)

스토리보드를 사용하고 있다면 여러분은 세그웨(segue)prepare(for:sender:) 메소드를 사용하여 뷰 컨트롤러 사이에 데이터를 전달할 수 있습니다.

스토리보드와 세그웨를 사용하여 뷰 컨트롤러 사이에 데이터를 전달하는 것은 앞서 XIB를 사용한 방법과 크게 다르지 않습니다. 스토리보드와 세그웨의 의미를 간단히 짚고 넘어가자면, 스토리보드는 여러분의 앱이 갖는 사용자 인터페이스들의 집합체입니다. Xcode의 Interface Builder를 가지고 생성할 수 있고 코드 작성을 최소화면서 뷰 컨트롤러 사이의 전환을 만들 수 있습니다.

세그웨(segue)는 부드러운 화면 전환(smooth transition)을 뜻합니다. 여러분이 예를 들어 내비게이션 컨트롤러를 가지고 어떤 뷰 컨트롤러에서 다른 뷰 컨트롤러로 전환시킬 때, 여러분은 이미 세그웨를 만든 것입니다. 여러분의 뷰 컨트롤러에서 이 세그웨에 후킹(hooking)하여 그 작동을 수정할 수도 있습니다. 뷰 컨트롤러 사이에서 발생하는 데이터 교환도 세그웨 과정 도중에 발생합니다.

이번 예제에서 여러분은 MainViewController에서 TertiaryViewController로 전환되는 세그웨를 보게 될 것입니다. 이를 위해 어떤 액션을 한 버튼에서 다른 뷰 컨트롤러로 이어준 다음 "Show" 유형을 선택합니다. 그러면 MainViewController에서 TertiaryViewController로 이어지는 화살표가 나타날 것입니다. 그 다음 뷰 컨트롤러의 "Identity Inspector"에서 커스텀 클래스를 TertiaryViewController로 지정합니다.

TertiaryViewController의 코드는 다음과 같습니다.

// Swift
class TertiaryViewController: UIViewController {
    var username: String = ""
    
    @IBOutlet weak var usernameLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        usernameLabel.text = username
    }
}
// Objective-C
@interface TertiaryViewController: UIViewController {
    @property (atomic, strong) NSString * username;
    @property (nonatomic, weak) IBOutlet UILabel * usernameLabel;
@end
@implement TertiaryViewController
    @synthesize username;
    @synthesize usernameLabel;
    
    -(void) viewDidLoad {
        [super viewDidLoad];
        [[self usernameLabel] setText: [self username]];
    }
}

 

특별할 것도 없이 이 예제는 앞서 살펴보았던 것과 크게 다르지 않습니다. 여러분은 이 예제를 통해 usernameLabel이라는 라벨의 텍스트를 username이라는 프로퍼티가 현재 갖고 있는 문자열로 설정한 것일 뿐입니다.

이제 MainViewController에서 TertiaryViewController로 데이터를 전달해 보겠습니다. 이 때 prepare(for:sender:)라 불리는 특별한 메소드를 사용해야 하는데 이 메소드는 세그웨를 하기 전에 호출되는 메소드이므로 여러분은 이를 사용자화할 수 있습니다. 세그웨 직전 수행하게 될 코드는 다음과 같습니다.

// Swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if (segue.destination is TertiaryViewController) {
        let tertiaryViewController = segue.destination as? TertiaryViewController
        tertiaryViewController.username = "Arthur Dent"
    }
}
// Objective-C
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue destination] isKindOfClass: [TertiaryViewController class]]) {
        TertiaryViewController * tertiaryViewController = (TertiaryViewController *)[segue destination];
        [tertiaryViewController setUsername: @"Arthur Dent"];
    }
}

 

위 코드에서 어떤 작업들이 수행되는지 살펴보겠습니다.

먼저 if문과 is 키워드는 세그웨의 목적지가 TertiaryViewController 클래스형인지를 검사하게 해 줍니다. 모든 세그웨는 이 prepare(for:sender:) 메소드를 거쳐가기 때문에 이 세그웨가 여러분이 사용자화하고 싶은 그 세그웨인지 아닌지를 검사할 필요가 있습니다.

그 다음 username 프로퍼티를 사용하기 위하여 segue.destinationTertiaryViewController 클래스형으로 형변환합니다. segue 매개변수를 통해 전달되는 destination 프로퍼티는 UIViewController 형식으로 되어 있기 때문에 TertiaryViewController에서 선언한 username 프로퍼티를 사용하려면 이처럼 다운캐스트해야 합니다.

마지막으로 username 프로퍼티를 설정합니다. 이는 앞서 살펴 본 예제와 동일합니다.

prepare(for:sender:) 메소드에 대한 재미있는 사실은 여러분이 이 메소드에서 특정 세그웨 이외의 다른 것에 대해서는 작업할 필요가 없다는 것입니다. 이 메소드는 단순히 세그웨에 훅(hook)되기 때문에 화면 전환을 위해 이어서 나머지 작업을 수행하도록 어떤 코드들을 작성할 필요가 없다는 것입니다. 여러분은 여러분이 새로 구성한 뷰 컨트롤러를 반환할 필요도 없습니다.

또한 여러분은 위 Swift 코드를 다음과 같이 축약할 수도 있습니다.

// Swift
if let tertiaryViewController = segue.destination as? TertiaryViewController {
    tertiaryViewController.username = "Ford Perfect"
}

 

destination의 타입을 검사하기 위해 is를 사용하고 캐스트하는 대신 옵셔널 캐스팅(optional casting)을 사용하여 한 문장으로 처리할 수도 있습니다. segue.destinationTertiaryViewController 타입이 아니라면 as? 식은 nil을 반환할 것이고 조건문에 딸린 문장은 실행되지 않을 것입니다.

만일 형변환(type casting)을 사용하고 싶지 않다면 segue.identifier 프로퍼티로 특정 세그웨를 식별할 수도 있습니다. 예를 들어 스토리보드에서 이 프로퍼티에 "tertiaryVC"라는 문자열을 부여한 다음 다음과 같이 코드를 작성할 수도 있습니다.

// Swift
if segue.identifier == "tertiaryVC" {
    // 수행할 작업
}
// Objective-C
if ([[segue identifier] isEqualToString:@"tertiaryVC"]) {
    // 수행할 작업
}

 

세그웨와 스토리보드를 사용하여 두 개의 뷰 컨트롤러 사이에 데이터를 전달하는 방법은 여기까지입니다.

대다수의 앱에서 스토리보드는 여러분이 사용할 수 있는 뷰 컨트롤러 사이의 전환을 제한합니다. 스토리보드는 종종 Xcode에서 사용자 인터페이스 만드는 것을 과도하게 복잡하게 만들기도 하는데 이에 비해 얻는 이점은 다소 적습니다. 무엇보다도 여러분이 복잡한 구조로 스토리보드나 XIB를 작성한다면 Interface Builder는 느려지고 버벅거립니다.

스토리보드로 작성할 수 있는 모든 것은 사실 여러분이 직접 손으로 코딩하여 구현할 수도 있습니다. 이 때는 개발자가 약간의 노력만 한다면 보다 상세한 제어가 가능합니다. 그렇다고 해서 필자는 여러분에게 손으로 일일이 다 코딩하라고 말하는 것은 아닙니다! 위의 예제처럼 하나의 뷰 컨트롤러당 하나의 XIB를 작성하고, UITableViewCell과 같은 서브 클래스 뷰(sub class view) 하나당 하나의 XIB를 작성합니다.

코더로서 궁극적으로 여러분은 탭 문자로 들여쓸 것인지 스페이스로 들여쓸 것인지, 스토리보드를 사용할 것인지 XIB를 사용할 것인지 아니면 Core Data를 쓸 것인지 Realm을 쓸 것인지 등등의 갈림길에서 여러분만의 최선의 선택을 찾고 싶어질 것입니다. 그것은 전적으로 여러분에게 달려 있습니다.

흥미로운 사실 모든 개발자들은 “segue”라는 단어를 자신만의 방법대로 발음하고 있습니다. 몇몇은 “se-” 부분을 “say”나 “set”의 “-e-”처럼 “-에-”로 발음하고, “-gue” 부분을 “gue_rilla”처럼 “-그웨”와 같이 발음합니다. 다른 이들은 “segway”처럼 “세그웨이”로 발음하기도 합니다. (segway: 여행자들을 위한 개인형 이동수단의 일종으로서 한 쌍의 바퀴로만 되어 있고 스스로 균형을 잡는 기구)

카테고리 “Language/Objective-C & Swift”
more...
썸네일 이미지
[번역글] View Controller 사이에 데이터를 교환하는 방법(1) - property
본 게시글은 How To: Pass Data Between View Controllers in Swift를 바탕으로 작성하였습니다. 여러분의 앱이 여러 개의 사용자 인터페이스(UI)를 가지고 있다면 여러분은 하나의 UI에서 다른 UI로 데이터를 전달해야 하는 경우도 생길 것입니다. Swift에서는 뷰 컨트롤러(View Controller) 사이에 어떤 방법으로 데이터를 전달할 수 있을까요? 뷰 컨트롤러 사이에 데이터를 주고 받는 것은 iOS 개발의 중요한 일부입니다. 여러분은 몇 가지 방법으로 이를 해낼 수 있고 각기 다른 이점과 약점을 가지고 있습니다. 뷰 컨트롤러 사이에 쉽게 데이터를 교환하는 방법을 선택하는 것은 여러분이 앱 구조를 어떻게 할 것인지에 달려 있습니다. 앱의 구조(App archite..
Language/Objective-C & Swift
2021. 9. 13. 20:51

[번역글] View Controller 사이에 데이터를 교환하는 방법(1) - property

Language/Objective-C & Swift
2021. 9. 13. 20:51

본 게시글은 How To: Pass Data Between View Controllers in Swift를 바탕으로 작성하였습니다.

 

여러분의 앱이 여러 개의 사용자 인터페이스(UI)를 가지고 있다면 여러분은 하나의 UI에서 다른 UI로 데이터를 전달해야 하는 경우도 생길 것입니다. Swift에서는 뷰 컨트롤러(View Controller) 사이에 어떤 방법으로 데이터를 전달할 수 있을까요?

뷰 컨트롤러 사이에 데이터를 주고 받는 것은 iOS 개발의 중요한 일부입니다. 여러분은 몇 가지 방법으로 이를 해낼 수 있고 각기 다른 이점과 약점을 가지고 있습니다.

뷰 컨트롤러 사이에 쉽게 데이터를 교환하는 방법을 선택하는 것은 여러분이 앱 구조를 어떻게 할 것인지에 달려 있습니다. 앱의 구조(App architecture)는 여러분이 뷰 컨트롤러 사이에 어떻게 작동이 이루어 질 것인지에 영향을 주고, 반대로 여러분이 뷰 컨트롤러 사이에 데이터 교환을 어떻게 할 것인지에 따라 앱의 구조가 달라집니다.

 

Swift에서 여러분은 뷰 컨트롤러 사이에 다음의 6가지 방법으로써 데이터를 주고 받을 수 있습니다.

  1. 인스턴스 프로퍼티(property)를 사용하는 방법(A → B 방향)
  2. 스토리보드(Storyboard)와 세그웨(segue)를 사용하는 방법
  3. 인스턴스 프로퍼티와 함수를 사용하는 방법(A ← B 방향)
  4. 델리게이션(delegation) 패턴을 사용하는 방법
  5. 클로저(closure) 또는 핸들러(completion handler)를 사용하는 방법
  6. NotificationCenter 또는 Observer 패턴을 사용하는 방법

 

이 게시글에서 여러분은 뷰 컨트롤러 사이에 데이터를 주고 받는 6가지의 각기 다른 방법에 대해 익히게 될 것입니다. 이 방법에는 프로퍼티(property)를 사용하는 방법, 세그웨(segue)를 사용하는 방법 및 NSNotificationCenter를 사용하는 방법도 들어 있습니다. 비교적 간단한 이들 방법을 통해 좀 더 복잡한 응용까지 나아갈 수 있습니다. 준비가 되었다면 이제 시작하겠습니다.

 

프로퍼티를 사용하여 뷰 컨트롤러 사이에 데이터를 주고 받기(A → B 방향)

이미 언급했듯이 일부 접근법들은 데이터가 한 뷰 컨트롤러에서 다른 뷰 컨트롤러로 전달되는 단방향성을 갖습니다. 말하자면 그런 일부의 방법은 대칭(bilateral)적이지 않다는 것입니다. 하지만 염려할 것은 없습니다. 대부분의 경우 여러분은 단방향 통신을 필요로 할 것이기 때문입니다.

뷰 컨트롤러 A(현재의 화면)에서 뷰 컨트롤러 B(다음으로 넘어갈 화면)로 데이터를 넘기는 가장 쉬운 방법은 프로퍼티(property)를 사용하는 방법입니다. 프로퍼티는 그 클래스의 일부인 변수입니다. 그러한 클래스로부터 만들어진 모든 인스턴스는 따로따로 프로퍼티를 가지고 있고 여러분은 여기에 값을 배정할 수 있습니다. 다른 클래스와 마찬가지로 UIViewController의 파생형인 뷰 컨트롤러들은 프로퍼티를 가질 수 있습니다.

프로퍼티를 사용하여 뷰 컨트롤러 사이에 데이터를 전달하기.

다음과 같이 text라는 이름을 가진 프로퍼티가 있는 뷰 컨트롤러인 MainViewController가 있습니다.

// Swift
class MainViewController: UIViewController {
    var text: String = ""
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
// Objective-C
@interface MainViewController: UIViewController {
    @property (atomic, strong) NSString * text;
}

@implementation MainViewController {
    @synthesize text
    
    -(void) viewDidLoad {
        [super viewDidLoad];
    }
}

 

여러분이 MainViewController 클래스 인스턴스를 만들 때마다, 여러분은 다음과 같이 text 프로퍼티에 값을 배정할 수 있습니다.

// Swift
let mainViewController = MainViewController()
mainViewController.text = "Lorem Ipsum"
// Objective-C
MainViewController * mainViewController = [[MainViewController alloc] init];
[mainViewController setText: @"Lorem Ipsum"];

 

참 쉽죠? 이제 이 뷰 컨트롤러가 내비게이션 컨트롤러(Navigation Controller)의 일부라고 가정하고, 이 뷰 컨트롤러에서 다음에 등장할 뷰 컨트롤러로 어떤 값을 전달할 것입니다.

먼저 여러분이 스토리보드(Storyboard)를 사용하고 있다면 이 MainViewController에다가 내비게이션 컨트롤러를 삽입하고 싶을 것입니다. 스토리보드를 사용하고 있지 않다면 여러분은 내비게이션 컨트롤러를 새로 생성한 다음 MainViewController를 그것의 root view controller로 지정할 수 있습니다. 이미 내비게이션 컨트롤러가 적용되어 있다면 완벽합니다!

그 다음 여러분은 새로운 뷰 컨트롤러를 생성하기 위하여 .xib 파일도 생성하고, 서브 클래스도 생성합니다. 이것은 [File] 메뉴에서 [New File...] 항목을 선택 후 "Cocoa Touch Class"를 선택하여 SecondaryViewController라는 이름을 적음으로써 할 수 있습니다. .xib 파일이 생성될 때 "Also create XIB file" 체크박스에 체크하는 것을 절대 잊지 말기를 바랍니다.

뷰 컨트롤러에 대한 소스 코드는 다음과 같습니다.

class SecondaryViewController: UIViewController {
    var text: String = ""
    @IBOutlet weak var textLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        textLabel.text = text
    }
}
@interface SecondaryViewController: UIViewController {
    @property (nonatomic, weak) NSString * text;
}
@implementation SecondaryViewController {
    @synthesize text
    
    -(void)viewDidLoad {
        [super viewDidLoad];
        [self textLabel.setText: [self text]];
    }
}

 

.xib 파일에서 뷰(view)에 UILabel 요소를 추가한 다음 그 요소를 textLabel에 연결합니다. 그러면 여러분은 뷰 컨트롤러에 textLabel라는 이름의 프로퍼티도 가지고 있고, 그 프로퍼티에 연결된 Label 요소도 갖게 됩니다.

위 소스 코드에서도 보다시피 viewDidLoad() 메소드에서 textLabel에 있는 text 프로퍼티는 SecondViewController에 있는 text로부터 할당된 값입니다.

다음 코드에서는 실제로 데이터를 전해주고 있습니다. MainViewController에 다음과 같은 메소드를 추가합니다.

// Swift
@IBAction func onButtonTap() {
    let secondaryViewController(nibName: "SecondaryViewController", bundle: nil)
    secondaryViewController.text = "Lorem Ipsum"
    navigationController?.pushViewController(secondaryViewController, animated: true)
}
// Objective-C
-(IBAction)onButtonTap {
    SecondaryViewController * secondaryViewController = [[SecondaryViewCollector alloc] initWithNibName: "SecondaryViewController" 
                                                                                                 bundle: nil];
    [secondaryViewController setText: @"Lorem Ipsum"]
    [[self navigationController] pushViewController: secondaryViewController
                                           animated: TRUE];
}

MainViewController의 인터페이스에서 버튼을 추가하고 위의 액션에 이를 연결시킨다면, 버튼을 클릭했을 때 코드가 실행될 것입니다. 위 코드를 통해 어떤 일이 벌어지는지를 살펴보겠습니다.

secondaryViewController라는 이름의 변수를 선언하고 여기에 SecondaryViewController 클래스형 인스턴스를 대입합니다. 이 때 여러분은 뷰 컨트롤러가 올바른 XIB 파일을 사용하고 있음을 보증하기 위하여 생성자를 호출할 때 XIB의 정확한 이름을 명시하게 됩니다.

그 다음 secondaryViewControllertext 프로퍼티에 문자열을 대입합니다. 이 때가 뷰 컨트롤러 사이에 실질적으로 데이터를 교환하는 순간입니다!

마지막으로 pushViewController(_:animated:)를 사용하여 내비게이션 스택에 새로 만들어진 뷰 컨트롤러를 푸쉬(push)합니다. 이 때 발생하는 화면 변화에는 애니메이션이 지정되어 있으므로 여러분은 새로 만들어진 뷰 컨트롤러가 단말기 화면의 우측에서 미끄러져 들어오는(slide in) 효과를 보게 될 것입니다.

프로퍼티를 사용하여 뷰 컨트롤러 A에서 뷰 컨트롤러 B로 데이터를 전달하는 방법에 대해 다시 한 번 정리해보겠습니다.

  • 제1단계. 뷰 컨트롤러 B(데이터를 받아서 나중에 나타나는 화면)에서 데이터를 받기 위한 프로퍼티를 선언합니다. 위 예제에서 그 프로퍼티는 text라는 이름을 가졌습니다.
  • 제2단계. 뷰 컨트롤러 B에서 데이터가 변경될 때 수행할 작업을 기술합니다. 위 예제에서 여러분은 text를 통해 받은 문자열을 label로 출력시켰습니다.
  • 제3단계. 뷰 컨트롤러 A(먼저 나타나 있고 데이터를 넘겨 줄 화면)에서 뷰 컨트롤러 B에 대한 새 인스턴스를 만든 다음 프로퍼티에 소정의 값을 설정합니다. 그리고 내비게이션 스택으로 푸쉬(push)합니다.

여기까지가 프로퍼티를 사용한 방법이었습니다. 이어서 스토리보드에서 세그웨(segue)를 사용하여 동일한 기능을 구현해보겠습니다.

카테고리 “Language/Objective-C & Swift”
more...
Cocoa (Touch)에서 애니메이션 구현하기
Cocoa Touch에서 애니메이션 구현하는 방법을 정리해 본다. UIView.animate를 사용하여 애니메이션을 지정하기 UIView.animate에는 몇 가지 오버로드들이 있지만 하나의 애니메이션에 대해 섬세하게 지정할 수 있는 메소드를 기준으로 다음과 같이 사용할 수 있다. // Swift UIView.animate(withDuration: /* 총 소요시간 */, delay: /* 애니메이션 시작 전 뜸 들이는 시간 */, options: /* 애니메이션이 실행되는 동안 속도 변화 및 각종 옵션 */, animations: { /* 애니메이션 끝에 가서 나타날 효과를 기술한다. */ /* 그러면 애니메이션을 통해 서서히 여기서 기술한대로 각종 요소들이 변할 것이다. */ }, completion..
Language/Objective-C & Swift
2021. 9. 13. 20:21

Cocoa (Touch)에서 애니메이션 구현하기

Language/Objective-C & Swift
2021. 9. 13. 20:21

Cocoa Touch에서 애니메이션 구현하는 방법을 정리해 본다.

 

UIView.animate를 사용하여 애니메이션을 지정하기

UIView.animate에는 몇 가지 오버로드들이 있지만 하나의 애니메이션에 대해 섬세하게 지정할 수 있는 메소드를 기준으로 다음과 같이 사용할 수 있다.

 // Swift
UIView.animate(withDuration: /* 총 소요시간 */,
                      delay: /* 애니메이션 시작 전 뜸 들이는 시간 */,
                    options: /* 애니메이션이 실행되는 동안 속도 변화 및 각종 옵션 */,
                 animations: {
                                 /* 애니메이션 끝에 가서 나타날 효과를 기술한다. */
                                 /* 그러면 애니메이션을 통해 서서히 여기서 기술한대로 각종 요소들이 변할 것이다. */
                             },
                 completion: { finished in
                                 /* 애니메이션이 끝난 뒤 수행할 작업을 기술한다. */
                                 /* 레퍼런스에 따르면 이 클로저는 Bool형 변수를 하나 받게 되는데, */
                                 /* 진짜로 애니메이션이 다 끝나서 호출되는 거라면 true가 전달된다. */
                                 /* 그냥 한 번 호출되었다면 false이다. */
                             })

 

 // Objective-C
[UIView animateWithDuration: /* 총 소요시간 */
                      delay: /* 애니메이션 시작 전 뜸 들이는 시간 */
                    options: /* 애니메이션이 실행되는 동안 속도 변화 및 각종 옵션 */
                 animations:^{
                                /* 애니메이션 끝에 가서 나타날 효과를 기술한다. */
                                /* 그러면 애니메이션을 통해 서서히 여기서 기술한대로 각종 요소들이 변할 것이다. */
                            }
                 completion:^(bool finished) {
                                /* 애니메이션이 끝난 뒤 수행할 작업을 기술한다. */
                                /* 레퍼런스에 따르면 이 클로저는 Bool형 변수를 하나 받게 되는데, */
                                /* 진짜로 애니메이션이 다 끝나서 호출되는 거라면 true가 전달된다. */
                                /* 그냥 한 번 호출되었다면 false이다. */
                            }];

 

UIViewPropertyAnimator를 사용하여 애니메이션을 지정하기

UIViewPropertyAnimator는 iOS 10 이후부터 도입된 클래스이다.

var animator: UIViewPropertyAnimator!
// animator 인스턴스를 초기화할 수 있는 메소드 안에서 다음과 같은 내용을 작성한다.
// 생성자 한 줄에서 많은 요소들을 한 번에 초기화 할 수도 있지만, 
// 몇 줄에 걸쳐서 하나씩 지정해 보기 위해 가장 간단한 생성자를 사용한다.
// duration은 생성자에서만 지정 가능하며 이후 프로퍼티에서는 읽기 전용이므로 변경 불가하다.
// curve도 생성자에서만 지정 가능하다.
// animations는 나중에 추가할 수 있으므로 생성자에서는 nil을 지정한다.
animator = UIViewPropertyAnimator(duration: /* 총 소요시간 */,
                                     curve: /* 애니메이션이 실행되는 동안 속도 변화 및 각종 옵션 */,
                                animations:nil)
animator.startAnimation(afterDelay: /* 애니메이션 시작 전 뜸 들이는 시간 */)
animator.addAnimations({
    /* 애니메이션 끝에 가서 나타날 효과를 기술한다. */
    /* 그러면 애니메이션을 통해 서서히 여기서 기술한대로 각종 요소들이 변할 것이다. */
})
animator.addCompletion({ position in
    /* 애니메이션이 끝난 뒤 수행할 작업을 기술한다. */
    /* 레퍼런스에 따르면 이 클로저는 UIViewAnimatingPosition형 변수를 하나 받게 되는데, */
    /* 이름 그대로 애니메이션의 주기에 따라 .start, .current, .end 중 하나이다. */
})

 

카테고리 “Language/Objective-C & Swift”
more...
Swift와 Kotlin, 각 언어에서 비동기 실행하기(2)
본 게시물은 Ivan Fytsyk님의 게시글 Asynchronous execution in Kotlin and Swift: Concurrency problems and how to solve them을 바탕으로 작성되었습니다. Swift와 Kotlin, 각 언어에서 비동기 실행하기 본 게시글에서는 모바일 앱 개발의 양대 언어인 Swift와 Kotlin에서 비동기 실행을 하는 방법에 대해 정리합니다. 이전 글에서 우리는 비동기 실행의 기본적인 예를 살펴보았습니다. 여러분은 아마도 변경 가능한 공유 상태(shared mutable state)와 교착상태(dead-lock)라는 두 개의 일반적인 문제에 대해 이미 알고 있을 것입니다. 이러한 문제는 양대 언어에서도 공통된 문제이기에 서로 비슷한 해결책을 가지고..
Language/Objective-C & Swift
2021. 9. 11. 22:09

Swift와 Kotlin, 각 언어에서 비동기 실행하기(2)

Language/Objective-C & Swift
2021. 9. 11. 22:09

본 게시물은 Ivan Fytsyk님의 게시글 Asynchronous execution in Kotlin and Swift: Concurrency problems and how to solve them을 바탕으로 작성되었습니다.

 

Swift와 Kotlin, 각 언어에서 비동기 실행하기

본 게시글에서는 모바일 앱 개발의 양대 언어인 Swift와 Kotlin에서 비동기 실행을 하는 방법에 대해 정리합니다.

 

이전 글에서 우리는 비동기 실행의 기본적인 예를 살펴보았습니다. 여러분은 아마도 변경 가능한 공유 상태(shared mutable state)와 교착상태(dead-lock)라는 두 개의 일반적인 문제에 대해 이미 알고 있을 것입니다. 이러한 문제는 양대 언어에서도 공통된 문제이기에 서로 비슷한 해결책을 가지고 있습니다. 지금부터 살펴보겠습니다.

 

Shared Mutable State

Shared mutable state부터 시작해 보겠습니다. 아래 예에서 우리는 어떤 정수 값을 비동기식으로 1,000회 증가시켜보겠습니다.

import kotlinx.coroutines.*
import org.junit.Test

class ConcurrencyProblemTest {
    private var someValue = 0
    
    fun runCodeWithConcurrencyProblem() = runBlocking {
        for (i in 0 .. 999) {
            GlobalScope.launch() {
                someValue = someValue + 1
            }
        }
        delay(2000)
        print(someValue)
    }
}

 

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

var someValue = 0

for _ in 0 ... 999 {
    DispatchQueue.global().async {
        someValue = someValue + 1
    }
}

sleep(2)
print(someValue)

 

예상했다시피 이 값은 1,000이 되지 못하고 때때로 달라집니다. 이러한 Shared Mutable State의 해결책은 간단합니다. 변경 가능한 변수의 읽기와 쓰기를 동기화시키면 됩니다. 즉 읽기와 쓰기 작업은 주로 실행되는 쓰레드에서 직렬로(serially) 실행되어야 합니다.

import kotlinx.coroutines.*
import org.junit.Test

class ReadingWritingSerialTest {
    private var someValue = 0
    val singleThreadContext = newSingleThreadContext("mySerialContext")
    
    private fun incrementValue() {
        GlobalScope.launch(singleThreadContext) {
            someValue = someValue + 1
        }
    }
    
    fun runCodeWithConcurrencyProblem() = runBlocking {
        for (i in 0 .. 999) {
            GlobalScope.launch() {
                incrementValue()
            }
        }
        delay(2000)
        print(someValue) // result is always 1,000
    }
}

 

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

var someValue = 0
let serialQueue = DispatchQueue(label: "mySerialQueue")

func incrementValue() {
    serialQueue.async {
        someValue = someValue + 1
    }
}

for _ in 0 ... 999 {
    DispatchQueue.global.sync {
        incrementValue()
    }
}

sleep(2)
print(someValue) // always prints 1,000

 

여기서 여러분은 우리가 작성한 비동기 코드를 동기 코드로 변환하였고, 이로 인해 멀티쓰레드 실행의 이점을 얻을 수 없게 됨을 알았을 것입니다. 맞습니다. 왜냐하면 mutable 변수의 값을 변경하는 것 외에 아무것도 하지 않았기 때문입니다. 하지만 실전 코드에서는, 백그라운드에서 실행될 필요가 있는 연산 및 그 결과는 동기 방식으로 보관하는 연산으로 이루어진 부분들이 많을 수 있습니다.

 

Dead-lock

두 번째 문제는 교착 상태(dead-lock)입니다. 일반적으로 어떤 코드 블록이 내부의 코드 블록에 기술된 내용이 종료될 때까지 기다리나, 내부의 코드 블록 또한 상위의 코드가 종료될 때까지 기다리고 있을 때 쿄착 상태는 발생합니다.

import kotlinx.coroutines.*
import org.junit.Test

class DeadLockTest {
    val singleThreadContext = newSingleThreadContext("mySerialContext")
    
    @Test
    fun runCodeWithConcurrencyProblem() = runBlocking {
        runDeadLock()
        delay(2000)
    }
    
    fun runDeadLock() {
        GlobalScope.launch(singleThreadContext) {
            println("Blocking coroutine start")
            runBlocking(singleThreadContext) {
                // 바깥 블록은 안쪽 블록이 완료될 때까지 기다리겠으나,
                // 안쪽 블록도 마찬가지로 바깥 블록이 완료될 때까지 기다립니다.
                println("Unreachable statement")
            }
            println("Unreachable statement")
        }
    }
}

 

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let serialQueue = DispatchQueue(label: "mySerialQueue")

serialQueue.sync {
    print("Blocking block starts")
    serialQueue.sync {
        print("Unreavhable statement")
    }
}

 

이들 예제에서는 하나의 싱글 쓰레드 큐와 두 블록의 코드가 사용되었습니다. 첫 번째 블록에서 우리는 동기식으로 두 번째 블록을 시작하였고 새로운 블록의 실행이 끝날 때까지 첫 번째 블록은 일시중지됩니다. 하지만 두 번째 블록도 실행되지 못할 것입니다. 왜냐하면 두 번째 블록은 작업 큐의 맨 마지막에 추가되었기 때문에 큐에서 앞쪽 순서에 놓인 블록이 종료될 때까지는 두 번째 블록도 큐에서 순서가 옮겨지지 못하기 때문입니다. 그러한 종류의 교착 상태를 피하기 위한 필자의 조언은 다음과 같습니다.

중첩된 비동기 코드 블록을 실행할 때 동일한 큐를 사용하지 마세요.

import kotlinx.coroutines.*
import org.junit.Test

class DeadLockResolutionTest {
    val singleThreadContext1 = newSingleThreadContext("mySerialContext1")
    val singleThreadContext2 = newSingleThreadContext("mySerialContext2")
    
    @Test
    fun runCodeWithConcurrencyProblem() = runBlocking {
        runDeadlock()
        delay(2000)
    }
    
    fun runDeadLock() {
        GlobalScope.launch(singleThreadContext1) {
            println("Blocking coroutine start")
            runBlocking(singleThreadContext2) {
                // 바깥 블록은 별도의 쓰레드에서 실행되고 있기에,
                // 다음 문장은 접근이 가능합니다.
                println("Unreachable statement")
            }
            println("Unreachable statement")
        }
    }
}

 

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let serialQueue1 = DispatchQueue(label: "mySerialQueue1")
let serialQueue2 = DispatchQueue(label: "mySerialQueue2")

serialQueue1.sync {
    print("Blocking block starts")
    serialQueue2.sync {
        print("Unreachable statement") // 이미 접근 가능하게 되었습니다.
    }
    print("Unreachable statement")
}

 

여기까지가 필자가 접해보았던 일반적으로 흔한 동시성 문제였습니다. 여러분도 보시다시피 동시성 문제와 그에 대한 해법은 양대 언어에서 매우 비슷하게 보입니다. 읽어주셔서 감사합니다.

카테고리 “Language/Objective-C & Swift”
more...
Swift와 Kotlin, 각 언어에서 비동기 실행하기(1)
본 게시물은 Ivan Fytsyk님의 게시글 Asynchronous execution in Kotlin and Swift을 바탕으로 작성되었습니다. Swift와 Kotlin, 각 언어에서 비동기 실행하기 본 게시글에서는 모바일 앱 개발의 양대 언어인 Swift와 Kotlin에서 비동기 실행을 하는 방법에 대해 정리합니다. 이번 시간에 우리는 Kotlin과 Swift에서 동시성 모델(concurrency model)을 비교하며 설명해 볼 것입니다. 이 지식은 Android 및 iOS 프레임워크를 모두 배워보고자 원하는 분들에게도 유익할 것이고, 이미 양대 플랫폼에서 개발을 해 본 분들에게도 유용할 것입니다. 더 많은 정보는 필자의 저서를 통해 확인할 수 있습니다. 기본 용어들 우리가 다루어보고자 하는 기본..
Language/Objective-C & Swift
2021. 9. 11. 21:11

Swift와 Kotlin, 각 언어에서 비동기 실행하기(1)

Language/Objective-C & Swift
2021. 9. 11. 21:11

본 게시물은 Ivan Fytsyk님의 게시글 Asynchronous execution in Kotlin and Swift을 바탕으로 작성되었습니다.

 

Swift와 Kotlin, 각 언어에서 비동기 실행하기

본 게시글에서는 모바일 앱 개발의 양대 언어인 Swift와 Kotlin에서 비동기 실행을 하는 방법에 대해 정리합니다.

 

이번 시간에 우리는 Kotlin과 Swift에서 동시성 모델(concurrency model)을 비교하며 설명해 볼 것입니다. 이 지식은 Android 및 iOS 프레임워크를 모두 배워보고자 원하는 분들에게도 유익할 것이고, 이미 양대 플랫폼에서 개발을 해 본 분들에게도 유용할 것입니다. 더 많은 정보는 필자의 저서를 통해 확인할 수 있습니다.

 

기본 용어들

우리가 다루어보고자 하는 기본 용어들에는 쓰레드(Thread), 오퍼레이션(Operation) 및 큐(Queue)가 있습니다.

 

쓰레드(Thread)

쓰레드(Thread)는 동시에 어떤 연산을 실행할 목적으로 멀티코어 CPU를 사용하는 저수준의 추상화(abstraction)입니다. 싱글코어 CPU에서는 각 연산들 사이를 빠르게 전환함으로써 병렬 실행을 에뮬레이트(emulate)할 수 있습니다. 오늘날 Kotlin이나 Swift에서는 쓰레드가 직접적으로 사용되는 경우가 흔치 않기 때문에 이 글에서 우리는 쓰레드에 대해서는 다루지 않겠습니다. 새로운 쓰레드를 생성하고 이들을 관리하는 것은 많은 시스템 자원을 요구하기 때문에, 현대적인 개발 환경에서 우리는 일반적으로 미리 정의된 몇몇의 쓰레드 풀(Thread Pool)만을 사용하고 (이를 위해) 오퍼레이션(Operation)과 큐(Queue)를 가지고 작업하게 될 것입니다.

 

오퍼레이션(Operation)

오퍼레이션(Operation)은 단순히 한 블록의 코드입니다. 일반적으로 클로저(closure)라든가 다른 객체지향(OOP)적 추상화로써 표현됩니다(예를 들어 Swift에서는 DispatchItem으로 불리고 Kotlin에서는 Task로 불립니다.

 

큐(Queue)

큐(Queue)는 오퍼레이션(Operation)들의 선입선출형 콜렉션(collection)입니다. (이 콜렉션에 담긴) 각각의 오퍼레이션들은 순서대로 선택되어 나온 뒤 각기 다른 쓰레드에서 실행될 수 있습니다.

 

큐에 새로운 오퍼레이션을 추가하는 것은 크게 두 가지 방법으로써 가능합니다. 동기식(sync)과 비동기식(async)이 그것입니다. 동기식은 새로 시작된 오퍼레이션이 끝날 때까지 현재의 오퍼레이션(큐에 오퍼레이션을 넣고자 하는 지금의 작업 흐름)이 일시중지될 것임을 의미합니다. 비동기식은 새로운 오퍼레이션이 시작되더라도 현재의 오퍼레이션은 일시중지되지 않고 나머지 작업을 계속 실행될 것임을 의미합니다.

 

예제 1

설명은 여기까지만 하고, 지금부터는 코드들을 많게 보면서 Kotlin과 Swift에서 위의 개념들이 어떻게 구현되어 있는지를 확인해 보겠습니다. 예제 코드를 실행해보기 위해 Android Studio Kotlin REPL과 Xcode Playground를 사용할 것입니다.

먼저 Kotlin의 코드입니다.

import kotlinx.coroutines.experimental.*

runBlocking {
    GlobalScope.async { // Kotlin sample
        delay(1000L) // 1 second suspending
        println("async call")
    }
    delay(2000L)
}

 

다음은 Swift의 코드입니다.

DispatchQueue.global().async { // Swift sample
    sleep(1) // 1 second suspending
    print("async call")
}

 

여기서 어떤 일들이 벌어지는지 설명해보겠습니다. 먼저 Kotlin 측 코드를 보면 runBlocking { ... }이라는 부분이 보입니다. 이것은 블로킹 함수(blocking function)으로서 REPL의 메인 쓰레드(main threa)가 종료되기 전에 비동기 호출의 결과를 우리가 볼 수 있도록 해 줍니다. 이것은 단지 우리가 코드의 실행 결과를 보는 데 도움을 주기 위해 도입된 구조일 뿐입니다. Xcode Playground는 이와 유사한 구조가 없어도 작동이 됩니다.

 

예제 2

그 다음 우리가 관심을 두어야 할 것은 GlobalScopeDispatchQueue.global()입니다. 이들은 "미리 정의되어 있고 현재 작동 중인 쓰레드" 및 필자가 앞서 언급했던 오퍼레이션 큐(operation queue)를 캡슐화하고 있습니다. 대부분의 경우 이것으로 우리가 소스 코드들을 비동기식으로 실행하는 데 충분합니다. 보다 특수한 목적을 위해 우리는 직접 사용자 정의 큐를 만들 수도 있습니다.

백그라운드에서 실행이 오래 걸리는 작업들이 완료되고 난 뒤, 보통 우리는 UI 쓰레드에게 이 실행 결과를 전달하기도 합니다. Kotlin에서는 이것이 어떻게 이루어지는지 보겠습니다.

import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.android.Main

val job = GlobalScope.async {
    delay(1000L) // 1 second suspending
    println("async call")
    GlobalScope.async(context = Dispatchers.Unconfined) { 
        // 실제 Android 어플리케이션에서는 Dispatchers.Main으로 대체합니다.
        println("called in UI thread")
    }
}

runBlocking {
    job.join()
}

 

Swift에서는 백그라운드 작업 실행 완료를 UI 쓰레드에게 어떻게 통지하는지 보겠습니다.

DispatchQueue.global().async {
    sleep(1) // 1 second suspending
    print("async call")
    DispatchQueue.main.async {
        print("call in UI thread")
    }
}

 

여기서 우리는 비동기 블록 실행이 완료되는 것을 기다리기 위하여 job.join()을 사용하여 UI 쓰레드를 대기시켰습니다. 앞서 적었던 예제에서 delay(2000L)을 호출했던 것을 대체한 것에 지나지 않습니다.

비동기 코드 내에서도 다시 일부 코드 블록을 UI 쓰레드에서 실행하기 위하여 우리는,
Kotlin에서는 GlobalScope.async(context = Dispatchers.Unconfined) { ... }이라는 미리 정의된 메인 큐(main queue)가 동반된 생성자를 사용하였고,
Swift에서는 DispatchQueue.main.async { ... }이라는 호출을 사용하였습니다.

여기까지가 모바일 개발에서 사용되는 동시성 구현의 가장 쉬운 예였습니다. 이런 구조라면 백그라운드에서는 무거운 연산이나 데이터 처리를 수행하고 그 결과만 UI 쓰레드에 전달하면 되겠지요. 다음 장에서 우리는 오퍼레이션과 큐를 가지고 할 수 있는 작업들을 해 보고, 우리가 직접 큐를 만들어 보기도 할 것이며 교착상태(deadlock)나 경쟁(race)과 같은 동시성 문제를 어떻게 해결할 것인지에 대해 살펴보겠습니다.

카테고리 “Language/Objective-C & Swift”
more...
@property의 특성: nonatomic, retain, strong, weak 등
본 게시물은 Saurav Satpathy님의 게시글 Attributes of @property : nonatomic, retain, strong, weak etc..을 바탕으로 작성되었습니다. @property의 특성: nonatomic, retain, strong, weak 등 본 게시글은 프로퍼티 특성(property attribute)에 대한 참고 자료로서 작성되었습니다. 기본적으로 @property는 다음과 같이 생겼습니다. @property (atomic, readonly, strong) NSString * name; 또는 다음과 같이 생겼습니다. @property (atomic, readwrite, assign) NSInteger age; 1. 접근 특성(access attributes) (1..
Language/Objective-C & Swift
2021. 8. 20. 11:35

@property의 특성: nonatomic, retain, strong, weak 등

Language/Objective-C & Swift
2021. 8. 20. 11:35

본 게시물은 Saurav Satpathy님의 게시글 Attributes of @property : nonatomic, retain, strong, weak etc..을 바탕으로 작성되었습니다.

 

@property의 특성: nonatomic, retain, strong, weak 등

본 게시글은 프로퍼티 특성(property attribute)에 대한 참고 자료로서 작성되었습니다.

 

기본적으로 @property는 다음과 같이 생겼습니다.

@property (atomic, readonly, strong) NSString * name;

또는 다음과 같이 생겼습니다.

@property (atomic, readwrite, assign) NSInteger age;

 

1. 접근 특성(access attributes)

(1-1) readonly

name의 선언에서 보이는 바와 같이 그 프로퍼티로 어떤 값이 대입(assign)될 수 없습니다. readonly 특성이 적용된 프로퍼티 내부에는 세터(setter) 메소드가 생략되기 때문입니다.

 

(1-2) readwrite

그 프로퍼티가 갖는 값을 다른 곳으로 가져갈 수 있을 뿐만 아니라, 프로퍼티가 갖는 값이 다른 값으로 대체될 수도 있습니다.

 

2. 쓰레드 특성(threading attributes)

(2-1) atomic

어떤 프로퍼티를 atomic으로 선언하는 것은, 컴파일러로 하여금 그 객체에 대해 여러 쓰레드(thread)에서 동시 접근하는 것을 방지하는 부가적은 코드를 생성하도록 지시합니다.
이 부가적인 코드에서는 세마포어(semaphore)를 잠그고(lock), 그 프로퍼티에 대해 값을 가져오거나 설정한 다음, 다시 세마포어의 잠금을 해제(unlock)합니다. 세마포어를 잠그거나 잠금 해제하는 것은 비용이 들지만(물론 보통은 이 정도의 비용은 경미한 편이지만), 그 프로퍼티의 값을 가져오거나 설정하는 과정이 온전하게 수행됨을 보장합니다.

 

(2-2) nonatomic

프로퍼티 내부의 접근 메소드가 그 값을 단순히 반환하거나 수정하도록 지시합니다. 서로 다른 쓰레드로부터 동시에 이 값에 접근해 올 경우 어떤 일이 벌어질 지에 대해서는 보장이 되지 않습니다. 이런 이유로 인해 atomic으로 선언된 프로퍼티보다 nonatomic으로 선언되는 프로퍼티가 (안전성은 낮지만) 접근 속도가 빠릅니다.

 

3. 메모리 관리 특성(memory management attributes) 및 수명 한정자(lifetime qualifiers)

많은 언어들은 가비지 콜렉터(garbage collector)를 통해 메모리 관리 기능을 지원하지만, Objective-C에서는 객체 소유(object ownership)레퍼런스 카운트(reference counting)이라고 불리는 보다 효율적인 대체 수단을 사용합니다.

 

(3-1) strong과 retain

어떤 프로퍼티를 strong으로 선언한다는 것은 그 프로퍼티가 참조하고자 하는 객체를 “소유(own)”한다는 뜻입니다. 이렇게 선언된 프로퍼티에 여러분이 어떤 데이터를 대입시키면, 그 프로퍼티가 strong 참조로써 다른 데이터를 참조하게 되지 않는 한, 그 데이터는 파괴되지 않을 것입니다.

Objective-C에서 한 객체는 다른 객체들로부터 적어도 한 번은 참조되고 있다면 그 객체는 계속하여 유효한 상태로 남아 있습니다.

non-ARC 코드에서 strong은 단지 retain과 같은 말일 뿐입니다.

어떤 변수가 있을 때 그 변수가 스코프(scope)에서 유효할 동안 또는 그 변수가 다른 객체나 nil을 참조할 때까지 그 변수는 가리키고 있는 객체와 strong 참조를 유지하고 있습니다.

// Example
@property (nonatomic, strong) NSArray * listOfStudents;

 

(3-2) weak

weak은 “누군가가 그 객체를 strong 참조를 하는 한 그 객체를 메모리에 남아있게 하라”는 의미를 갖고 있으며 여러분이 그 객체의 수명에 관여하지 않을 때 지정합니다. 여러분이 weak 참조로 어떤 객체를 참조하면, 그 객체는 다른 객체에 의해 strong 참조되고 있는 한 weak 참조하고 있는 이 쪽에서도 계속 유효하지만, 어떤 객체도 참조하지 않게 될 때 weak 참조로 참조하던 객체는 파괴(destroy)됩니다.

델리게이트 형식의 프로퍼티(delegate property)는 순환적으로 retain되는 것을 방지하기 위해 weak 참조가 됩니다.

IBOutlet이나 서브 뷰(Subview)는 슈퍼 뷰(Superview)에 의해 strong 참조되고 있기 때문에 다른 코드에서는 weak 참조가 됩니다.

// Example
@property (nonatomic, weak) id<SampleDelegate> sDelegate;
@property (nonatomic, weak) IBOutlet UILabel * titleLabel;

 

(3-3) assign

새 값으로서 프로퍼티에 대입됩니다. 이 특성은 대개 int, float, NSInteger 등의 프리미티브 타입(primitive type)의 프로퍼티에 적용됩니다.

 

(3-4) copy

객체가 수정 가능(mutable)할 때 이 특성이 적용됩니다. 프로퍼티에 참조될 객체가 지금 이 순간의 상태로 참조되길 원하며 이후에 다른 요소에 의해 변경되는 것을 여기에서 반영하고 싶지 않을 때 이 특성을 사용하면 됩니다. 다만 이 프로퍼티에 참조되는 객체는 원본에 대한 복사본으로서 현재의 프로퍼티에 의해 retain된 상태이기 때문에, 사용이 끝났다면 여러분이 직접 해제(release)해야 합니다.

이 특성이 적용된 프로퍼티로 객체가 참조될 때 원본과는 다른 메모리 위치에 복사가 이뤄지며 그 복사본의 참조 횟수(retain count)가 증가된 후 해당 프로퍼티에 참조됩니다. 이 참조 횟수는 여러분이 그 객체를 더 이상 소유하고 있지 않을 때 자동으로 감소되겠으나, non-ARC 환경이라면 여러분이 직접 명시적으로 해제해 주어야 합니다.

 

참고 여러분이 mutable 프로퍼티에 대해 copy 특성을 사용하여 선언하였다면, 여러분이 이를 mutable이라 선언했어도 여전히 immutable입니다. 여러분이 여기에 어떤 값이나 객체를 가감하려 한다면 예외가 throw될 것입니다.

 

(3-5) unsafe_unretained

이 특성은 Cocoa 및 Cocoa Touch의 클래스 중 weak 참조를 지원하지 않는 극히 일부 클래스에 대해 사용됩니다. 여러분이 그러한 클래스 형식의 객체에 대해 정보를 얻고자 할 때 weak 참조의 로컬 변수나 weak 참조의 프로퍼티를 사용할 수 없음을 의미합니다. 이 특성은 참조된 객체를 계속해서 유효한 상태로 유지하는 능력이 없으면서 그 객체에 대한 strong 참조가 없어져도 이 특성을 갖는 프로퍼티는 nil 참조로 바뀌지 않는다는 뜻입니다. 때문에 이 특성을 가진 프로퍼티가 가리키는 객체가 할당 해제(deallocated)되었다면, 이 프로퍼티가 갖는 포인터는 오류입니다.

 

4. getter=

직접 작성한 메소드를 프로퍼티에 대한 getter로서 사용할 때 이 특성을 적용합니다.

 

5. setter=

직접 작성한 메소드를 프로퍼티에 대한 setter로서 사용할 때 이 특성을 적용합니다.

 

카테고리 “Language/Objective-C & Swift”
more...

“Language/Objective-C & Swift” (21건)