【Effective Objective - C 2.0】——读书笔记(二)

文章目录

  • 前言
  • 六、理解“属性”这一概念
  • 七、在对象内部尽量直接访问实例变量
  • 八、理解“对象等同性”这一概念
  • 九、以“类族模式”隐藏实现细节
  • 十、在既有类中使用关联对象存放自定义数据
  • 十一、理解objc_msgSend的作用
  • 十二、理解消息转发机制
    • 动态方法解析
    • 备援接受者
    • 完整的消息转发
    • 消息转发全流程
  • 十四、理解“类对象”的用意


前言

这是一篇为了快手✌️xmy学长写的博客

六、理解“属性”这一概念

“属性”(property)

是OC的一项特性,用于封装对象中的数据。OC对象通常会把其所需要的数据保存为各种实例变量。其中“获取方法”用于读取变量值,而“设置方法”用于写入变量值。切此特性引入了一种新的“点语法”,使开发者可以更为容易地依照类对象来访问存放于其中的数据。

我们在类接口的public区段中声明一些实例变量:

@interface EOCPerson: NSObject {
@public 
	NSString *_firstName;
	NSString *_lastName;
@private
	NSString *_someInternalData;
}
@end

然后我们添加一个实例变量:

@interface EOCPerson: NSObject {
@public 
	NSString *_dateOfBirth;
	NSString *_firstName;
	NSString *_lastName;
@private
	NSString *_someInternalData;
}
@end

我们新添加的实例变量就会代替原第一个位置实例变量的偏移量。
这样的话,如果代码使用了编译器计算出来的偏移量,那么在修改类定义之后必须重新编译,否则就会出错。例如:某个代码库中的代码使用了一份旧的类定义。如果和其相链接的代码使用了新的类定义,那么运行时就会出现不兼容现象。对此类问题,OC的解决方法是,把实例变量当做一种存储偏移量所用的“特殊变量”,交由“类对象”保管。偏移量会在运行期查找,如果类的定义变了,那么存储的偏移量也就变了,这样的话,无论何时访问实例变量,总能使用正确的偏移量。这就是稳固的“应用程序二进制接口”

这里我们的解决方法就是尽力使用存取方法来访问实例变量,这时@property语法就派上用场了。这种规范的命名方法OC会自动创建出存取方法。

简单来说,以下两部分代码的效果是相同的:

@interface EOCPerson: NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
@interface EOCPerson: NSObject
- (NSString *)firstName;
- (void)setFirstName: (NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName: (NSString *)lastName;
- @end

@dynamic关键字

如果我们不想令编译器自动合成存取方法,那我们应该怎么做呢?
那就是使用@dynamic关键字了,它会告诉编译器:不要自动创建实现属性所用的实例变量,也不要为其创建存取方法。然后我们就需要在运行期动态创建存取方法了。

@interface EOCPerson : NSManageObject
@property NSString *firstName;
@end

@implementation
@dynamic firstName;
@end

要点总结

  • 可以通过**@property**语法来定义对象中所封装的数据。
  • 通过“特质”来指定存储数据所需的正确语义。尤其指代属性关键字
  • 在设置属性所对应的实例变量时,一定要遵从该属性所声明的语义。
  • 开发iOS程序时应该使用nonatomic属性,因为atomic属性会严重影响性能,nonatomic的加锁维护了线程安全

七、在对象内部尽量直接访问实例变量

在书中作者说建议大家在读取实例变量的时候采用直接访问的形式,而在设置实例变量的时候采用属性操作

直接访问和属性访问的区别:

  • 由于不经过Objective-C的〝方法派发” (methoddispatch,参见第11条)步骤,所以
    直接访问实例变量的速度当然比较快。 在这种情况 下,编译器所生成的代码会直接访 问保存对象实例变量的那块内存。
  • 直接访问实例变量时,不会调用其〝设置方法”,这就绕过了为相关属性所定义的 “内 存 管 理 语 义 ” 。 比 方 说 , 如 果 在 A
    R C 下 直 接 访 问 一个 声 明 为 c o p y 的 属 性 , 那 么 并 不 会拷贝该属性,只会保留新值并释放旧值。
  • 如果直接访问实例变量,那么不会触发“键值观测"(Key-ValueObserving, Kvo)。通
    知。这样做是否会产生问题,还取决于具体的对象行为。
  • 通过属性来访问有助于排查与之相关的错误,因为可以给 “获取方法〞和/ 或 “设置 方法〞中新增
    “断点”(breakpoint),监控该属性的调用者及其访问时机。

惰性初始化:

也叫做“延迟初始化”。在惰性初始化的情况下,必须通过“获取方法”来访问属性,否则,实例变量就永远不会初始化。
一般用于:一个属性不常用,而且创建该属性的成本较高的情况。

- (EOCBrain *) brain {
	if (!_brain) {
		_brain = [Brain new];
	}
	return _brain;
}

在这种情况下我们就必须使用存取方法来访问我们的属性

要点

  • 在对象内部读取数据时,应该直接通过实例变量来读,而写人数据时,则应通过属性 来写。
  • 在初始化方法及dealloe 方法中,总是应该直接通过实例变量来读写数据。
  • 有时会使用惰性初始化技术配置某份数据,这种情况下,需要通过属性来读取数据。

八、理解“对象等同性”这一概念

“==”和“isEqual:”区别

我们先来回顾一下之前学过的“==”和“isEqual:”区别:

====是看地址(指针)==来进行判断,地址不一致即返回false
isEqual:是专门用于判断的方法,不一定是看地址,也可以是其他的标准。

在NSObject类中,==与isEqual:没有明显区别,但在NSString中,已经完成了重写,只要字符串字符序列相同,isEqual:方法就返回true。

以如下代码为例:

NSString *foo = @"Badger 123";
NSString *bar = [NSString stringWithFormat:@"Badger %i", 123];
BOOL equalA = (foo == bar);//NO
BOOL equalB = [foo isEqual:bar];//YES
BoOL equalC = [foo isEqualToString:bar];//YES

可以看到== 与等同性判断方法之间的差别。NSString类实现了一个自己独有的等 同性判断方法,名叫 “isEqualToString: ” 。传递给该方法的对象必领是NSString,否则结果 末定义(u deined)。调用该方法比调用“ isEqual:〞方法快,因为“ isEqual:〞方法还要执行额外的步骤,因为isEqual不知道受测对象的类型。

NSObject 协议中有两个用于判断等同性的关键方法:

 - (BOOL) isEqual: (id) object;
 - (NSUInteger) hash;

NSObject 类 对 这 两 个 方 法 的 默 认 实 现 是 : 当 且 仅 当 其 “ 指 针 值 ”
(pointervalue)日 完 全 相 等时,这两个对象才相等。若想在自定义的对象中正确後写这些方法,就必领先理解其约定
(contract)。如果“ isEqual:〞方法判定两个对象相等,那么其hash 方法也必须返回同 一个
值。但是,如果两个对象的hash 方法返回同一个值,那么“ isEqual:〞方法末必会认为两者 相 等 。

容器中可变类的等同性

当把某个对象放入collection后,就不应该再改变其哈希码了。因为collection会把各个对象按照其哈希码分装到不同的“箱子数组”中。如果某对象在放入“箱子”之后,哈希码又发生变化,那么其所处的这个箱子对他来说就是“错误”的。

要点

  • 若想检测对象的等同性,请提供“isEqual:”与hash方法。
  • 相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同。
  • 不要盲目地逐个检测每条属性,而是应该依照具体需求来指定检测方案。
  • 编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。

九、以“类族模式”隐藏实现细节

类方法

“类族”是一种很有用的模式,可以隐藏“抽象基类”背后的实现细节。Objective-C的系统框架中普遍使用此模式。比奶,ios 的用户 界面框架(user interface framework)UIKit中就有一个名为UIButton的类。想创建按钮,需 要 调 用 下面 这 个 “ 类方法” ( class method )

+ (UIButton*)buttonWithType:(UIButtonType)type;

该方法所返回的对象,其类型取决于传人的按钮类型(button type)。然而,不管返回什 么类型的对象,它们都继承自同 一个基类:UIButton。这么做的意义在于:UIButton 类的使 用者无领关心创建出来的按钮具体属于哪个 子类,也不用考虑按钮的绘制方式等实现细节。 使用者只需明白如何创建按钮,如何设置像“标题” (title)这样的属性,如何增加触摸动作 的目标对象等问题就好。

创建类族:

首先要定义抽象基类,也就是一个新的类,其中可以包括你的类型选取,使用枚举器和switch语句来完成,并且还的定义你的类的相关方法,再创建一个新的类,继承你之前的类,并且完成之前的定义方法,使用覆盖的原理,完成这些方法。这种“工厂模式”是创建类族的办法之一。
如果你想创建的类中没有init初始化的方法,那么这就是在暗示你该类的实例也许不应该由用户直接创建。总而言之,以后创建对象一定不要被其的表象迷惑住了,你可能觉得自己创建了某个类的实例,然而实际上创建的却是其子类的实例。

//定义员工类型
typedef NS_ENUM(NSUInteger, EOCEmployeeType) {
    EOCEmployeeTypeDeveloper,
    EOCEmployeeTypeDesiner,
    EOCEmployeeTypeFinance
};

@interface EOCEmployee : NSObject
//定义属性
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger salary;
//定义方法
+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type;

- (void)doADaysWork;

@end
@implementation EOCEmployee

+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type {
    switch (type) {
        case EOCEmployeeTypeDeveloper:
            return [EOCEmployeeTypeDeveloper new];
            break;
            
        case EOCEmployeeTypeDesiner:
            return [EOCEmployeeTypeDesiner new];
            break;
            
        case EOCEmployeeTypeFinance:
            return [EOCEmployeeTypeFinance new];
            break;
    }
}

- (void)doADaysWork {
    // Subclasses implement this.
}

@end
@interface EOCEmployeeTypeDeveloper : EOCEmployee
@end

@implementation  EOCEmployeeTypeDeveloper

- (void)doADaysWork {
	[self writeCode];
}

@end

Cocoa里的类族

系统框架中有许多类族,就用我们经常使用的NSArray和NSMutableArray来说,这样来看,它是两个抽象基类,但是他们两个拥有相同的方法,这个方法可能就是他们共同类族中的方法,而可变数组的特殊方法就是只适用于可变数组的方法,其他的共同方法可能就是类族中的方法。

在使用NSArray的alloc方法来获取实例时,该方法首先会分配一个属于某个类的实例,此实例充当一个“占位数组”,也就是说,你把这个位置是先分配给其类族的,后来其类族才将这个位置分配给你创建的具体数据类型的。
所以像这些类的背后其实是一个类族,在对一些if条件进行判断的时候一定要注意,例如:

id maybeAnArray = /* ... */;
if ([maybeAnArray class] == [NSArray class]) {
	//Will never be hit
}

使用这种方法来判断两个类是否属于同一类族很明显是错的

因为NSArray是一个类族,NSArray初始化返回的对象并非NSArray类,而是隐藏在类族公共接口中的某个内部类型

就像这段代码:
在这里插入图片描述

不过OC仍旧提供了判断实例所属的类 是否位于类族之中 isKindOfClass

id maybeAnArray = /*...*/
if(maybeAnArray isKindOfClass:[NSArray class]) {
	//will be hit
}

使用这种方法即可判断是否属于同一类族

手动增加实体子类的规则

  • 子类应该继承自类族中的抽象基类。
  • 子类应该定义自己的数据存储方式。
  • 子类应当覆写超类文档中指明需要覆写的方法。

要点总结

  • 类族模式可以把实现细节隐藏在一套简单的公共接口后面。
  • 系统框架中经常使用类族。(可变和不可变是最直接的例子)
  • 从类族的公共抽象基类中继承子类时要当心,若有开发文档,则应首先阅读。

十、在既有类中使用关联对象存放自定义数据

关联对象的出现

在iOS开发里,分类是不能添加成员变量的,只允许给分类添加属性,所以出现了关联对象

具体使用后面应该会学到,这里直接进行要点总结。

要点总结

  • 可以通过“关联对象”机制来把两个对象连起来。
  • 定义关联对象时可指定内存管理语义,用以模仿定义属性时所采用的“拥有关系”与“非拥有关系”。
  • 只有在其他做法不可行时才应选用关联对象,因为这种做法通常会引入难于查找的bug

十一、理解objc_msgSend的作用

对于C语言来说,静态绑定意味着在编译期就能决定运行时所需要调用的函数

以以下代码为例子:
在这里插入图片描述

编译器在编译代码的时候就已经知道程序中有 printflhelloprintGodbye这两个函数了,于是会直接生成调用这些函数的指令。而函数地址实际上是硬编码在指令之中

若是对于这段代码:
在这里插入图片描述
这时就得使用“动态绑定” (dynamicbinding)了,因为所要调用的函数直到运行期才能 确定。编译器在这种情况下生成的指令与刚才那个例子不同,在第一个例子中,其与else 语 句里都有函数调用指令。而在第二个例子中,只有 一个函数调用指令,不过待调用的函数地址无法硬编码在指令之中,而是要在运行期读取出来。

在OC中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有的方法都是普通的C语言函数,然而对象收到消息之后究竟该调用那个方法则完全取决于运行期决定,甚至可以在程序运行时改变,这些特性使得OC成为一门真正的动态语言。

消息转发过程

来看下面一个对象发送消息:

id returnValue = [someObject messageName:parameter]

在这个例子中

someObject叫做“接收者”(receiver),messageName叫做 “选择子”(selector)。 选择子与参数合起来称为 “消息” (message)。编译器看到此消息后,将其转换为一条标准的 C语言两数调用,所调用的函数乃是消息传递机制中的核心两数,叫做objc_msgSend,其 “ 原型” ( prototype )如下:

void objc_msgsend(id self, SEL cmd, ...)

这是个“ 参数个数可变的两数” ( variadic function)。能接受两个或两个以上的参数。第一 个参数代表接收者,第二个参数代表选择子(SEL 是选择子的类型),后续参数就是消息中的 那些参数,其顺序不变。选择子指的就是方法的名字。“选择子〞与“方法” 这两个词经常 交替使用。编译器会把刚才那个例 子中的消息转换为如下函数:

id returnValue = objc msgSend(someobject, @selector (messageName:), parameter);

边界情况:
在这里插入图片描述

要点

  • 消息由接收者、选择子及参数构成。给某对象“发送消息”也就相当于在该对象上“调用方法”。
  • 发给某对象的全部消息都要由“动态消息派发系统”来处理,该系统会查出对应的方法,并执行其代码。

十二、理解消息转发机制

上一条强调了消息是如何传递下去的,这一条深入理解一下在某些出现问题的时刻系统是如何解决问题的。

消息转发

  • 某个对象收到了无法解读的消息之后会发生什么情况?这就是OC的消息转发机制。
  • 对于在编译期向类发出的无法解读的消息之后不会报错,因为可以在运行期继续向类里面添加方法,所以在编译时期出现了对象无法解读的消息就会启动消息转转发机制。
  • 消息转发分为两大阶段,第一阶段先征询接收者,所属的类,看其是否能动态添加方法,处理当前这个“未知的选择子”,这叫做“动态方法解析”。第二阶段涉及“完整的消息转发机制”。

动态方法解析

对象在收到无法解读的消息后,首先将调用其所属类的下列类方法:

+ (BOOL)resolveInstanceMethod:(SEL)selector

该方法的参数就是那个未知的选择子,其返回值为Boolean类型,表示这个类是否能新增一个实例方法用以处理此选择子。在继续往下执行转发机制之前,本类有机会新增一个处理此选择子的方法,假如尚未实现的方法不是实例方法而是类方法,那么运行期系统就会调用另一个方法,该方法与“resolveInstanceMethod:”类似,叫做 “resolveClassMethod”

动态方法解析的前提

对于上述的消息转发第一步,前提是我们相关的实现代码已经写好了,只需要等着运行时的时候插入类里面即可

以以下代码举例子:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface MyClass : NSObject
@end

@implementation MyClass

// 动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(dynamicMethod)) {
        class_addMethod([self class], sel, (IMP)dynamicMethodImplementation, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void dynamicMethodImplementation(id self, SEL _cmd) {
    NSLog(@"Dynamic method has been resolved and called.");
}

@end

int main() {
    @autoreleasepool {
        MyClass *obj = [MyClass new];
        
        // 调用未实现的方法,触发动态方法解析
        [obj dynamicMethod];
    }
    return 0;
}

备援接受者

在第一步还是没有找到写好的方法之后,当前接受者还有第二次机会处理未知的选择子,在这一步里运行期的系统会询问接受者能不能找到其他接受者处理该消息,这里也有一个方法:

- (id)forwardingTargetForSelector:(SEL)selector;

方法参数代表未知的选择子,若当前接收者能找到各授对象,则将其返回,若找不到, 就 返 回 nil。 通过此方案,我们可以用“组合”(composition) 来模拟出“多重继承”( multipleinheritance )的某些特性。在一个对象内部,可能还有一系列其他对象,该对象可经由此方法 将能够处理某选择 子的相关内部对象返回,这样的话,在外界看来,好像是该对象亲自处理了这些消息似的。
请注意,我们无法操作经由这 一步所转发的消息。若是想在发送给备援接收者之前先修改消息内容,那就得通过完整的消息转发机制来做了。

例子:

#import <Foundation/Foundation.h>

// 定义备用接收者对象
@interface AnotherObject : NSObject
- (void)anotherMethod;
@end

@implementation AnotherObject

- (void)anotherMethod {
    NSLog(@"Method implemented in AnotherObject.");
}

@end

// 主要对象
@interface MyClass : NSObject
@end

@implementation MyClass

// 备用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(anotherMethod)) {
        return [AnotherObject new];
    }
    return [super forwardingTargetForSelector:aSelector];
}

@end

int main() {
    @autoreleasepool {
        MyClass *obj = [MyClass new];
        
        // 调用未实现的方法,触发备用接收者
        [obj performSelector:@selector(anotherMethod)];
    }
    return 0;
}

在这个方法中,当选择器为 @selector(anotherMethod) 时,返回了 AnotherObject 的实例。因此,当你在 MyClass 对象上调用 anotherMethod 时,实际上是调用了 AnotherObject 的实例上的方法。

完整的消息转发

如果转发算法已经来到这一步的话,那么唯一能做的就是启用完整的消息转发机制 了。首先创建NSInvocation 对象,把与尚未处理的那条消息有关的全部细节都封于其中。 此对象包含选择子、目标(target )及参数。在触发NSInvocation 对象时,“ 消息派发系统”( message-dispatchsystem )将亲自出马,把消息指派给目标对象。

此步骤会调用 下列方法来转发消息:

 - (void)forwardInvocation:(NSInvocation*)invocation
  • 这个方法的实现方式有2种,一种是只需要改变调用目标,和备援接受者方法实现的等效,第二种则是在触发消息前,先以某种方式改变消息内容,比如追加另一个参数,或者改变选择子等
  • 实现此方法时,若发现某调用操作不应由本类来处理,则需调用超类的同名方法。这样的话,继承体系中的每个类都有机会处理此调用请求,直至NSObject。如果最后调用NSObject类的方法,那么该方法还会继而调用“doesNotRecognizeSelector:”以抛出异常,此异常表明选择子最终未能得到处理。

消息转发全流程

在这里插入图片描述

要点

  • 若对象无法响应某个选择子,则进人消息转发流程。
  • 通过运行期的动态方法解析功能,我们可以在需要用到某个方法时再将其加人类中。
  • 对象可以把其无法解读的某些选择子转交给其他对象来处理。
  • 经过上述两步之后,如果还是没办法处理选择 子,那就启动完整的消息转发机制。

十四、理解“类对象”的用意

1.id类型:

一般情况下,应该指明消息接收者的具体类型,这样的话,如果向其发送了无法解读的消息,那么编译器就会产生警告信息。而类型为id的对象则不然,编译器假定它能响应所有信息。
每个OC对象实例都是指向某块内存数据的指针,所以在声明变量时,类型后面要跟一个*字符。

描述Objective-C对象所用的数据结构定义在运行期程序库的头文件里,id类型本身也在定义在这里:

typedef struct objc_object {
    Class isa;
} *id;

2.Class对象:
在这里插入图片描述

typedef struct objc_class *Class;
struct objc_class {
    Class isa;
    Class super_class;
    const char *name;
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;
    struct objc_method_list **methodLists;
    struct objc_cache *cache;
    struct objc_protocol_list *protocols;
};

此结构体存放类的“元数据”。其中的super_class,它定义了本类的父类。类对象所属的类型(也就是isa指针所指向的类型)是另一个类,叫做“元类”(metaclass)。并且每个类仅有一个“类对象”,而每个“类对象”仅有一个与之相关的“元类”。

这里注意isa是元类,而super_class是父类在这里插入图片描述

而对于isMemberOfClassisKindOfClass在此博客【iOS】类、元类、父类的关系已经讲述,不再赘述

要点:

  • 每个实例都有一个指向Class对象的指针,用以表明其类型,而这些Class对象则构成了类的继承体系。

  • 如果对象类型无法在编译期确定,那么就应该使用类型信息查询方法来探知。

  • 尽量使用类型信息查询方法来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/383437.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PE 特征码定位修改程序清单 uiAccess

requestedExecutionLevel level"asInvoker" uiAccess"false" 可以修改这一行来启用禁用原程序的盾牌图标&#xff0c;似乎作用不大。以前没事写的一个小玩意&#xff0c;记录一下。 等同于这里的设置&#xff1a; 截图 代码如下&#xff1a; #include …

mac卸载被锁定的app

sudo chflags -hv noschg /Applications/YunShu.app 参考&#xff1a;卸载云枢&#xff08;MacOS 版&#xff09;

Java 学习和实践笔记(6)

各数据类型所占的空间&#xff1a; byte: 1个字节 short&#xff1a;2个字节 int&#xff1a;4个 long&#xff1a;8个 float&#xff1a;4个 double: 8个 char:1个 boolean:1bit 所有引用数据类型都是4个字节&#xff0c;实际其值是指向该数据类型的地址。 上图中稍特…

【iOS】——使用ZXingObjC库实现条形码识别并请求信息

文章目录 前言一、实现步骤二、扫描界面和扫描框的样式1.扫描界面2.扫描框 三、实现步骤 前言 ZXing库是一个专门用来解析多种二维码和条形码&#xff08;包括包括 QR Code、Aztec Code、UPC、EAN、Code 39、Code 128等&#xff09;的开源性质的处理库&#xff0c;而ZingObjC库…

单片机学习笔记---AT24C02(I2C总线)

目录 有关储存器的介绍 存储器的简介 存储器简化模型 AT24C02介绍 AT24C02引脚及应用电路 I2C总线介绍 I2C电路规范 开漏输出模式和弱上拉模式 其中一个设备的内部结构 I2C通信是怎么实现的 I2C时序结构 起始条件和终止条件 发送一个字节 接收一个字节 发送应答…

Failed to construct ‘RTCIceCandidate‘ sdpMid and sdpMLineIndex are both null

最近在搞webrtc&#xff0c;在编写函数处理远端传递来的candidate时报错了&#xff0c;具体信息如下。国内关于webrtc的资料很少&#xff0c;所以去国外社区转了一圈&#xff0c;回来记录一下报错的解决方案 其实这个bug也好解决&#xff0c;根据报错信息可以判断是RTCIceCand…

【数据库】Unlogged 表使用

【数据库】Unlogged 表使用 前言普通表和Unlogged 表的写性能比较普通表创建和数据插入Unlogged 表创建和数据插入比较结果 Unlogged 表崩溃和正常关闭测试Unlogged 表特点总结 前言 大神偶像在开会上提及了Unlogged 表&#xff0c;它的特点很不错&#xff0c;很适合实时数据保…

图(高阶数据结构)

目录 一、图的基本概念 二、图的存储结构 2.1 邻接矩阵 2.2 邻接表 三、图的遍历 3.1 广度优先遍历 3.2 深度优先遍历 四、最小生成树 4.1 Kruskal算法 4.2 Prim算法 五、最短路径 5.1 单源最短路径-Dijkstra算法 5.2 单源最短路径-Bellman-Ford算法 5.3 多源最…

代码随想录算法训练营第四十九天(动态规划篇之01背包)| 474. 一和零, 完全背包理论基础

474. 一和零 题目链接&#xff1a;https://leetcode.cn/problems/ones-and-zeroes/submissions/501607337/ 思路 之前的背包问题中&#xff0c;我们对背包的限制是容量&#xff0c;即每个背包装的物品的重量和不超过给定容量&#xff0c;这道题的限制是0和1的个数&#xff0…

C语言学习记录

小飞机_牛客题霸_牛客网 (nowcoder.com) 飞机翅膀12个*&#xff0c;第一行按5下空格&#xff0c;再按两下*&#xff0c;再按5下空格&#xff0c;最后一行按4下空格&#xff0c;再按一下*&#xff0c;再按两下空格&#xff0c;再按一下*&#xff0c;再按4下空格 数格子就完了&a…

优秀!护理学者用CLHLS数据库发表二区文章 IF=6.6

编者 近日&#xff0c;我们关注到一篇发表在《Journal of Affective Disorders》&#xff08;二区&#xff0c;IF6.6&#xff09;的精彩文章。研究者们利用潜在剖面分析方法&#xff0c;利用中国老年健康影响因素跟踪调查数据&#xff08;CLHLS&#xff09;&#xff0c;深入研究…

项目02《游戏-14-开发》Unity3D

基于 项目02《游戏-13-开发》Unity3D &#xff0c; 任务&#xff1a;战斗系统之击败怪物与怪物UI血条信息 using UnityEngine; public abstract class Living : MonoBehaviour{ protected float hp; protected float attack; protected float define; …

C++入门(上)

文章目录 1:什么是C2.C的发展史3:C关键字(C98)4:命名空间4.1:命名空间的概念4.2:命名空间的定义4.3:命名空间的使用4.3.1加命名空间的名称以及域作用限定符4.3.2:使用using将命名空间中某个成员引入4.3.3:使用using namespace 命名空间名称展开命名空间代码1代码2 5:C输入与输出…

React Native开发iOS实战录

文章目录 背景环境准备主要工具xcode安装安装CocoaPods 基本步骤常见问题ruby3在macOS上编译失败import of module ‘glog.glog.log_severity’ appears within namespace ‘google’yarn网络问题pod安装失败unable to open settings file 相关链接 背景 准备将之前的一个Reac…

【复现】大华 DSS SQL 注入漏洞_46

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 大华DSS是大华的大型监控管理应用平台&#xff0c;支持几乎所有涉及监控等方面的操作&#xff0c;支持多级跨平台联网等操作。 可…

python+django咖啡网上商城网站

全网站共设计首页、咖啡文化、咖啡商城、个人信息、联系我们5个栏目以及登录、注册界面&#xff0c;让用户能够全面的了解中国咖啡咖啡文化宣传网站以及一些咖啡知识、文化。 栏目一首页&#xff0c;主要放置咖啡的起源及发展进程的图文介绍&#xff1b;栏目二咖啡文化&#xf…

BKP寄存器与RTC实时时钟

BKP寄存器 BKP寄存器简介 BKP&#xff08;Backup Registers&#xff09;备份寄存器 BKP可用于存储用户应用程序数据。当VDD&#xff08;2.03.6V&#xff09;电源被切断&#xff0c;他们仍然由VBAT&#xff08;1.83.6V&#xff09;维持供电。当系统在待机模式下被唤醒&#xf…

python高校实验室管理系统的django设计与实现81txp

技术栈 后端&#xff1a;python 前端&#xff1a;vue.jselementui 框架&#xff1a;django Python版本&#xff1a;python3.7 数据库&#xff1a;mysql5.7 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm .本高校实验室管理系统采用python语言、MySQL数据库&…

每日五道java面试题之java基础篇(六)

第一题&#xff1a;Java 创建对象有哪⼏种⽅式&#xff1f; Java 中有以下四种创建对象的⽅式: new 创建新对象通过反射机制采⽤ clone 机制通过序列化机制 前两者都需要显式地调⽤构造⽅法。对于 clone 机制,需要注意浅拷⻉和深拷⻉的区别&#xff0c;对于序列化机制需要明…

Java 集合、迭代器

Java 集合框架主要包括两种类型的容器&#xff0c;一种是集合&#xff08;Collection&#xff09;&#xff0c;存储一个元素集合&#xff0c;另一种是图&#xff08;Map&#xff09;&#xff0c;存储键/值对映射。Collection 接口又有 3 种子类型&#xff0c;List、Set 和 Queu…