Stay weird. Stay different.

0%

Effective Objective-c 2.0读书笔记(四)

  • 第十三条:用“方法调配”技术调试“黑盒方法”

    类的方法列表会把选择子的名称映射到相关的方法实现上,使得动态消息能够根据此找到对应的方法,这些方法均以函数指针的形式来表示,其原型如下:

    1
    id (*IMP)(id, SEL, ...)

    例如NSString类中的方法lowercaseString, uppercaseString, capitalizedString 等,这些选择子都映射到不同的IMP上面:

    1
    2
    3
    lowercaseString    ---> IMP1	
    uppercaseString ---> IMP2
    capitalizedString ---> IMP3

    objective-c中可以通过修改以上的映射关系实现消息实现的改变,这种方法叫做method swizzing(方法掉配)。

    1
    2
    3
    lowercaseString    ---> IMP2	
    uppercaseString ---> IMP3
    capitalizedString ---> IMP1

    具体的实现方法是调用下列方法

    1
    void method_exchangeImplementations ( Method m1, Method m2 );

    其中m1,m2可通过下列方法获得

    1
    Method class_getInstanceMethod ( Class cls, SEL name );

    具体到上面提到的例子可以这样使用:

    1
    2
    3
    Method lowercaseMethod = class_getInstanceMethod ( [NSString class], @selector(lowercaseString) );
    Method uppercaseMethod = class_getInstanceMethod ( [NSString class], @selector(uppercaseString) );
    void method_exchangeImplementations ( lowercaseMethod, uppercaseMethod );

    这样NSString中的文字大小写转换方法就进行了互换。但这样对我们实际的编程中并不会有任何实际的帮助。
    前一阵看了一篇文章iOS应用架构谈 view层的组织和调用方案,其中提到的是否有必要让业务方统一派生ViewController一节中使用到的就是Method Swizzling技术,具体使用他使用了一个开源库Aspects。具体使用如下

    创建一个父类为NSObject的类NQViewControllerIntercepter,在类的实现中写下如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    + (void)load
    {
    [super load];
    [NQViewControllerIntercepter sharedInstance];
    }

    + (instancetype)sharedInstance
    {
    static dispatch_once_t onceToken;
    static NQViewControllerIntercepter *sharedInstance;
    dispatch_once(&onceToken, ^{
    sharedInstance = [[NQViewControllerIntercepter alloc] init];
    });
    return sharedInstance;
    }

    - (instancetype)init
    {
    self = [super init];
    if (self) {

    [UIViewController aspect_hookSelector:@selector(loadView) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo>aspectInfo){
    [self loadView:[aspectInfo instance]];
    } error:NULL];

    [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated){
    [self viewWillAppear:animated viewController:[aspectInfo instance]];
    } error:NULL];
    }
    return self;
    }

    - (void)loadView:(UIViewController *)viewController
    {
    NSLog(@"[%@ loadView]", [viewController class]);
    }

    - (void)viewWillAppear:(BOOL)animated viewController:(UIViewController *)viewController
    {
    NSLog(@"[%@ viewWillAppear:%@]", [viewController class], animated ? @"YES" : @"NO");
    }

    其中load方法会在运行时提前调用,(load与另一个类方法initialize的区别在于,load是类在被引用的时候调用,而initialize是在类的成员方法调用的时候调用,并且这两个方法只会被调用一次)。

    33行到42行就是我们通过Aspecthook到的方法。这样可以在不使用继承的情况下重写父类的方法。

  • 第十四条:理解类对象的用意

    这条主要围绕这张图进行讲解:
    ios class

    • SuperClass指针确定了继承关系,isa指针描述所属的类。
    • 尽量使用类型信息查询方法来确定对象类型(isKindOfClass,isMemberOfClass),而不要直接比较没对象
  • 第十五条:用前缀避免命名空间冲突

    • 每个类要选用固定的前缀
    • 如果自己开发的程序中用到了第三方库,最好也在前面加上前缀