Tuesday, December 20, 2016

[번역][Swift 공식 블로그] Objective-C [id] 를 Swift [Any] 로 사용하기. 1부

Objective-C [id] 를 Swift [Any] 로 사용하기 1부

원문 : Objective-C id as Swift Any 

Swift3를 이전버전에 비해 좀더 강력하세 Objective-C API를 사용할수 있습니다.
예를들어, Swift2에서는 [id](ObjC)형을 값과,Class 종류만을 가질수 있는 [AnyObject](Swift)형으로 사용할수 있었습니다. Swift2에서는 NSString이나 NSArray 혹은 다른 Foundation 에서 제공하는 형을 기대하는 CoCoa API에서 사용될수 있는 [String][Array][Dictionary][Set] 같은 형으로 묵시적으로 캐스팅 하여 사용할수 있었습니다. 이러한 방식은 일관성이 없고, AnyObject에 실제 어떤 형이 들어 있는지 알수 없어, 버그의 소지가 높았습니다.

Swift3에서는 [id] 타입을 class,enum,struct 혹은 다른 Swift type의 어떠한 타입의 값을 서술할수 있는 [Any] 타입으로 매핑합니다. 이러한 변화는 Swift 에서 사용되는 Objective-C API를 더 유연하게 사용할수 있게 합니다. 왜냐하면  Swift로 정의된 데이터를 Objective-C API에 바로 전달함으로써, Swift type를 분리해서 일일이"box" types로 치환하는 작업을 제거하기 때문입니다.  이러한 특성은 NSArray, NSDictionary, NSSet 과 같은 Collection Class 에도 적용됩니다. 예를들어 hashed container인 [Dictionary] 와 [Set] 같은 경우는 [Hashable] protocol을 구현하는 [AnyHashable] 타입으로 변환되게 됩니다.



ObjCSwift 2Swift 3
[id]AnyObjectAny
[NSArray *][AnyObject][Any]
[NSDictionary *][NSObject:AnyObject][AnyHashable:Any]
[NSSet *]SetSet

대부분의 경우, 이러한 변화에 대응하기위해 코드를 많이 바꿀 필요는 없습니다. 
Swift 2에서 동작했던 [AnyObject]로의 묵시적인 변환을 이용한 코드는 Swift3에서 여전히 동작할것입니다. 그러나 Swift3를 제대로 활용하기 위해, 이러한 변수들의 타입 선언문을 바꾸어야합니다. [AnyObject], [NSString]. [NSArray],[NSDictionary] 같은 걸 묵시적으로 이때까지 사용했다면, 명시적으로 [ as NSString ] 혹은 [ as String ]을 사용해줄 필요가 있습니다. Swift3 에서는 더이상 이런 묵시적인 컨버팅은 허용되지 않습니다. 자동치환 도구는 이런 사소한 변화를 자동으로 해주어, Swift 2 에서 3로 이전할때 코드가 지속적으로 컴파일 될수 있게 도와줍시다. 물론 가장 잘 정리된 형태는 아닐껍니다. 이 글에서 우리는 [id]를 [Any]로 사용하는것을 이용할때, 우리가 직접 수정해야 하는 부분과, 주의해야 하는것들에 대해 살펴 볼 예정입니다. 

Method 오버라이딩과 Protocol 구현

Objective-C Class를 상속받아, Method를 오버라이딩하거나, Objective-C protocol을 구현해야할때, Objective-C 에서 [id] 를 사용한다면 Method Signature를 변경해야 합니다. 
흔한 예로, [NSObject]의 [isEqual:] 이나 [NSCopying] Protocol의 [copyWithZone:] 를 구현하는 경우 입니다. 

// Swift 2
class Foo: NSObject, NSCopying {
 override func isEqual(_ x: AnyObject?) -> Bool { ... }
 func copyWithZone(_ zone: NSZone?) -> AnyObject { ... }
}
Swift3 에서는 추가로 [copyWithZone(_:)]를 [copy(with:)]로 이름을 바꿔야 하며, [AnyObject] 대신에 Any를 사용하도록 Method Signature를 변경해야 합니다.

// Swift 3
class Foo: NSObject, NSCopying {
 override func isEqual(_ x: Any?) -> Bool { ... }
 func copy(with zone: NSZone?) -> Any { ... }
}

Untyped Collections

Property lists, JSON 혹은 User Info Dictionary 들은 Cocoa 에서 일반적이고, 이런것들은 Cocoa Native 에서 untyped collection으로 표기됩니다. Swift2에서는 이 요소들을 사용하기 위해, 묵시적 bridging 변화을 사용하여 [AnyObject] 혹은 [NSObject]로 명시된 [Array] [Dictionary] [Set] 을 만들어야 했습니다. 

// Swift 2
struct State {
 var name: String
 var abbreviation: String
 var population: Int

 var asPropertyList: [NSObject: AnyObject] {
  var result: [NSObject: AnyObject] = [:]
  // Implicit conversions turn String into NSString here…
  result["name"] = self.name
  result["abbreviation"] = self.abbreviation
  // …and Int into NSNumber here.
  result["population"] = self.population
  return result
 }
}

let california = State(name: "California",
        abbreviation: "CA",
        population: 39_000_000)
NSNotification(name: "foo", object: nil,
      userInfo: california.asPropertyList)
혹은 Cocoa container class 인 NSDictionary를 사용할수도 있었습니다.

// Swift 2
struct State {
 var name: String
 var abbreviation: String
 var population: Int

 var asPropertyList: NSDictionary {
  var result = NSMutableDictionary()
  // Implicit conversions turn String into NSString here…
  result["name"] = self.name
  result["abbreviation"] = self.abbreviation
  // …and Int into NSNumber here.
  result["population"] = self.population
  return result.copy()
 }
}
let california = State(name: "California",
        abbreviation: "CA",
        population: 39_000_000)
// NSDictionary then implicitly converts to [NSObject: AnyObject] here.
NSNotification(name: "foo", object: nil,
      userInfo: california.asPropertyList)
Swift 3 에서는 이런 묵시적 전환은 더이상 사용할수 없음으로 위의 예제 코드는 더이상 동작하지 않습니다. 자동 변환 도구는 개별 값들을 [as] 를 통환 형변환을 하도록 하여 코드가 동작하게 할것이지만 좀더 좋은 방법이 있습니다.
Swift는 Cocoa API에서 받을수 있는, [Any] 혹은 [AnyHashable]의 Collection을 사용할수 있음으로 우리는 [NSObject:NSObject] 혹은 [NSDictoinary]를 사용하는 대신 [AnyHashable:Any]를 사용함으로써 다른 코드의 치환 없이 동작하는 코드를 만들수 있습니다. 

// Swift 3
struct State {
 var name: String
 var abbreviation: String
 var population: Int

 // Change the dictionary type to [AnyHashable: Any] here...
 var asPropertyList: [AnyHashable: Any] {
  var result: [AnyHashable: Any] = [:]
  // No implicit conversions necessary, since String and Int are subtypes
  // of Any and AnyHashable
  result["name"] = self.name
  result["abbreviation"] = self.abbreviation
  result["population"] = self.population
  return result
 }
}
let california = State(name: "California",
        abbreviation: "CA",
        population: 39_000_000)
// ...and you can still use it with Cocoa API here
Notification(name: "foo", object: nil,
    userInfo: california.asPropertyList)

[AnyHashable] Type

Swift [Any] Type은 어떠한 형도 담을수 있지만, [Dictionary],[Set]은 [Hashable] 한 Key를 필요로 합니다. 또한 [Any]는 너무 범용이죠. Swift3를 시작으로 Swift Standard Library [AnyHashab]e 타입을 지원합니다. [Any] 와 마찬가지로 이것은 모든 [Hashable] 형의 슈퍼 타입입니다. [String][Int] 혹은 다른 hashable 한 타입들이 들어 있을수 있으며, 묵시적으로 [AnyHashable] 형과 값을 가질수 있습니다. [AnyHashable]에 있는 자료형은 [is],[as!][as?]등을 통해 동적으로 체크할수 있습니다. [AnyHashable]은 type이 명시되지 않은 [NSDioctionary] 혹은 [NSSet] 형을 Objective-C를 통해 가져오는데 사용할수도 있지만, 순수 Swift 에서도 다형성을 가진 sets이나 dictionary를 다루는데 사용할수 있습니다. 


2부에서 계속 

Explicit Conversion for Unbridged Context
[AnyObject] Member Lookup
Swift Value Types in Objective-C
Linux Portability
Learning More



No comments:

Post a Comment