iOS-琐碎知识点(一)

2,995 阅读6分钟

问题先行

1initalizeload的区别是什么?
2、如下代码输出结果是?

@implementation ASSon

- (instancetype)init {
    if (self == [super init]) {
        NSLog(@"[self class]=%@,[super class]=%@,",[self class],[super class]);
    }
    return self;
}

@end

3、编译后的类能否添加成员变量?运行时创建的类能否添加成员变量?
4、如下代码输出结果是? image.png

[[[ASSon alloc]init] faction1];

5、关联对象需要自己进行释放吗?

initalize分析

官方描述 image.png Initializes the class before it receives its first message
在这个类接收第一条消息之前调用

根据这个重要的注释,我们看下消息发送的流程方法路径,不难发现在判断类是否初始化即初始化的相关逻辑中存在initalize相关。

相关路径如下

lookUpImpOrForward->realizeAndInitializeIfNeeded_locked->initializeAndLeaveLocked->initializeAndMaybeRelock->initializeNonMetaClass->callInitialize

NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;
    Class curClass;

    runtimeLock.assertUnlocked();

    /// 代码省略......
    checkIsKnownClass(cls);
    
    /// 判断类是否实现,如果没有是实现,需要先实现。此时的目的是为了确定父类链,方法后续的循环
    cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
    // runtimeLock may have been dropped but is now locked again
    runtimeLock.assertLocked();
    curClass = cls;

    /// 代码省略......
    return imp;
}
static Class
realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize)
{
    runtimeLock.assertLocked();
    if (slowpath(!cls->isRealized())) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        // runtimeLock may have been dropped but is now locked again
    }
    /// 判断类是否初始化,如果没有需要先初始化
    if (slowpath(initialize && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
        // runtimeLock may have been dropped but is now locked again

        // If sel == initialize, class_initialize will send +initialize and
        // then the messenger will send +initialize again after this
        // procedure finishes. Of course, if this is not being called
        // from the messenger then it won't happen. 2778172
    }
    return cls;
}
// Locking: caller must hold runtimeLock; this may drop and re-acquire it
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
    return initializeAndMaybeRelock(cls, obj, lock, true);
}
static Class initializeAndMaybeRelock(Class cls, id inst,
                                      mutex_t& lock, bool leaveLocked)
{
    lock.assertLocked();
    ASSERT(cls->isRealized());

    if (cls->isInitialized()) {
        if (!leaveLocked) lock.unlock();
        return cls;
    }

    // Find the non-meta class for cls, if it is not already one.
    // The +initialize message is sent to the non-meta class object.
    Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);

    // Realize the non-meta class if necessary.
    if (nonmeta->isRealized()) {
        // nonmeta is cls, which was already realized
        // OR nonmeta is distinct, but is already realized
        // - nothing else to do
        lock.unlock();
    } else {
        nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
        // runtimeLock is now unlocked
        // fixme Swift can't relocate the class today,
        // but someday it will:
        cls = object_getClass(nonmeta);
    }

    // runtimeLock is now unlocked, for +initialize dispatch
    ASSERT(nonmeta->isRealized());
    /// 
    initializeNonMetaClass(nonmeta);

    if (leaveLocked) runtimeLock.lock();
    return cls;
}
void initializeNonMetaClass(Class cls)
{
    ASSERT(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->getSuperclass();
    if (supercls  &&  !supercls->isInitialized()) {
        /// 递归调用父类
        initializeNonMetaClass(supercls);
    }
    
    /// 代码省略......
	{
		/// 消息发送initialize
        callInitialize(cls);

        /// 代码省略......
        return;
    }
    
    /// 代码省略......
}
void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    asm("");
}
  • initialize在类或者其子类的第一个方法被调用前(发送消息前)调用
  • 父类的initialize方法会比子类先执行
  • 当子类未实现initialize方法时,会调用父类initialize方法;子类实现initialize方法时,会覆盖父类initialize方法
  • 当有多个分类都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行最后被加载到内存中的分类的方法)

[self class]、[super class]分析

通过clang编译出cpp文件,然后分析核心代码

// @implementation ASSon

static instancetype _I_ASSon_init(ASSon * self, SEL _cmd) {
    if (self == ((ASSon *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ASSon"))}, sel_registerName("init"))) {
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_nm_y1t1syks0yj_jrzr73yc4qp00000gn_T_ASSon_b9cdb4_mi_0,((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")),((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ASSon"))}, sel_registerName("class")));
    }
    return self;
}

// @end

struct __rw_objc_super { 
	struct objc_object *object; 
	struct objc_object *superClass; 
	__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};

[self class]

[self class]是通过objc_msgSend进行消息发送,消息接收者是self,方法编号class

- (Class)class {
    return object_getClass(self);
}
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

通过classobjc源码可知是获取对象的isa,当前的对象是ASSonisa就是ASSon,所以[self class]打印的是ASSon

[super class]

[super class]则是通过objc_msgSendSuper进行消息发送,消息接受者为__rw_objc_super结构类型参数,方法编号class

注意由于objc会存在一些编译优化的操作,我们通过汇编看下是否有进行objc_msgSendSuper方法的编译优化。 image.png 发现最终是调用objc_msgSendSuper2方法。

// objc_msgSendSuper2() takes the current search class, not its superclass.
OBJC_EXPORT id _Nullable
objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
    OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0, 2.0);

objc_msgSendSuper2源码如上可以看出第一个参数为objc_super,由此可知.cpp文件中__rw_objc_super其实是一个中间结构体。

/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull id receiver;

    /// Specifies the particular superclass of the instance to message. 
#if !defined(__cplusplus)  &&  !__OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};

通过objc_super的源码可知,当前消息的接收者为参数receiver,而receiver等于传入self,因此[super class]其中的selfinit后的实例对象,其isa即消息接收者是ASSon本类。

super是关键字官方解释
使用super关键字发送消息会被编译器转化为调用objc_msgSendSuper以及相关函数 image.png

dealloc分析

dealloc流程图 image.png 通过流程图我们可以看到系统会自动进行一些操作

  • 1、C++函数释放:objc_cxxDestruct
  • 2、移除关联属性:_object_remove_assocations
  • 3、将弱引用自动设置nil:weak_clear_no_lock(&table.weak_table, (id)this)
  • 4、引用计数处理:table.refcnts.erase(this)
  • 5、销毁对象:free(obj)

问题先行解答

1initalizeload的区别是什么?
通过对initalizeload分析,可以在调用时机调用顺序执行方式三个方面比较。

调用时机
initialize:方法是在类收到第一条消息之前被调用的,一个类只会调用一次,如果类没有初始化,这个也不会调用,在main函数之后调用
load:方法是在程序加载类和分类的时候调用的,每个类和分类只会调用一次,调用和类有没有被使用无关,在main函数之前调用

调用顺序
initialize:父类->本类,子类如果没有实现initialize方法的话,也会自动调用父类的initialize方法
load:父类->本类->分类,分类后面加载的会覆盖前面加载的,子类中没有实现load方法的话,不会调用父类的load方法

执行方式
load:根据load方法的地址直接调用
initializeobjc_msgSend消息发送

2、如下代码输出结果是?
结果如下,具体原因见[self class][super class]分析

[self class]=ASSon,[super class]=ASSon

3、编译后的类能否添加成员变量?运行时创建的类能否添加成员变量?
(iOS-objc_object & objc_class)和iOS-类的加载(一)之前分析过实例变量是存储在class_ro_t中,其是在编译阶段(类的加载)就生成的且是只读属性,后续无法修改。

通过runtime相关方法可知是可以添加的,objc_allocateClassPair创建类,class_addIvar给类添加成员变量,objc_registerClassPair注册类。但是有一点需要注意,在注册类之后,就不能再添加了。

4、如下代码输出结果是?

原类+[ASSon load]
分类2+[ASSon(grandson) load]
分类1+[ASSon(Son) load]
分类1+[ASSon(Son) initialize]
分类1-[ASSon(Son) faction1]
  • 如果同名方法是普通方法(包括initialize),只调用分类方法。因为分类的方法是在类realize实现之后再attach进去的,插在类的方法的前面,所以只调用分类的方法。
  • 如果同名方法是load方法,先原类load,后分类load可以参考iOS-类的加载(二)分析 注意如果有多个分类,加载顺序看这里分类的顺序 image.png

5、关联对象需要自己进行释放吗?
通过上面dealloc分析,可知系统会自动进行移除关联属性,因此不需要自己进行释放。