Scalar type은 Objective-C로 전달되면 어떻게 될가?

Scalar type은 Objective-C로 전달되면 어떻게 될가?

Objective-C에서 Collection 객체를 사용할 때 scalar type은 값으로 바로 사용할 수 없습니다. NSValue나 NSNumber를 이용하여 값을 객체로 만들어 저장할 수 있습니다. 그에 반해 Swift에서는 scalar type을 값으로 설정할 수 있습니다. 만약 swift에서 scalar type을 저장하고 있는 collection 객체를 objective-C에 전달하면 어떻게 될가요? 이번 포스트에서는 swift와 Objective-C사이에 scalar type을 전달할 때 어떻게 동작하지는 알아보겠습니다.



먼저, NSNumber와 NSValue에 대해서 알아보겠습니다.

  • NSNumber
    NSNumber는 NSValue의 subclass이며 scalar의 값을 제공합니다. unsigned char, short int, int, long int, long long int, float, double, BOOL을 지원합니다.
    NSNumber는 collection객체에 scalar type을 전달하기 위해서 사용합니다. collection객체는 객체만 저장할 수 있기 때문에 scalar type은 저장할 수 없습니다. 즉, NSNumber는 scalar type을 저장하고 있는 wrapper객체입니다.

  • NSValue
    NSValue는 scalartype을 저장할 수 있는 객체입니다. 포인터 주소, 구조체 등을 저장할 수 있습니다.
    collection 객체나 키-벨류 코딩 등 객체가 사용되어야할 위치에 scalar type을 전달하기 위해서 사용합니다.


지금부터 swift에서 cocoa touch library(Objective-C라이브러리)에 값을 전달할 때 어떻게 동작하는지 확인해보겠습니다.
아래 예제는 CIFilter를 이용하여 이미지의 밝기를 조정하는 코드입니다.

let ciImage = CIImage(image: UIImage(named: "test.png")!)

let filter = CIFilter(name: "CIColorControls")
filter?.setDefaults()
filter?.setValue(ciImage, forKey: "inputImage")
filter?.setValue(0.5, forKey: "inputBrightness")
let outputImage = filter?.value(forKey: "outputImage") as! CIImage
let context = CIContext()
let imageRef = context.createCGImage(outputImage, from: outputImage.extent)!
let resultImage = UIImage(cgImage: imageRef)

필터에 밝기 값을 scalar type으로 0.5를 주었을 때 결과는 아래와 같습니다.
enter image description here
<원본 이미지>

enter image description here
<수정된 이미지>


이번에는 NSNumber를 이용하여 0.5값을 전달하겠습니다.

filter?.setValue(NSNumber(floatLiteral: 0.5), forKey: "inputBrightness")

이 코드를 실행하면 scalar type으로 0.5를 설정했을 때와 결과가 같습니다.
swift에서 Objective-C 객체에 전달할 때 scalar type을 자동으로 변환하여 값을 전달했을 것으로 추정됩니다.


정말 그렇게 되는지 Objective-C 예제 코드를 통해 확인해 보겠습니다.

swift에서 Objective-C코드를 호출했을 때 Objective-C에서 전달한 값을 확인하는 코드를 아래와 같이 작성합니다.

@interface TestClass : NSObject
- (void)setValue:(NSDictionary *)value;
@end

@implementation TestClass

- (void)setValue:(NSDictionary *)value {
    double doubleNumber = [value[@"key1"] doubleValue];
    CGPoint pointValue = [value[@"key2"] CGPointValue];
}
@end

그 다음 swift에서 아래와 같이 인자로 scalar type을 포함하는 dictionary를 Objective-C코드에 전달합니다.

let dic: [String: Any] = ["key1": 2.0, "key2": CGPoint(x: 10, y: 10)]
        
let test = TestClass()
test.setValue(dic)

swift에서 TestClass를 호출하면 key1과 key2는 double(scalar type)과 CGPoint(구조체)를 인자로 전달합니다. TestClass에서는 전달된 값이 모두 NSNumber와 NSValue로 변환되어 dobleNumber와 pointValue에 값이 할당됩니다.


let dic: [String: Any] = ["key1": NSNumber(floatLiteral: 1.0), "key2": NSValue(cgPoint: CGPoint(x: 10, y: 10))]

위 코드 처럼 swift에서 인자로 NSNumber와 NSValue로 전달할 때도 TestClass에는 동일하게 NSNumber와 NSValue로 전달됩니다.


이번에는 반대로 Objective-C에서 swift로 전달해보겠습니다.

swift코드를 아래와 같이 작성합니다.

@objc (SwiftClass)
class SwiftClass: NSObject {
    @objc (setTest:)
    func setTest(_ value: [String: Any]) {
        print("Double:", value["key1"] as! Double)
        print("NSNumber:", value["key1"] as! NSNumber)
        
        print("CGPoint:", value["key2"] as! CGPoint)
        print("NSValue:", value["key2"] as! NSValue)
    }    
}

Objective-C에서 아래와 같이 swift에 NSNumber와 NSValue를 전달합니다.

SwiftClass *test = [[SwiftClass alloc] init];
[test setTest:@{@"key1" : [NSNumber numberWithDouble:2.0],
                @"key2" : [NSValue valueWithCGPoint:CGPointMake(10, 10)]
    }];

// 실행결과
// Double: 2.0
// NSNumber: 2
// CGPoint: (10.0, 10.0)
// NSValue: NSPoint: {10, 10}

Objective-C에서 전달한 NSNumber는 swift에서 전달받을 때 Double과 NSNumber 두가지로 모두 받을 수 있습니다. NSValue는 CGPoint와 NSValue 두가지로 받을 수 있습니다. swift에서 NSValue나 NSNumber를 바로 scalar type으로 변환해서 사용할 수 있다는 것을 알 수 있습니다.



지금까지의 내용을 다시한번 정리하면,

swift에서 scalar type으로 전달한 값은 Objective-C에서 NSNumber나 NSValue로 자동 변환되어 전달됩니다. 반대로 Objective-C에서 전달한 NSValue나 NSNumber는 swift에서는 NSValue, NSNumber로 전달받거나 또는 scalar type으로 바로 변환되어 사용할 수 있습니다.



참고
https://developer.apple.com/documentation/foundation/nsnumber
https://developer.apple.com/documentation/foundation/nsvalue

댓글

이 블로그의 인기 게시물

dismiss에 대해서 알아봅시다

1. Framework: framework란?

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