2013-05-15 2 views
0

배경 :iOS 하위 호환성 - 존재하지 않는 클래스의 alloc/init

iOS 6 SDK를 사용하여 앱을 개발하고 있다고 가정 해 보겠습니다.

// method only available from 6, class of someObj existed in 5 
if (someObj respondToSelector:@selector(aMethod)) { 
    [someObj aMethod]; 
} 

또는

// entire class only available from 6 
if (NSStringFromClass([SKStoreProductViewController class]) != nil) { 
    SKStoreProductViewController *store = [[SKStoreProductViewController alloc] init]; 
} 
: 나는 내 배포 대상은 그때 6에서 기능을 사용하지만 5와의 호환성을 유지하기 위해 할 수 아이폰 OS 5 에 설정 한, 당신은 당신의 코드에서 어떤 검사를해야

지금까지 그렇게 좋았습니다. 내가 아는 한 이것들은 일을하는 표준 방법입니다.

그러나 새로운 클래스 예제에서, 내가 체크하고 그 클래스에서 객체를 할당/초기화하려고한다면, 예상했던 것보다는 오히려 반환할만한 iOS 5에서는 충돌이 일어나지 않는다는 것을 알았다. 없는.

// run this on an iOS 5 device 
NSLog(@"%@", [UICollectionView alloc] init]); 

iOS 5에서 충돌이 발생하지 않는 이유는 무엇입니까? 이것은 링커가 작동하는 방식과 관련이 있다고 생각하지만 심볼이 해당 버전에 존재하지 않기 때문에 충돌이 발생할 것으로 예상됩니다.

두 번째 질문은 정상적인 테스트가 NSStringFromClass 메서드를 사용하는 경우 존재하지 않는 클래스에 + 클래스 메서드를 보낼 수 있고 null을 반환한다는 것을 의미합니다. 왜 그런 식으로 작동합니까?

마지막으로, iOS 6에서만 정의 된 프로토콜을 채택하는 ViewController를 만들 수 있고 다시 5에서 문제가 발생하지 않는다는 것을 알았습니까?

답변

2

이 동작은 NS_CLASS_AVAILABLE 매크로 때문입니다. 이 매크로는 대부분의 (모든?) UIKit 클래스에서 구현되었으며 사용할 수없는 클래스에 대해 nil을 반환합니다. 이것은 당신이 코드를 특정 iOS 버전에 클래스의 존재를 확인 할 수 있습니다 :

if ([UICollectionView class]) { 
    // class exists, must be iOS6+ 
} 

지금, [[UICollectionView alloc] init]에 전화는 항상 전무를 반환하는 무기 호 클래스의 클래스 메소드 호출이다.

2 차 질문에 대답하려면 클래스 존재 여부를 확인하는 올바른 방법은 클래스가 위와 같이 nil인지 아닌지 확인하는 것입니다. NSStringFromClass는 더 이상 필요하지 않습니다.

그래서 질문 3에 대해서도 놀랐습니다. 그러나 프로토콜 객체가 이진 파일로 직접 컴파일 된 것처럼 보입니다. 최신 SDK를 사용하여 컴파일 할 때 코드가 잘 컴파일되고 누락 된 클래스에 대한 링크가 필요하지 않으므로 프로토콜을 아직 도입하지 않은 SDK 버전에서 실행할 때 프로토콜을 사용할 수 있습니다. 즉, Protocol 개체가 유효하며 실행중인 iOS 버전에 관계없이 클래스가 아무런 문제없이 올바르게 conformsToProtocol:에 응답합니다. 이것은 컴파일 된 바이너리에서 otool -l을 사용하면 쉽게 볼 수 있습니다. 컴파일 된 바이너리는 클래스 및 메소드에 의해 준수되는 프로토콜을 보여줍니다. 프로토콜 자체는 __objc_protolist이라는 섹션에있는 것처럼 보입니다. UICollectionViewDelegate 및 데이터 소스에 부합하는 클래스의 출력은 다음과 같습니다 :

000050a4 0x5cf4 
      isa 0x5d08 
    superclass 0x0 
     cache 0x0 
     vtable 0x0 
      data 0x5b30 (struct class_ro_t *) 
        flags 0x80 
      instanceStart 156 
      instanceSize 156 
       ivarLayout 0x0 
        name 0x4a0f TTViewController 
       baseMethods 0x5b10 (struct method_list_t *) 
      entsize 12 
      count 2 
       name 0x3eb8 viewDidLoad 
      types 0x4955 [email protected]:4 
       imp 0x2620 
       name 0x3ec4 didReceiveMemoryWarning 
      types 0x4955 [email protected]:4 
       imp 0x2670 
      baseProtocols 0x5ad8 
         count 2 
       list[0] 0x5da0 (struct protocol_t *) 
        isa 0x0 
       name 0x4997 UICollectionViewDelegate 
      protocols 0x5304 
      instanceMethods 0x0 (struct method_list_t *) 
      classMethods 0x0 (struct method_list_t *) 
     optionalInstanceMethods 0x5310 
     optionalClassMethods 0x0 
      instanceProperties 0x0 
       list[1] 0x5dcc (struct protocol_t *) 
        isa 0x0 
       name 0x49ce UICollectionViewDataSource 
      protocols 0x53d8 
      instanceMethods 0x53e4 (struct method_list_t *) 
       entsize 12 
       count 2 
        name 0x394e collectionView:numberOfItemsInSection: 
       types 0x455d [email protected]:[email protected] 
        imp 0x0 
        name 0x3975 collectionView:cellForItemAtIndexPath: 
       types 0x4589 @[email protected]:[email protected]@12 
        imp 0x0 
      classMethods 0x0 (struct method_list_t *) 
     optionalInstanceMethods 0x5404 
     optionalClassMethods 0x0 
      instanceProperties 0x0 
        ivars 0x0 
      weakIvarLayout 0x0 
      baseProperties 0x0 
+0

그래도 난 여전히 프로토콜 상황에 관해서는 관심 - NS_CLASS_AVAILABLE이 적용하지 않는 것 같습니다. – Brynjar

관련 문제