OC--load、initialize、一些NSObject方法

load、initialize

Load和Initialize往死了问是一种怎样的体验?
懒惰的 initialize 方法

OC--load、initialize、一些NSObject方法_第1张图片
+ load与+ initialize的异同

load流程

OC--load、initialize、一些NSObject方法_第2张图片
load.png

initialize代码

1、先调用父类superclass的initialize
2、再调用当前类的initialize

void _class_initialize(Class cls)
{
    Class supercls;
    BOOL reallyInitialize = NO;

    // 1. 未初始化过的父类调用 initialize 方法
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }

    {
        // 2. 通过加锁来设置 RW_INITIALIZING:正在初始化
        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;
        }
    }

    if (reallyInitialize) {
        // 3. 成功设置标志位,向当前类发送 +initialize 消息
        _setThisThreadIsInitializingClass(cls);
        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);

        // 4. 完成初始化,如果父类已经初始化完成,设置 RW_INITIALIZED:初始化完成,
        //    否则,在父类初始化完成之后再设置标志位。
        monitor_locker_t lock(classInitLock);
        if (!supercls  ||  supercls->isInitialized()) {
            _finishInitializing(cls, supercls);
        } else {
            _finishInitializingAfter(cls, supercls);
        }
        return;
    } else if (cls->isInitializing()) {
        // 5. 当前线程正在初始化当前类,直接返回,否则,会等待其它线程初始化结束后,再返回
        if (_thisThreadIsInitializingClass(cls)) {
            return;
        } else {
            monitor_locker_t lock(classInitLock);
            while (!cls->isInitialized()) {
                classInitLock.wait();
            }
            return;
        }
    } else if (cls->isInitialized()) {
        // 6. 初始化成功后,直接返回
        return;
    } else {
        _objc_fatal("thread-safe class init in objc runtime is buggy!");
    }
}

OC--load、initialize、一些NSObject方法_第3张图片
initalize.png

initialize与main的先后有特殊情况:在A类的load 方法中调用了B类的类方法

@implementation Father
+ (void)load {
    NSLog(@"father==> load===%@", [Dog class]);
}

+(void)initialize {
    NSLog(@"Father===>initialize");
}
@end

#warning 打印结果如下
2017-08-09 11:19:09.838 tests[34274:8415363] Dog===>initialize
2017-08-09 11:19:09.839 tests[34274:8415363] father==> load===Dog
2017-08-09 11:19:09.839 tests[34274:8415363] Dog==> load
2017-08-09 11:19:09.840 tests[34274:8415363] child==> load
2017-08-09 11:19:09.840 tests[34274:8415363] child + hahha==> load
2017-08-09 11:19:09.840 tests[34274:8415363] main

在 Initialize 方法内部可以进行一些不方便在编译期进行初始化的静态变量的赋值

#warning Person.m
// int 等基本类型可以在编译期进行赋值
static int numCount = 0; 
// 对象无法在编译器进行赋值
static NSMutableArray *dataSource;

+ (void)initialize {
    if (self == [Person class]) {
        // 不能在编译期赋值的对象在这里进行赋值
        dataSource = [[NSMutableArray alloc] init];
    }
}

避免父类的initialize被调用多次(如果当前类没有initialize方法就会调用父类的),可以通过类名判断

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

总结:

1、load 方法内部一般用来实现 Method Swizzle
2、Initialize方法一般用来初始化全局变量或者静态变量(对象类型)

一些NSObject方法

各种performSelector方法

/**
 向接收方发送一条指定的消息,并返回消息的结果
 @param aSelector 需要执行的方法(方法允许您发送在运行时才确定的消息。这意味着您可以传递一个变量选择器作为参数)
 @param object1 参数1
 @param object2 参数2
 @return 返回消息的结果
 */

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

/**
 在当前线程中调用一个方法,这个方法是异步的,必须在主线程调用,子线程无效,子线程可以用dispatch_after来代替
 注意:内部大概是创建一个定时器NSTimer

 @param aSelector 需要执行的方法
 @param anArgument 传递的参数
 @param delay 指定的时间之后
 @param modes Runloop模式(默认NSDefaultRunLoopMode)
 */
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;

/**
 取消方法调用请求
 (对于使用performSelector:withObject:afterDelay:方法(仅限于此方法)注册的执行请求)
(不过仅限于当前run loop,而不是所有的)

 @param aTarget 指定对象
 @param aSelector 指定方法
 @param anArgument 指定参数
 */
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;


/**
 主线程上执行某个对象的方法

 @param aSelector 需要执行的方法
 @param arg 方法参数
 @param wait YES,则当前线程被阻塞
 @param array Runloop模式(默认NSDefaultRunLoopMode)
 */
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
/**
 指定线程线程上执行某个对象的方法
 
 @param aSelector 需要执行的方法
 @param thr 指定线程
 @param arg 方法参数
 @param wait YES,则当前线程被阻塞
 @param array Runloop模式(默认NSDefaultRunLoopMode)
 */
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

/**
 后台线程中调用接收者的方法
(这个方法会在程序中创建一个新的线程。由aSelector表示的方法必须像程序中的其它新线程一样去设置它的线程环境)

 @param aSelector 需要执行的方法
 @param arg 方法参数
 */
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;

methodForSelector

NSObject类提供了两个方法来获取一个selector对应的方法实现的地址,如下所示:

- (IMP)methodForSelector:(SEL)aSelector
+ (IMP)instanceMethodForSelector:(SEL)aSelector

获取到了方法实现的地址,我们就可以直接将IMP以函数形式来调用。

对于methodForSelector:方法,如果接收者是一个对象,则aSelector应该是一个实例方法;如果接收者是一个类,则aSelector应该是一个类方法。

对于instanceMethodForSelector:方法,其只是向类对象索取实例方法的实现。如果接收者的实例无法响应aSelector消息,则产生一个错误。

instancesRespondToSelector与respondToSelector的区别

1、 instancesRespondToSelector只能写在类名后面,respondsToSelector可以写在类名和实例名后面。
2、[类 instancesRespondToSelector]判断的是该类的实例是否包含某方法,等效于:[该类的实例respondsToSelector]。
3、[类 respondsToSelector]用于判断是否包含某个类方法。

+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
- (BOOL)respondsToSelector:(SEL)aSelector;

-(BOOL)conformsToProtocol 与 +(BOOL)conformsToProtocol 的区别

// 当前类检测不到,不会检查父类
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
// 当前类检测不到,会检查父类
+ (BOOL)conformsToProtocol:(Protocol *)protocol;

class、superclass、isKindOfClass、isMemberOfClass

// 该方法返回类对象
+ (Class)class;
// 获取接收者的父类类对象
+ (Class)superclass;
// 查看一个类是否是另一个类的子类,
+ (BOOL)isSubclassOfClass:(Class)aClass;
isKindOfClass、isMemberOfClass
  • 联系:两者都能检测一个对象是不是某个类的成员
  • 区别:isKindOfClass可以检测到父类,如:ClassA *a = [ClassA alloc] init];,[a isKindOfClass:[NSObject class]] 可以检查出 a 是否是 NSObject派生类
+ (Class)class、- (Class)class区别

BBObj *obj = [BBObj new];
1、[BBObj class] = BBObj类对象
2、[obj j class] = object_getClass(obj) = obj->isa,就是BBObj类对象

+ (Class)class {
    return self;
}

- (Class)class {
    return object_getClass(self);
}
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;
}

例子:

    BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
    BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL res3 = [[UIButton class] isKindOfClass:[UIButton class]];
    BOOL res4 = [[UIButton class] isMemberOfClass:[UIButton class]];
    
    NSLog(@"%d %d %d %d", res1, res2, res3, res4);//输出 1 0 0 0

解析:


OC--load、initialize、一些NSObject方法_第4张图片
Runtime 各种Class关系.jpg

(1)+ (BOOL)isKindOfClass:(Class)cls方法内部,会先去获得object_getClass的类,而object_getClass的源码实现是去调用当前类的obj->getIsa(),最后在ISA()方法中获得meta class的指针。(+ (BOOL)isKindOfClass:(Class)cls是用meta class来对比cls)

(2)接着在isKindOfClass中有一个循环,先判断class是否等于meta class,不等就继续循环判断是否等于super class,不等再继续取super class,如此循环下去。

(3)[NSObject class]执行完之后调用isKindOfClass,第一次判断先判断NSObject 和 NSObject的meta class是否相等,之前讲到meta class的时候放了一张很详细的图,从图上我们也可以看出,NSObject的meta class与本身不等。接着第二次循环判断NSObject与meta class的superclass是否相等。还是从那张图上面我们可以看到:Root class(meta) 的superclass 就是 Root class(class),也就是NSObject本身。所以第二次循环相等,于是第一行res1输出应该为YES。
其他三个同样这么撸下去,全是0

特殊的

    NSArray *arr = [[NSArray alloc] init];
    BOOL a = [arr isMemberOfClass:[NSArray class]];// a = NO;因为arr是__NSArray0, NSArray是一个抽象的基类。这种模式就是了[类簇模式](http://blog.csdn.net/u013016828/article/details/41720353).

自定义对象的归档与解档大神终结入口1----大神终结入口2

NSData *cityData = [NSKeyedArchiver archivedDataWithRootObject:model];// model要现实NSCoding协议,initWithCoder:和encodeWithCoder
model = [NSKeyedUnarchiver unarchiveObjectWithData:data];

// 1.遵循NSCoding协议
@interface Person : NSObject 
// 2.设置属性
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;
@end

@implementation Person
// 解档
- (id)initWithCoder:(NSCoder *)aDecoder {
    // 父类现实就调用[super initWithCoder:aDecoder]
    // 父类没有现实[super init]
    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.age = [aDecoder decodeIntegerForKey:@"age"];
    }
    return self;
}
// 归档
- (void)encodeWithCoder:(NSCoder *)aCoder {
     // 父类现实就调用[super encodeWithCoder:aCoder];
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.age forKey:@"age"];
}
@end

你可能感兴趣的:(OC--load、initialize、一些NSObject方法)