performSelector
下面会输出神马呢?
- (IBAction)btnAction:(id)sender {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:0];
NSLog(@"2");
});
}
- (void)test
{
NSLog(@"3");
}
答案是只打印:1、2
因为[self performSelector:@selector(test) withObject:nil afterDelay:.0]实际在runloop里面,是一个定时器,但是因为在子线程,runloop是默认没有开启的。
实现原理:当调用 NSObject 的 performSelecter:afterDelay: 后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。
(以上内容来自:https://www.jianshu.com/p/9264b4847626)
load & initialize
load是加载的时候触发,initialize是第一次使用的时候触发,但是注意哦load是不会自动继承的,但是initialize是会的。
+ load
在 runtime源码 方法执行过程:(本文中所有涉及runtime源码均为 objc4-750.1 版本)
从
objc_os.mm
开始,这里可以理解成runtime的入口方法
1、_objc_init
(dyld通知注册镜像,在库初始化之前有libsystem调用)
2、load_images
(处理那些正在被dyld映射的镜像文件中的+load方法)
3、prepare_load_methods
(将要加载load方法时调用)
4、schedule_class_load
(遍历class调用执行 ‘5’)
5、add_class_to_loadable_list
(将需要执行+ load方法的class添加到一份全局表loadable_class
中)
6、add_category_to_loadable_list
( 将需要执行 load 的 category 添加到另一个全局列表loadable_category
里)
7、call_load_methods
(遍历调用‘8’和‘9’)
8、call_class_loads
(调用class的+load方法)
9、call_category_loads
(调用category的load方法)
子类的 +load 方法会在它的所有父类的 +load 方法执行之后执行,而分类的 +load 方法会在它的主类的 +load 方法执行之后执行。其实就是被依赖的会先执行load。
如果A是酱紫的:
@implementation ClassA
+ (void)load {
NSLog(@"class a loaded");
}
+ (void)initialize
{
NSLog(@"class A initialize");
}
@end
然后B是酱紫的,继承A的:
@interface ClassB : ClassA
@end
@implementation ClassB
+ (void)load {
NSLog(@"class b loaded");
}
+ (void)initialize
{
NSLog(@"class B initialize");
}
@end
那么如果现在 alloc init 一个 ClassB 会放生什么呢?结果是酱紫的:
2020-12-25 15:44:55.850725+0800 Example1[591:160086] class a loaded
2020-12-25 15:44:55.851376+0800 Example1[591:160086] class b loaded
2020-12-25 15:44:55.909289+0800 Example1[591:160086] class A initialize
2020-12-25 15:44:55.909342+0800 Example1[591:160086] class B initialize
如果注释掉B的 load 方法就是酱紫的:
2020-12-25 15:46:15.197522+0800 Example1[606:161162] class a loaded
2020-12-25 15:46:15.256616+0800 Example1[606:161162] class A initialize
2020-12-25 15:46:15.256689+0800 Example1[606:161162] class B initialize
如果注释掉B的 initialize 是酱紫的:
2020-12-25 15:47:05.969011+0800 Example1[619:161855] class a loaded
2020-12-25 15:47:05.969682+0800 Example1[619:161855] class b loaded
2020-12-25 15:47:06.033214+0800 Example1[619:161855] class A initialize
2020-12-25 15:47:06.033273+0800 Example1[619:161855] class A initialize
为什么会是酱紫呢?因为B虽然继承了A,但是它自己没有load方法的时候就不会执行load,也不会把A的load拿来执行;但是B如果没有initialize方法的时候,他会找A要一个initialize方法,但是因为B继承了A,在第一次使用B的时候也就第一次使用了A,所以A的initialize会再调用一次,于是A的initialize会调用两次。
如果先初始化A然后再初始化B就会是酱紫的(B仍旧没有自己的initialize):
ClassA *a = [[ClassA alloc] init];
NSLog(@"a and b separator");
ClassB *b = [[ClassB alloc] init];
输出
2020-12-25 15:50:13.567200+0800 Example1[634:163133] class a loaded
2020-12-25 15:50:13.568748+0800 Example1[634:163133] class b loaded
2020-12-25 15:50:13.643834+0800 Example1[634:163133] class A initialize
2020-12-25 15:50:13.643895+0800 Example1[634:163133] a and b separator
2020-12-25 15:50:13.643908+0800 Example1[634:163133] class A initialize
- 那么如何防止父类的initialize被调用多次呢?
// In Parent.m
+ (void)initialize {
if (self == [Parent class]) {
NSLog(@"Initialize Parent, caller Class %@", [self class]);
}
}
Category & Extensions & AssociatedObject
我们都知道 category 是不能加实例变量的,但是可以增加属性和方法,但是 runtime 提供了一个看起来很神奇的方法class_addIvar()
,但它却不能帮助我们给已有的类加实例变量,我们来看看它的定义:
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.
大概的意思说,这个函数只能在“构建一个类的过程中”调用。当编译类的时候,编译器生成了一个实例变量内存布局 ivar layout,来告诉运行时去那里访问类的实例变量们,一旦完成类定义,就不能再添加成员变量了。经过编译的类在程序启动后就被 runtime 加载,没有机会调用 addIvar。程序在运行时动态构建的类需要在调用 objc_registerClassPair 之后才可以被使用,同样没有机会再添加成员变量。
但是其实我们都知道 category 是可以添加属性的,通过用关联对象来实现 dynamic 的属性~ 那么关联对象是存在哪里的呢?
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
所有关联对象其实是由 AssociationsManager
管理的,大概是酱紫:
AssociationsManager
持有一个map,key是DISGUISE(object)
,也就是disguised_ptr_t
指针,这个指针又会指向一个保存了这个对象所有关联对象的map,这个map的key就是我们传入的key啦,会指向一个value和policy的对。
当对象销毁的时候,也会清空他对应的所有关联对象的:
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
话说为啥policy没有提供 weak 呢?
这个问题其实我没有找到一个答案,我感觉其实加weak的时候同时把引用加到弱引用表就好啦,没啥难度吖,为啥不能直接提供呢。同事表示弱引用表是编译时生成的,如果遍历关联对象找weak太麻烦了,但是我感觉编译时的话可能难度比较大啊,内容应该还是动态添加的叭。
- Extension 和 Category 的区别?
- Extension 在编译期决议,它就是类的一部分,在编译期和头文件里的 @interface 以及实现文件里的 @implement 一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。Extension 一般用来隐藏类的私有信息,你必须有一个类才能为这个类添加 Extension,所以你无法为系统的类比如 NSString 添加 Extension。
- Category 则完全不一样,它是在运行期决议的。
- Extension 可以添加成员变量,而 Category 一般不可以。
总之,就 Category 和 Extension 的区别来看,Extension 可以添加成员变量,而 Category 是无法添加成员变量的。因为 Category 在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局。
Extension
在swift中,swift可以为特定的class, strut, enum或者protocol添加新的特性。当你没有权限对源代码进行改造的时候,此时可以通过extension来对类型进行扩展。extension有点类似于OC的类别 -- category,但稍微不同的是category有名字,而extension没有名字。
swift的extension可以做如下几件事
- 添加计算属性 - computed properties
- 添加方法 - methods
- 添加初始化方法 - initializers
- 添加附属脚本 - subscripts
- 添加并使用嵌套类型 - nested types
- 遵循并实现某一协议 - conform protocol
在swift中,你甚至可以对一个协议protocol进行扩展,实现其协议方法或添加额外的功能,以便于实现该协议的类型可以使用,在swift中,这叫做协议扩展 - protocol extension。但是它不能覆盖已有的特性。例如Animal已经有eat的方法,我们不能使用extension覆盖Animal的eat方法。
不能覆写已有方法这里其实也能感受到其实extension是编译时定的,而category是运行时的,所以category运行覆写。
reference:
https://www.jianshu.com/p/ccbd2a07db1f
https://www.jianshu.com/p/f5c8d9a043c0
https://www.cnblogs.com/lxlx1798/p/9256643.html
https://www.jianshu.com/p/783df05a9b59
网友评论