iOS--runtime

news/2024/5/19 16:24:38

什么是Runtime

  • runtime是由C和C++、汇编实现的一套API,为OC语言加入了面向对象、运行时的功能
  • 运行时(runtime)将数据类型的确定由编译时推迟到了运行时
  • 平时编写的OC代码,在程序运行过程中,最终会转换成runtime的C语言代码——runtime是Objective-C 的幕后⼯作者

如类结构中的ro和rw属性

  • ro(read-only)在编译时已经确定
  • rw(read-write)在运行时才确定,因此可以使用runtime进行修改

方法的本质是什么

方法的本质是发送消息objc_msgSend,即寻找IMP的过程
发送消息会有以下⼏个流程:

  • 快速查找流程——通过汇编objc_msgSend查找缓存cache_t是否有imp实现
  • 慢速查找流程——通过C++中lookUpImpOrForward递归查找当前类和父类的rwmethodlist的方法
  • 动态方法解析——通过调用resolveInstanceMethodresolveClassMethod来动态方法决议——实现消息动态处理
  • 快速转发流程——通过CoreFoundation来触发消息转发流程,forwardingTargetForSelector实现快速转发,由其他对象来实现处理方法
  • 慢速转发流程——先调用methodSignatureForSelector获取到方法的签名,生成对应的invocation;再通过forwardInvocation来进行处理
  • 以上流程均无法挽救就崩溃并报错

SEL和IMP的关系

遇到这种问题先要解释两者分别是什么?再解释两者的关系

SEL是方法编号,也是方法名,在dyld加载镜像到内存时,通过_read_image方法加载到内存的表中了
IMP是函数实现指针,找IMP就是找函数实现的过程
SELIMP的关系就可以解释为:

  • SEL就相当于书本的⽬录标题
  • IMP就是书本的⻚码
  • 函数就是具体页码对应的内容

比如我们想在《程序员的自我修养——链接、装载与库》一书中找到“动态链接”(SEL),肯定会翻到179页(IMP),179页会开始讲述具体内容(函数实现)

请添加图片描述

能否向运⾏时创建的类中添加实例变量

具体情况具体分析:

  • 编译好的类不能添加实例变量
  • 运行时创建的类可以添加实例变量,但若已注册到内存中就不行了

原因:

  • 编译好的实例变量存储的位置在ro,而ro是在编译时就已经确定了的
  • ⼀旦编译完成,内存结构就完全确定就⽆法修改
  • 只能修改rw中的方法或者可以通过关联对象的方式来添加属性

利用runtime-API创建对象

这题对runtime-API要求程度比较高

API介绍

动态创建类

/***创建类**superClass: 父类,传Nil会创建一个新的根类*name: 类名*extraBytes: 额外的内存空间,一般传0*return:返回新类,创建失败返回Nil,如果类名已经存在,则创建失败*/
Class FXPerson = objc_allocateClassPair([NSObject class], "LGPerson", 0);

添加成员变量

/**
*添加成员变量
*这个函数只能在objc_allocateClassPair和objc_registerClassPair之间调用。不支持向现有类添加一个实例变量
*这个类不能是元类,不支持在元类中添加一个实例变量
*实例变量的最小对齐为1 << align,实例变量的最小对齐依赖于ivar的类型和机器架构。对于任何指针类型的变量,请通过log2(sizeof(pointer_type))
*
*cls 往哪个类添加
*name 添加的名字
*size 大小
*alignment 对齐处理方式
*types 签名
*/
class_addIvar(FXPerson, "fxName", sizeof(NSString *), log2(sizeof(NSString *)), "@");

注册到内存

/***往内存注册类** cls 要注册的类*/objc_registerClassPair(FXPerson);

添加属性变量

/**
*往类里面添加属性
*
*cls 要添加属性的类
*name 属性名字
*attributes 属性的属性数组。
*attriCount 属性中属性的数量。
*/
class_addProperty(targetClass, propertyName, attrs, 4);

添加方法

/***往类里面添加方法**cls 要添加方法的类*sel 方法编号*imp 函数实现指针*types 签名*/
class_addMethod(FXPerson, @selector(setHobby), (IMP)fxSetter, "v@:@");

整体使用

// hobby的setter-IMP
void fxSetter(NSString *value) {printf("%s/n",__func__);
}// hobby的getter-IMP
NSString *fxHobby() {return @"iOS";
}// 添加属性变量的封装方法
void fx_class_addProperty(Class targetClass, const char *propertyName) {objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([NSString class])] UTF8String] }; //typeobjc_property_attribute_t ownership0 = { "C", "" }; // C = copyobjc_property_attribute_t ownership = { "N", "" }; //N = nonatomicobjc_property_attribute_t backingivar  = { "V", [NSString stringWithFormat:@"_%@",[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]].UTF8String };  //variable nameobjc_property_attribute_t attrs[] = {type, ownership0, ownership, backingivar};class_addProperty(targetClass, propertyName, attrs, 4);
}// 打印属性变量的封装方法
void fx_printerProperty(Class targetClass){unsigned int outCount, i;objc_property_t *properties = class_copyPropertyList(targetClass, &outCount);for (i = 0; i < outCount; i++) {objc_property_t property = properties[i];fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));}
}int main(int argc, const char * argv[]) {@autoreleasepool {// 动态创建类Class FXPerson = objc_allocateClassPair([NSObject class], "FXPerson", 0);// 添加成员变量class_addIvar(FXPerson, "name", sizeof(NSString *), log2(sizeof(NSString *)), "@");// 注册到内存objc_registerClassPair(FXPerson);// 添加属性变量fx_class_addProperty(FXPerson, "hobby");fx_printerProperty(FXPerson);// 添加方法(为属性方法添加setter、getter方法)class_addMethod(FXPerson, @selector(setHobby:), (IMP)fxSetter, "v@:@");class_addMethod(FXPerson, @selector(hobby), (IMP)fxHobby, "@@:");// 开始使用id person = [FXPerson alloc];[person setValue:@"Felix" forKey:@"name"];NSLog(@"FXPerson的名字是:%@ 爱好是:%@", [person valueForKey:@"name"], [person valueForKey:@"hobby"]);}return 0;
}

注意事项

  • 记得导入<objc/runtime.h>
  • 添加成员变量class_addIvar必须在objc_registerClassPair前,因为注册到内存时ro已经确定了,不能再往ivars添加(同第四个面试题)
  • 添加属性变量class_addProperty可以在注册内存前后,因为是往rw中添加的
  • class_addProperty中“属性的属性”——nonatomic/copy是根据属性的类型变化而变化的
  • class_addProperty不会自动生成settergetter方法,因此直接调用KVC会崩溃

不只可以通过KVC打印来检验,也可以下断点查看ro、rw的结构来检验

关联对象分析

实则是为了解决分类创建属性的问题

分类直接添加属性的后果

  • 编译会出现警告:没有setter方法和getter方法
  • 运行会报错:-[FXPerson setName:]: unrecognized selector sent to instance 0x100f61180’
    请添加图片描述

为什么不能直接添加属性

Category在runtime中是用一个结构体表示的:

struct category_t {const char *name;classref_t cls;struct method_list_t *instanceMethods;struct method_list_t *classMethods;struct protocol_list_t *protocols;struct property_list_t *instanceProperties;// Fields below this point are not always present on disk.struct property_list_t *_classProperties;...
};

里面虽然可以添加属性变量,但是这些properties并不会自动生成Ivar,也就是不会有 @synthesize的作用,dyld加载期间,这些分类会被加载并patch到相应的类中。这是一个动态过程,Ivar不能动态添加

解决方案

手动实现setter、getter方法,关联对象

- (void)setName:(NSString *)name {/**参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self。参数二:void * == id key : 属性名,根据key获取关联对象的属性的值,在objc_getAssociatedObject中通过次key获得属性的值并返回。参数三:id value : 关联的值,也就是set方法传入的值给属性去保存。参数四:objc_AssociationPolicy policy : 策略,属性以什么形式保存。*/objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}- (NSString *)name {/**参数一:id object : 获取哪个对象里面的关联的属性。参数二:void * == id key : 什么属性,与objc_setAssociatedObject中的key相对应,即通过key值取出value。*/return objc_getAssociatedObject(self, @"name");
}

关联对象原理

setter方法——objc_setAssociatedObject分析

苹果设计接口时往往会加个中间层——即使底层实现逻辑发生变化也不会影响到对外接口

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {_object_set_associative_reference(object, (void *)key, value, policy);
}

跟进去看看_object_set_associative_reference实现

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {// This code used to work when nil was passed for object and key. Some code// probably relies on that to not crash. Check and handle it explicitly.// rdar://problem/44094390if (!object && !value) return;assert(object);if (object->getIsa()->forbidsAssociatedObjects())_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));// retain the new value (if any) outside the lock.// 在锁之外保留新值(如果有)。ObjcAssociation old_association(0, nil);// acquireValue会对retain和copy进行操作,id new_value = value ? acquireValue(value, policy) : nil;{// 关联对象的管理类AssociationsManager manager;// 获取关联的 HashMap -> 存储当前关联对象AssociationsHashMap &associations(manager.associations());// 对当前的对象的地址做按位去反操作 - 就是 HashMap 的key (哈希函数)disguised_ptr_t disguised_object = DISGUISE(object);if (new_value) {// break any existing association.// 获取 AssociationsHashMap 的迭代器 - (对象的) 进行遍历AssociationsHashMap::iterator i = associations.find(disguised_object);if (i != associations.end()) {// secondary table existsObjectAssociationMap *refs = i->second;// 根据key去获取关联属性的迭代器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).// 如果AssociationsHashMap从没有对象的关联信息表,// 那么就创建一个map并通过传入的key把value存进去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.// 如果传入的value是nil,并且之前使用相同的key存储过关联对象,// 那么就把这个关联的value移除(这也是为什么传入nil对象能够把对象的关联value移除)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).// 最后把之前使用传入的这个key存储的关联的value释放(OBJC_ASSOCIATION_SETTER_RETAIN策略存储的)if (old_association.hasValue()) ReleaseValue()(old_association);
}
  • ObjcAssociation old_association(0, nil)处理传进来的值得到new_value
  • 获取到管理所有关联对象的hashmap总表的管理者AssociationsManager,然后拿到hashmap总表AssociationsHashMap
  • DISGUISE(object)对关联对象的地址进行取反操作得到哈希表对应的下标index
  • 如果new_value为空(即对属性赋值为nil)就直接找到相应的表进行删除
  • 如果new_value不为空,就拿到总表的迭代器通过拿到的下标index进行遍历查找;如果找到管理对象的关联属性哈希map表,然后再通过key去遍历取值
    • 如果取到了,就先把新值设置到key上,再将旧值释放掉
    • 如果没取到,就直接将新值设置在key上

还是不明白就LLDB断点调试呗请添加图片描述

getter方法——objc_getAssociatedObject分析

id objc_getAssociatedObject(id object, const void *key) {return _object_get_associative_reference(object, (void *)key);
}
id _object_get_associative_reference(id object, void *key) {id value = nil;uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;{// 关联对象的管理类AssociationsManager manager;AssociationsHashMap &associations(manager.associations());// 生成伪装地址。处理参数 object 地址disguised_ptr_t disguised_object = DISGUISE(object);// 所有对象的额迭代器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()) {// 找到 - 把值和策略读取出来ObjcAssociation &entry = j->second;value = entry.value();policy = entry.policy();// OBJC_ASSOCIATION_GETTER_RETAIN - 就会持有一下if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {objc_retain(value);}}}}if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {objc_autorelease(value);}return value;
}

objc_getAssociatedObjectobjc_setAssociatedObject的逆过程
在这里插入图片描述

weak置空原理

当面试官问你weak置空原理是什么,你可能只知道weak怎么用却不知道怎么答吧

int main(int argc, const char * argv[]) {@autoreleasepool {FXPerson *person = [[FXPerson alloc] init];id __weak person = object;}return 0;
}

Xcode菜单栏Debug->Debug Workflow->Always show Disassembly打上勾查看汇编——汇编代码会来到libobjc库的objc_initWeak

weak创建过程

objc_initWeak

  • location:表示__weak指针的地址(我们研究的就是__weak指针指向的内容怎么置为nil)
  • newObj:所引用的对象,即例子中的person
id
objc_initWeak(id *location, id newObj)
{if (!newObj) {*location = nil;return nil;}return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>(location, (objc_object*)newObj);
}

storeWeak

  • HaveOld:weak指针之前是否已经指向了一个弱引用
  • HaveNew:weak指针是否需要指向一个新引用
  • CrashIfDeallocating:如果被弱引用的对象正在析构,此时再弱引用该对象,是否应该crash

storeWeak最主要的两个逻辑点(源码太长,这里不贴了)
请添加图片描述

由于是第一次调用,所以走haveNew分支——获取到的是新的散列表SideTable,主要执行了weak_register_no_lock方法来进行插入

weak_register_no_lock

  • 主要进行了 isTaggedPointerdeallocating条件判断
  • 将被弱引用对象所在的weak_table中的weak_entry_t哈希数组中取出对应的weak_entry_t
  • 如果weak_entry_t不存在,则会新建一个并插入
  • 如果存在就将指向被弱引用对象地址的指针referrer通过函数append_referrer插入到对应的weak_entry_t引用数组
    请添加图片描述
    append_referrer
    找到弱引用对象的对应的weak_entry哈希数组中插入请添加图片描述

weak创建流程

请添加图片描述

weak销毁过程

由于弱引用在析构dealloc时自动置空,所以查看dealloc的底层实现并LLVM调试

  • _objc_rootDealloc->rootDealloc
  • rootDealloc->object_dispose
  • object_dispose->objc_destructInstance
  • objc_destructInstance->clearDeallocating
  • clearDeallocating->sidetable_clearDeallocating
  • sidetable_clearDeallocating3->table.refcnts.erase(it)

weak销毁流程

在这里插入图片描述

Method Swizzing坑点

黑魔法应用

在日常开发中,再好的程序员都会犯错,比如数组越界

NSArray *array = @[@"F", @"e", @"l", @"i", @"x"];
NSLog(@"%@", array[5]);
NSLog(@"%@", [array objectAtIndex:5]);

因此为了避免数组越界这种问题,大神们开始玩起了黑魔法——method swizzing

  • 新建NSArray分类
  • 导入runtime头文件——<objc/runtime.h>
  • 写下新的方法
  • +load利用黑魔法交换方法
#import "NSArray+FX.h"
#import <objc/runtime.h>@implementation NSArray (FX)+ (void)load {// 交换objectAtIndex方法Method oriMethod1 = class_getInstanceMethod(self, @selector(objectAtIndex:));Method swiMethod1 = class_getInstanceMethod(self, @selector(fx_objectAtIndex:));method_exchangeImplementations(oriMethod1, swiMethod1);// 交换取下标方法Method oriMethod2 = class_getInstanceMethod(self, @selector(objectAtIndexedSubscript:));Method swiMethod2 = class_getInstanceMethod(self, @selector(fx_objectAtIndexedSubscript:));method_exchangeImplementations(oriMethod2, swiMethod2);
}- (void)fx_objectAtIndex:(NSInteger)index {if (index > self.count - 1) {NSLog(@"objectAtIndex————————数组越界");return;}return [self fx_objectAtIndex:index];
}- (void)fx_objectAtIndexedSubscript:(NSInteger)index {if (index > self.count - 1) {NSLog(@"取下标————————数组越界");return;}return [self fx_objectAtIndexedSubscript:index];
}@end

然而程序还是无情的崩了…

在这里插入图片描述
其实在iOS中NSNumber、NSArray、NSDictionary等这些类都是类簇(Class Clusters),一个NSArray的实现可能由多个类组成。所以如果想对NSArray进行方法交换,必须获取到其真身进行方法交换,直接对NSArray进行操作是无效的
以下是NSArrayNSDictionary本类的类名
请添加图片描述
这样就好办了,可以使用runtime取出本类请添加图片描述

坑点一

黑魔法最好写成单例,避免多次交换

比如说添加了[NSArray load]代码,方法实现又交换回去了导致了崩溃请添加图片描述
+load方法改写成单例(虽然不常见,但也要避免)

+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{// 交换objectAtIndex方法Method oriMethod1 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));Method swiMethod1 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(fx_objectAtIndex:));method_exchangeImplementations(oriMethod1, swiMethod1);// 交换取下标方法Method oriMethod2 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndexedSubscript:));Method swiMethod2 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(fx_objectAtIndexedSubscript:));method_exchangeImplementations(oriMethod2, swiMethod2);});
}

坑点二

①子类交换父类实现的方法

  • 父类FXPerson类中有-doInstance方法,子类FXSon类没有重写
  • FXSon类新建分类做了方法交换,新方法中调用旧方法
  • FXPerson类FXSon类调用-doInstance请添加图片描述
    子类打印出结果,而父类调用却崩溃了,为什么会这样呢?
    因为FXSon类交换方法时取得doInstance先在本类搜索方法,再往父类里查找,在FXFather中找到了方法实现就把它跟新方法进行交换了。可是新方法是在FXSon分类中的,FXFather找不到imp就unrecognized selector sent to instance 0x600002334250
    所以这种情况下应该只交换子类的方法,不动父类的方法
+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{Method oriMethod = class_getInstanceMethod(self, @selector(doInstance));Method swiMethod = class_getInstanceMethod(self, @selector(fx_doInstance));BOOL didAddMethod = class_addMethod(self, @selector(doInstance), method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));if (didAddMethod) {class_replaceMethod(self, @selector(fx_doInstance), method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));} else {method_exchangeImplementations(oriMethod, swiMethod);}});
}
  • 通过class_addMethod给FXSon类添加方法(class_addMethod不会取代本类中已存在的实现,只会覆盖本类中继承父类的方法实现)
    • 取得新方法swiMethod的实现和方法类型
    • 往方法名@selector(fx_doInstance)添加方法
    • class_addMethod 把新方法实现放到旧方法名中,此刻调用doInstance就是调用fx_doInstance,但是调用fx_doInstance会崩溃
  • 根据didAddMethod判断是否添加成功
    • 添加成功说明之前本类没有实现——class_replaceMethod替换方法
    • 添加失败说明之前本类已有实现——method_exchangeImplementations交换方法
    • class_replaceMethoddoInstance方法实现替换掉fx_doInstance中的方法实现
      请添加图片描述
      在这里插入图片描述
      FXPerson类只写了方法声明,没有方法实现,却做了方法交换——会造成死循环请添加图片描述
  • doInstance方法中添加了新的方法实现
  • fx_doInstance方法中想用旧的方法实现替代之前的方法实现,可是找不到doInstance实现,导致class_replaceMethod无效->在fx_doInstance中调用fx_doInstance就会死循环
    请添加图片描述
+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{Method oriMethod = class_getInstanceMethod(self, @selector(doInstance));Method swiMethod = class_getInstanceMethod(self, @selector(fx_doInstance));if (!oriMethod) {class_addMethod(self, @selector(doInstance), method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd) {NSLog(@"方法未实现");}));}BOOL didAddMethod = class_addMethod(self, @selector(doInstance), method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));if (didAddMethod) {class_replaceMethod(self, @selector(fx_doInstance), method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));} else {method_exchangeImplementations(oriMethod, swiMethod);}});
}

在这里插入图片描述

注意事项

使用Method Swizzling有以下注意事项:

  • 尽可能在+load方法中交换方法
  • 最好使用单例保证只交换一次
  • 自定义方法名不能产生冲突
  • 对于系统方法要调用原始实现,避免对系统产生影响
  • 做好注释(因为方法交换比较绕)
  • 迫不得已情况下才去使用方法交换

这是一份做好封装的Method Swizzling交换方法

+ (void)FXMethodSwizzlingWithClass:(Class)cls oriSEL:(SEL)oriSEL swizzledSEL:(SEL)swizzledSEL {if (!cls) NSLog(@"传入的交换类不能为空");Method oriMethod = class_getInstanceMethod(cls, oriSEL);Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);if (!oriMethod) {class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd) {NSLog(@"方法未实现");}));}BOOL didAddMethod = class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));if (didAddMethod) {class_replaceMethod(cls, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));} else {method_exchangeImplementations(oriMethod, swiMethod);}
}

某个方法进行多次交换,最终的调用顺序是怎样的?请添加图片描述

补充面试题二

问:如果发现方法交换之后不生效,应该怎么排查?
答:解决的方案有多种,主要从两个方向进行思考

  • 方法交换的class有没有问题
  • 方法交换的method有没有问题

http://www.mrgr.cn/p/31582368

相关文章

分享200+个关于AI的网站

分享200个关于AI的网站 欢迎大家访问&#xff1a;https://tools.haiyong.site/ai 快速导航 AI 应用AI 写作AI 编程AI 设计AI 作图AI 训练模型AI 影音编辑AI 效率助手 AI 应用 文心一言: https://yiyan.baidu.com/ 百度出品的人工智能语言模型 ChatGPT: https://chat.openai.c…

2023年值得推荐的5个数据可视化平台

之前看过一篇介绍20款国外常用的数据可视化工具后&#xff0c;很多朋友在评论区表示国内也有很多很不错的主流数据可视化平台&#xff0c;今天就来给大家介绍国内5个主流的数据可视化平台。 1、阿里云DataV DataV数据可视化是使用可视化应用的方式来分析并展示庞杂数据的产品。…

大数据课程D7——hadoop的YARN

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解YARN的概念和结构&#xff1b; ⚪ 掌握YARN的资源调度流程&#xff1b; ⚪ 了解Hadoop支持的资源调度器&#xff1a;FIFO、Capacity、Fair&#xff1b; ⚪ 掌握YA…

Django实现音乐网站 ⑴

使用Python Django框架制作一个音乐网站。 目录 网站功能模块 安装django 创建项目 创建应用 注册应用 配置数据库 设置数据库配置 设置pymysql库引用 创建数据库 创建数据表 生成表迁移文件 执行表迁移 后台管理 创建管理员账户 启动服务器 登录网站 配置时区…

SpringBoot整合ActiveMQ

ActiveMQ简单使用 JMS ActiveMQ 下载安装 https://activemq.apache.org/components/classic/download/解压缩文件。进入win64目录&#xff0c;双击运行activemq.bat文件&#xff0c;运行服务 将下面的网址输入到浏览器&#xff0c;用户名和密码都是admin SpringBoot整合Act…

flutter 导出iOS问题2

问题1:The Swift pod FirebaseCoreInternal depends upon GoogleUtilities, which does not define modules. To opt into those targets generating module maps (which is necessary to import them from Swift when building as static libraries) 参考 正如上图报错第三方…

Java 源码打包 降低jar大小

这里写目录标题 Idea maven 插件配置pom.xml 配置启动技巧 Idea maven 插件配置 pom.xml 配置 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><!-- 只…

不同局域网下使用Python自带HTTP服务进行文件共享「端口映射」

文章目录 1. 前言2. 视频教程3. 本地文件服务器搭建3.1 python的安装和设置3.2 cpolar的安装和注册 4. 本地文件服务器的发布4.1 Cpolar云端设置4.2 Cpolar本地设置 5. 公网访问测试6. 结语 1. 前言 数据共享作为和连接作为互联网的基础应用&#xff0c;不仅在商业和办公场景有…

基于光子实验的指数级加速的量子同态加密理论

前言 量子计算机不仅有望在某些重要任务上超越经典计算机&#xff0c;而且还能保护计算的隐私。例如&#xff0c;盲量子计算协议支持安全委托量子计算&#xff0c;其中客户端可以保护其数据和算法的隐私&#xff0c;不受分配来运行计算的量子服务器的影响。然而&#xff0c;这…

RNN架构解析——传统RNN模型

目录 传统RNN的内部结构图使用RNN优点和缺点 传统RNN的内部结构图 使用RNN rnnnn.RNN(5,6,1) #第一个参数是输入张量x的维度&#xff0c;第二个是隐藏层维度&#xff0c;第三层是隐藏层的层数 input1torch.randn(1,3,5) #第一个是输入序列的长度&#xff0c;第二个是批次的样本…

前端文件上传实践与后端处理——文件分块上传

文件上传是现代Web应用程序中常见的功能之一。在这篇博客中&#xff0c;我们将探讨一个简单但完整的前端文件上传实践&#xff0c;同时提供一个后端示例&#xff0c;演示如何处理上传的文件。我们将使用JavaScript作为前端语言&#xff0c;并结合Node.js作为后端环境。让我们开…

前端Vue入门-day05-自定义指令、插槽、路由入门

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 自定义指令 基本语法 (全局&局部注册) 全局注册 局部注册 指令的值 v-loading 指令封装 插槽 …

this is incompatible with sql_mode=only_full_group_by

查看配置 select global.sql_mode 在sql命令行中输入select sql_mode 能够看到sql_mode配置,如果有ONLY_FULL_GROUP_BY&#xff0c;则需要修改 在mysql5.7.5后&#xff0c;ONLY_FULL_GROUP_BY是默认选项&#xff0c;所以就会导致group by的问题 set sql_mode‘复制去掉ONLY_F…

游戏引擎UE如何革新影视行业?创意云全面支持UE云渲染

虚幻引擎UE&#xff08;Unreal Engine&#xff09;作为一款“殿堂级”的游戏引擎&#xff0c;占据了全球80%的商用游戏引擎市场&#xff0c;但如果仅仅将其当做游戏开发的工具&#xff0c;显然是低估了它的能力。比如迪士尼出品的电视剧《曼达洛人》、电影《狮子王》等等都使用…

Docker 阿里云容器镜像服务

阿里云-容器镜像服务ACR 将本地/服务器docker image&#xff08;镜像&#xff09;推送到 阿里云容器镜像服务仓库 1. 在容器镜像服务ACR中创建个人实例 2. 进入个人实例 > 命名空间 创建命名空间 3. 进入个人实例 > 镜像仓库 创建镜像仓库 4. 进入镜像仓库 > 基本信…

Vue没有node_modules怎么办

npm install 一下 然后再npm run serve 就可以运行了

PyTorch从零开始实现Transformer

文章目录 自注意力Transformer块编码器解码器块解码器整个Transformer参考来源全部代码&#xff08;可直接运行&#xff09; 自注意力 计算公式 代码实现 class SelfAttention(nn.Module):def __init__(self, embed_size, heads):super(SelfAttention, self).__init__()self.e…

Hive数据仓库

数据仓库概念与起源发展由来 数仓概念 数据仓库&#xff08;英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW&#xff09;&#xff0c;是一个用于存储、分析、报告的数据系统。数据仓库的目的是构建面相分析的集成化数据环境&#xff0c;分析结果为企业提供决策支持…

【组内工作】木马回联

文章目录 C2服务器安装和运行方法CrossC2运行方法sliver运行方法empire安装方法DeimosC2安装教程TrevorC2安装教程&#xff1a; C2服务器的流量特征CrossC21. 心跳包2. 命令3. ja3/ja3s Sliver1. http2. https empirehttphttps DeimosC2https TrevorC2 C2服务器安装和运行方法 …

vue 使用vue-json-viewer 展示 JSON 格式的数据

npm install vue-json-viewer --save<el-button type"primary" click"previewClick">预览</el-button><el-dialog title"预览" :visible.sync"previewVisible" width"70%"><viewer ref"viewer&qu…