iOS面试回顾 ---内存篇

最近在面试,脸都被打肿了,下面分享一些脸被打肿的经历。
请听题

@property(copy, nonatomic) NSMutableArray *myFish;
 self.myFish = [NSMutableArray array];

此时 self.myFish 的具体类型?

说来惭愧从来没有用过这个[NSMutableArray array]api,但是根据我的直觉判断它可能返回的是NSMutableArray, 接下来就是赋值语句于是我想着[NSMutableArray copy]可能最终返回NSMutableArray。所以我给出的答案是NSMutableArray。

回答错误

回去老老实实看了下api

@interface NSArray (NSArrayCreation)

+ (instancetype)array;
@end

@interface NSObject 
- (id)copy;
- (id)mutableCopy;
@end


首先, array 函数属于NSArray,那么NSMutableArray 作为NSArray的子类有没有重写这个方法呢?

    NSMutableArray *p =  [NSMutableArray array];
    NSMutableArray *p1 = [p copy];
    NSMutableArray *p2 = [p mutableCopy];

先写一段小代码,看看结果
iOS面试回顾 ---内存篇_第1张图片
屏幕快照 2019-10-25 上午10.57.30.png

所以了

  • [NSMutableArray array] -> NSMutableArray
  • [NSMutableArray copy] -> NSArray
  • [NSMutableArray mutableCopy] -> NSMutableArray

继续探索一下 [NSMutableArray array] 为什么最终返回NSMutableArray,有两种可能

  • NSMutableArray 重写了 + array方法
  • 还是调用的 [NSAarray array],那么问题就会变成 + array为什么这么神奇。到底是怎么做到的。

再写段代码验证一下

    Method arrayMethod = class_getClassMethod([NSArray class], @selector(array));
    Method mutableArrayMethod = class_getClassMethod([NSMutableArray class], @selector(array));
    
    
    IMP  arrayMethodIMP = method_getImplementation(arrayMethod);
    IMP  mutableArrayMethodIMP = method_getImplementation(mutableArrayMethod);
    
    assert(arrayMethod == mutableArrayMethod);
    assert(arrayMethodIMP == mutableArrayMethodIMP);

assert 成功,这说明 [NSMutableArray array] 最终调用的是 [NSArray array]

当然稳当起见可以反向证明一下:

@interface NSMutableArray(array)

+ (instancetype)array;

@end


@implementation NSMutableArray(array)

+ (instancetype)array {
    return [[NSArray array] mutableCopy];
}

@end

给NSMutableArray加个extension,最终导致断言失败,那么现在就要分析[NSArray array] 的魔力在哪里了,找了很久都没有找到Foundation的源码,如果谁知道哪里有下载,请告诉我。
先写个山寨版的构造函数。

@interface NSArray(Create)

+ (instancetype)createArray;

@end

@implementation NSArray(Create)

+ (instancetype)createArray {
    return [[self alloc]init];
}

@end

这个createArray 可以实现和系统array一样的功能。
好了最后为了证明array,确实调用了init, 现在init方法进行hook

@implementation NSMutableArray (array)

/*
 +load 方法会在类加入运行期调用,这是把方法调配放在+load方法里面的原因
 */
+ (void)load
{
    static dispatch_once_t onceToken;
    /*单例保证并行情况下不会有问题*/
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSelector = @selector(init);
        
        SEL swizzledSelector = @selector(customInit);
        
        /*获取原来的方法*/
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        /*获取要替换的方法*/
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        method_exchangeImplementations(originalMethod, swizzledMethod);
        
    });
}

- (instancetype)customInit {
   
  //  objc_msgSend()
    return [self customInit];

}




@end

遗憾的是这个hook 并没有正常运行,而且引发了crash

屏幕快照 2019-10-25 下午4.50.19.png

造成了栈溢出,具体原因还需要研究一下,可能跟NSMutableArray本身是一个类簇有关系。

copy vs mutableCopy
两者的区别在于
一个调用copyfromZone
另一个调用mutableCopyFromZone

self.myFish = @[@0x1234];

你可能感兴趣的:(iOS面试回顾 ---内存篇)