iOS 6有关Objective-C和Cocoa框架的重要更新

  • 原文URL:Embracing Modern Objective-C

    本文将针对Objective-C和Cocoa框架的几项重要更新,做一个简单的介绍。撰写本文时,这些更新指的是由iOS 6和相应的SDK(Xcode 4.6)引入的那些。重要的更新(个人意见)会写在前面。

     

    创建NSNumber literal、NSArray literal和NSDictionary literal的新语法

    这项改进非常好用,能帮助我们少敲很多代码,且代码也会更干净易读。要创建NSNumber literal、NSArray literal和NSDictionary literal,现在可以使用带@前缀的语法。以下列举若干简单的例子:


    NSNumber *a = @1.0;
    NSNumber *b = @NO;
    int c = 12;
    NSNumber *d = @(c);
    
    NSArray *ary = @[@"hello", @1];
    
    NSDictionary *dict = @{@"key1":@"value1", @"key2":@2};

    此外,也可以用[]来访问collection对象中的对象:


    NSString *str = ary[0];
    
    NSString *value = dict[@"key1"];

     

    在iOS 6中,要获取通讯录中的数据,必须先询问并取得用户的“许可”。否则,相关的应用逻辑会“安静地”失败--应用会得到一个空的“通讯录”,且不会有错误提示。

    此外,如果没有在真实设备上进行仔细的测试,而只是通过虚拟机来完成开发工作,也容易产生问题。这是因为虚拟机不支持相应的“数据隔离”(Data Isolation)功能,在用虚拟机跑iOS 6时,即使应用没有询问并取得“许可”,也能正确地获取通讯录中的数据。但是,一旦在真实设备中运行,潜在的问题就会暴露出来。

    在Apple提供的文档“iOS SDK Release Notes for iOS 6”中,有一节特别提到了这个问题。要妥善处理这处“许可”问题,必须编写相应的代码、处理所有可能发生的错误并兼容之前的iOS 5模式。稍后有机会的话,再分享这部分的代码。

    http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/


     

    viewWillUnload和viewDidUnload不会再被调用

    在iOS 6中,UIViewController之前提供的两个方法viewWillUnload和viewDidUnload已经“过期”(deprecated),不会再被调用。对于之前需要在这两个方法中完成的任务,可以将相关的代码移入didReceiveMemoryWarning方法。对这个问题,仍需要相当的篇幅才能尽述,所以有机会再开新篇讨论。这里先给出一段覆盖didReceiveMemoryWarning方法的示例:

    - (void)didReceiveMemoryWarning {
      [super didReceiveMemoryWarning];
    
      if (![self isViewLoaded]) {
        return;
      }
    
      if (self.view.window) {
        return;
      }
    
      // clean reloadable stuff here
      // ...
    }

     

    新的enum语法

    先看一则旧的enum声明代码:


    enum {
      MyErrorUnknownType = 0,
      MyErrorHTTPType,
      MyErrorTypingType
    };
    typedef NSUInteger MyErrorType;

    如果使用新的语法,则声明MyErrorType的代码如下:


    typedef NS_ENUM(NSUInteger, MyErrorType)  {
      MyErrorUnknownType = 0,
      MyErrorHTTPType,
      MyErrorTypingType
    };

    新的语法能为编译器和Xcode提供更多的信息,从而加强类型检查、自动补全等功能。

     

    用CFBridgingRelease替换__bridge_transfer.

    使用ARC时,对于Core Foundation框架的数据类型,如果要将其所有权(ownership)转移至某个Objective-C对象时(两者都支持toll-free bridging),之前必须使用“修饰符”(modifier)__bridge_transfer。现在则推荐使用宏CFBridgingRelease。这个宏的作用目前看来似乎只是为了能让代码更干净,但也可能是为将来的变化作预留。以下是一则使用CFBridgingRelease的例子:


    NSString* email = CFBridgingRelease(ABMultiValueCopyValueAtIndex(emailProperty, j));
    <br>

    对于只在“当前”文件中使用的私有方法,现在可以不必作特别的声明,编译器不会发出警告。

    这项新的语言特性也是相当好用的。在此之前,声明私有方法需要借助class extension,示例如下:


    // FooClass.m 
    
    @interface FooClass ()
    
    - (void)privateMethod;
    
    @end
    
    @implementation FooClass
    
    - (void)foo {
      [self privateMethod];
    }
    
    - (void)privateMethod {
    
    }
    
    @end

    或者将私有方法的定义写在调用代码之前,以省略相应的声明,示例如下:


    // FooClass.m 
    
    @implementation FooClass ()
    
    - (void)privateMethod {
    
    }
    
    - (void)foo {
      [self privateMethod];
    }
    
    @end

    现在不用再顾及私有方法的声明,可以在当前文件的任意位置定义私有方法。编译器能够自动识别并找出这些方法,也因此不会发出警告:


    // FooClass.m 
    
    @implementation FooClass ()
    
    - (void)foo {
      [self privateMethod];
    }
    
    - (void)privateMethod {
    
    }
    
    @end

     

    可以将实例变量的声明移入.m文件

    这项新的语言特性能帮助我们隐藏Objective-C类的实现细节。在此之前,必须在.h中声明实例变量(ivar):


    // FooClass.h
    
    @interface FooClass : NSObject {
      NSString *_ivar;
    }
    
    @end

    现在则可以在类的实现代码段中声明实例变量,示例如下:


    // FooClass.m
    
    @implemation FooClass {
      NSString *_ivar;
    }
    
    @end

    注意: 对于“私有属性”(private property),则仍必须在class extension中声明,示例如下:


    // FooClass.m
    
    @interface FooClass ()
    
    @property NSNumber *privateValue;
    
    @end
    
    @implemation FooClass {
      NSString *_ivar;
    }
    
    @end
    <br>

    可以省略@synthesize

    现在可以省略@synthesize,编译器会代劳。例如下面这段代码,没有@synthesize也能正常工作(编译器会自动定义一个名为_name的实例变量,并将其和name属性关联):


    // FooClass.h
    
    @interface FooClass : NSObject {
    
    }
    
    @property NSString *name;
    
    @end
    
    
    // FooClass.m
    
    @implementation FooClass
    
    @end

    但是,如果你不打算使用编译器所采用的默认变量名,就要用@synthesize来明确地指定变量名,示例如下:


    // FooClass.h
    
    @interface FooClass : NSObject {
    
    }
    
    @property NSString *name;
    
    @end
    
    
    // FooClass.m
    
    @implementation FooClass {
      NSString *_fullname;
    }
    
    @synthesize name = _fullname;
    
    @end
    iOS 6 SDK的另一项重要改动是针对“自动转屏”(autorotation)的。
  • 在iOS 6中,UIViewController之前提供的shouldAutorotateToInterfaceOrientation:方法已经“过期”(deprecated),不会再被调用。相对应的,需要通过其他途径来告知系统如何处理自动转屏。这些途径可分为两个级别,分别是“应用级别”和“视图控制对象级别”。此外还有一项重要的变动:容器类型的视图控制对象(例如UITabViewController或UINavigationController)不会再向其子视图控制对象询问是否应该自动转屏。
    • 应用级别:可以通过编辑Info.plist,或者覆盖app delegate的application:supportedInterfaceOrientationsForWindow:方法来“设置”。此外,也可以在Xcode中,通过Target的Summary面板,借助“Supported Interface Orientations”界面来设置Info.plist文件中的,和自动转屏有关的参数。
    • 视图控制对象级别:要让某特定类型的视图控制对象支持自动转屏,需要1)覆盖shouldAutorotate方法并返回YES。2)覆盖supportedInterfaceOrientations方法,返回一个特定的掩码,该掩码代表相应的视图控制对象所支持的所有自动转屏方向。
     
    有若干细节点需要额外注意:
    • 系统会向顶层的,全屏的视图控制对象询问其支持的转屏方向,具体的询问时刻为每当:1)设备转动时 或 2)某个视图控制对象以全屏模态方式显示时。
    • 针对某个视图控制对象,系统会结合(intersect)应用级别的自动转屏设置以及该视图控制对象自身所支持的自动转屏方向,来决定是否支持某个特定方向的自动转屏。也就是说,如果应用中的视图控制对象需要支持默认方向以外的自动转屏方向,哪怕只有一个,也需要在应用层面设置相应的自动转屏参数。
    • 可以覆盖UIViewController的preferredInterfaceOrientationForPresentation方法(可选的),告之系统在显示相应的控制对象时,应该使用哪个方向。当然,对应的视图控制对象必须“支持”指定的方向。
    • 在supportedInterfaceOrientations方法中,需要返回的是掩码(NSUInteger)。在 preferredInterfaceOrientationForPresentation方法中,需要返回的是方向类型(UIInterfaceOrientation)。如果在返回相应的数值时不加注意,就很容易产生错误:看上去代码正确,但是自动转屏却无法正确按预期工作。

相关推荐