OC底层原理分析2

89 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情

isKindofClass、isMemberOfClass

+ (BOOL)isMemberOfClass:(Class)cls {
  return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
  return [self class] == cls;
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self), tcls; tcls = tcls->superclass){
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class], tcls; tcls = tcls->superclass){
        if (tcls == cls) return YES;
    }
    return NO;
}

测试题:

@implementation YCViewController 

- (void)viewDidLoad{
    
    NSLog(@"%d",[self class] == [YCViewController class]);
    NSLog(@"%d",[self class] == object_getClass(self));
    NSLog(@"%d",[self class] == [[YCViewController class] class]);
    NSLog(@"%d",[self class] == object_getClass(object_getClass(self)));
    NSLog(@"%d",[[self class] class] == object_getClass(object_getClass(self)));

    NSLog(@"%d",[self isMemberOfClass:[YCViewController class]]);
    NSLog(@"%d",[[self class] isMemberOfClass:[YCViewController class]]);
    NSLog(@"%d",[[self class] isMemberOfClass:object_getClass([YCViewController class])]);

    NSLog(@"%d",[self isKindOfClass:[YCViewController class]]);
    NSLog(@"%d",[self isKindOfClass:[NSObject class]]);
    NSLog(@"%d",[[self class] isKindOfClass:[YCViewController class]]);
    NSLog(@"%d",[[self class] isKindOfClass:object_getClass([YCViewController class])]);
    NSLog(@"%d",[[self class] isKindOfClass:object_getClass([NSObject class])]);
    
    //****格外注意***
    NSLog(@"%d",[YCViewController isKindOfClass:[NSObject class]]);
}

//答案
/*
1
1
1
0
0
1
0
1
1
1
0
1
1

 //****格外注意***
1
*/

@end
// 面试题
@interface YCObject ()
@property (nonatomic,copy) NSString *name;
@end

@implementation YCObject

- (void)print {
 
    NSLog(@"my name is %@",self.name);
}

@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *test = @"123";
    
    id cls = [YCObject class];
    
    void *obj = &cls;
    
    [(__bridge id)obj print];
}
  • 解释
    cls 指向YCObject类对象,相当于YCObject实例对象的isa指针,obj指针指向cls,obj相当于实例对象的指针。即:
YCObject *ycObj = [YCObject new];
ycObj->isa == cls // cls == YCObject实例对象isa
ycObj == obj

等同于:
obj->cls->[ YCObject class]
ycObj->isa->[ YCObject class]

因此:
[(__bridge id)obj print];
等同于:
[ycObj print];

再考虑到函数调用栈中局部变量的分配,是从高往低分配,所以上面viewDidLoad方法中, 从高到低变量的分配依次是:test >> cls >> obj,都是指针类型变量,因此每个变量占用8个字节。 再结合YCObject的print方法中调用到局部变量name,实例对象的内存布局前8个字节是isa指针,因此 实例对象偏移8个字节才能拿到name变量,所以再viewDidLoad方法上下文环境中,obj偏移8个字节即是 test字符串,因此打印输出为“my name is 123”

注意

如果注释掉“NSString *test = @"123";”,则输出为“my name is YCViewController<....>”, 原因是受到“[super viewDidLoad];”方法影响,这句话会调用objc_msgSendSuper方法,此方法第一个参数为结构体,结构体第一个参数为self

Runloop

runloop 与线程是一一对应关系,线程中默认是没有runloop的,只会在线程第一次获取runloop的时候才会创建runloop,且如果runloop没有一个mode的话,runloop会立马结束

typedef struct __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
    pthread_t _pthred;
    CFMutableSetRef _commonModes;  // 支持commonMode的runloopMode,如defaultMode,UITrackingMode
    CFMutableSetRef _commonModeItems; // 设置了commonMode的timer,source
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
}

typedef struct  __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
    CFStringRef _name;
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
}

Runloop 休眠实现原理:应用程序在用户态下发送mach_msg()消息,操作系统切换到内核态调用系统休眠,等待消息,有消息就切换回用户态处理消息,没有就继续休眠

Runloop使用场景

  • 控制线程生命周期(线程保活)
  • NSTimer滑动时停止工作问题
  • 监测应用卡顿