runtime 分类结构体_iOS 读懂runtime基础(一)

目录

前言

本文会详细描述Objective-C运行时的各对象数底层据结构、类和原类、消息传递与转发、动态方法等技术方案. 文中底层代码实现均来自Apple open source; 本文篇幅较长, 文中描述加之有个人的一点理解, 主要用作记录和学习之用, 文笔粗陋, 技术菜鸡, 如有错误或不妥之处, 万望各位大佬不吝指教, 不胜感激!

runtime是什么

Objective-C runtime是一个动态运行库, 它给Objective-C语言的动态性提供了支撑. 所有的应用都会链接到该运行时库.

The Objective-C runtime is a runtime library that provides support for the dynamic properties of the Objective-C language, and as such is linked to by all Objective-C apps. Objective-C runtime library support functions are implemented in the shared library found at /usr/lib/libobjc.A.dylib.

三个重要概念

在下述讲述过程中, 你应该注意三个非常重要的概念.即Class、SEL、IMP, 在这里我先把他们列出来, 后面我们会一一的深入讲到其内部结构和之间的关系.

typedef struct objc_class *Class;

typedef struct objc_object {

Class isa;

} *id;

typedef struct objc_selector *SEL;

typedef id (*IMP)(id, SEL, ...);

一 各主要对象数据结构

1 objc_object

objc_object表示实例对象底层是结构体, 内部有一个私有的isa指针, 该指针指向了其类对象

struct objc_object {

private:

isa_t isa;

...

// isa相关操作

// 弱引用, 关联对象, 内存管理等等相关的操作

// 都是在此结构体中, 篇幅太长, 不再全部贴出

}

2 objc_class

objc_class继承自objc_object(所以肯定有isa指针), 表示类对象, 底层仍然是结构体, 其内部的isa指针, 指向了该类的元类对象. 同时, 内部的superclass指向了自身的父类对象, NSObject对象superclass指向了nil, cache是一个方法缓存结构体, bits是存储变量、属性、方法等的结构体

struct objc_class : objc_object {

// Class ISA;

Class superclass;

cache_t cache; // formerly cache pointer and vtable

class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags

class_rw_t *data() {

return bits.data();

}

...

// 类相关的数据操作都是在此结构体中, 不再全部贴出

}

2.1 cache_t

缓存方法, 消息传递时, 会先通过哈希查找算法, 在此数据结构中查询是否有要执行的方法缓存, 如果有则快速执行该方法函数, 这样提高了消息传递的效率;

方法缓存策略, 是局部性原理的最佳应用;

本质是一个可增量的哈希表, 其内部维护了一个由bucket_t组成的结构体列表

struct cache_t {

struct bucket_t *_buckets;

mask_t _mask;

mask_t _occupied;

public:

struct bucket_t *buckets();

...

};

bucket_t内部存储了方法缓存key和无类型函数指针地址的映射关系, 在查找缓存时, 通知指定的key查找到具体的bucket_t, 再从bucket_t中查询到函数IMP地址, 进而去执行函数

struct bucket_t {

private:

cache_key_t _key;

IMP _imp;

public:

inline cache_key_t key() const { return _key; }

inline IMP imp() const { return (IMP)_imp; }

inline void setKey(cache_key_t newKey) { _key = newKey; }

inline void setImp(IMP newImp) { _imp = newImp; }

void set(cache_key_t newKey, IMP newImp);

};

2.2 class_data_bits_t

class_data_bits_t结构主要是对class_rw_r的封装

class_rw_r又是对class_ro_r的封装

struct class_rw_t {

// class_rw_t部分代码

uint32_t flags;

uint32_t version;

// 指向只读的结构体, 存储类初始内容

const class_ro_t *ro;

/*

三个可读写二维数组, 存储了类的初始化信息, 内容

*/

method_array_t methods;// 方法列表

property_array_t properties;// 属性列表

protocol_array_t protocols;// 协议列表

// 第一个子类

Class firstSubclass;

// 下一个同级类

Class nextSiblingClass;

};

class_ro_t结构

struct class_ro_t {

// class_ro_t部分代码

const char * name;

// class_ro_t存储的是类在编译期就确定的方法, 属性, 协议等

method_list_t * baseMethodList;

protocol_list_t * baseProtocols;

const ivar_list_t * ivars;

const uint8_t * weakIvarLayout;

property_list_t *baseProperties;

method_list_t *baseMethods() const {

return baseMethodList;

}

};

需要注意的是, class_ro_t存储的是类在编译期就确定的内容信息, 而class_rw_t不仅包含了类在编译期的内容信息(其实是把class_ro_t的内容合并), 还包含了在运行时动态添加的类内容, 如分类添加的方法, 属性, 协议等内容; 一张图来表示上述结构之间的关系:

3 isa

在arm64为架构之前, isa指针存储了类或元类对象的地址信息, 从arm64架构开始对isa指针(非指针型指针)进行了优化, 用位域存储了除类或元类地址信息以外的其他信息, 如has_assoc表示是否设置关联对象

union isa_t {

isa_t() { }

isa_t(uintptr_t value) : bits(value) { }

Class cls;

uintptr_t bits;

struct {

// 标记位, 0 代表指针型isa, 1代表非指针型isa

uintptr_t indexed : 1;

// 是否有关联对象

uintptr_t has_assoc : 1;

// 是否有C 析构函数

uintptr_t has_cxx_dtor : 1;

// 存储当前对象的类或元类的内存地址

uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000

// 判断对象是否已经初始化

uintptr_t magic : 6;

// 对象是否有弱引用指针

uintptr_t weakly_referenced : 1;

// 当前对象是否有dealloc操作

uintptr_t deallocating : 1;

// 当前isa指针是否有外挂引用表

// 引用计数值大于isa所能存储最大值时

// 就会绑定一个sidetable散列表属性, 来存储更多的引用计数信息

uintptr_t has_sidetable_rc : 1;

// 额外的引用计数值

uintptr_t extra_rc : 19;

};

}

这里需要注意

isa所属对象是实例对象, 则其指向实例对象的类对象

isa所属对象是类对象, 则其指向类对象的元类对象

4 method_t

method_t是函数的底层数据结构, 是对函数的封装, Apple对函数的介绍在这里

struct method_t {

SEL name;// 函数名称

const char *types;// 函数返回值和参数

IMP imp;// 无类型函数指针指向函数体

struct SortBySELAddress :

public std::binary_function

const method_t&, bool> {

bool operator() (const method_t& lhs,

const method_t& rhs)

{ return lhs.name < rhs.name; }

};

};

4.1 函数的四要素

名称

返回值

参数

函数体

4.2 types

Apple使用了Type Encodings技术, 来实现类型编码, Objective-C 运行时库内部利用类型编码帮助加快消息分发.

结构是个列表, 包含了函数的返回值, 参数1, 参数2, 参数3 …, 其中函数的返回值存储在第0个位置, 因为函数只有一个返回值(Go支持多返回值), 而参数可以有多个.

对于一个无类型无参数的函数, 其types值为 “V@:”

- (void)method {

// 其中

// V对应返回值, 代表返回值类型为void

// @对应第一个参数, id类型代表一个对象, 默认第一个参数是对象本身(self), 且该参数是固定的

// :对应SEL, 代表该参数是个方法选择器, 且该参数是默认的第二个固定参数

}

5 一张图表明各数据结构之间的关系

二 实例对象、类对象和元类对象

一大佬(膜拜)画的一张图, 足以说明三者之间的关系.( Apple官网也有类似的描述,但是个人感觉没有下面这张图更精彩)

实例对象的isa指针指向其类对象

类对象的isa指针指向其元类对象

任何元类对象的isa指针都指向根元类对象

类对象的superclass指针指向其父类对象, 根类对象指向nil

元类对象的superclass指针指向其父元类对象, 根元类对象指向根类

其中, 根类在Objective-C中即为NSObject. 实例对象其实就是objc_object(), 类对象就是objc_class(); 上面讲到, objc_class()是继承自objc_object(), 因此类对象中也有isa指针

typedef struct objc_object {

Class isa;

} *id;

从底层数据结构可以看出, 类对象中存储了实例对象方法列表, 成员变量等内容; 同时, 元类对象中存储了类对象的类方法列表等内容;

1 实例方法调用时是如何查找的

当一个实例对象调用一个实例方法时

首先会根据该对象的isa指针, 查到到其类对象, 在类对象方法列表中查询是否有所调用方法的实现;

如果没有, 则类对象会根据自身的superclass指针查找其父类对象, 在父类对象方法列表中查询是否有所调用方法的同名方法实现;

递归调用直至根类对象, 如果中间有任何一步查询到了具体的方法实现, 就去执行具体的函数调用;

如果直至根类, 仍然没有找到方法实现, 则会调用系统两个方法, 然后走系统调用流程;

(BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

(BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

具体的消息传递流程请往下看

2 self和super

self是当前类的因此参数, 指向类的实例对象, 进行方法调用时, 代表从当前类开始进行查找

OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ )

__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

super本质是个编译器标识符, 仅代表方法查找时从当前对象所属父类开始查找方法实现

OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...)

__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

三 消息传递与消息转发

在开发中, 我们经常会碰见这样子一个错误 unrecognized selector sent to instance xx

大致意思是, 你调用了一个并不存在的方法. 现在, 我们会深入的探究一下为什么会出现这个异常.

其实上面这个异常会正好就是我们要讲的, 在Objective-C的消息机制中, 用OC消息机制来说: 如果消息在传递的过程中找不到具体的IMP, 内部就触发了消息转发机制, 而系统的消息转发机制默认实现是抛出上述的异常. 接下来, 我们分别讲述消息的传递和转发.

我们知道Objective-C是动态语言, 方法的调用并不像C的静态绑定一样, 在编译的时候就确定了程序运行时该调用哪个函数(C中没有方法实现会报错), 而是在运行时基于runtime这个动态运行时库通过一系列的查找才决定调用哪个函数, 这样的调用方式更加灵活, 我们甚至可以在运行时动态的修改某个方法的实现, 与当下流行的"热更新"技术有些类似. 而这个查找过程就是Objective-C的消息机制.

1 消息传递流程

在Objective-C中, 方法调用其实就是给某个对象发送消息, 在编译后的文件中我们发现, 底层都转变为函数调用

// 返回值, 参数1: 固定self, 参数2: 固定SEL, 后面是参数3, 参数4....

OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ )

__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

从代码中可以看到, 消息发送函数有两个默认的参数, 第一个是消息接受者receiver, 默认就是当前对象self; 第二个默认参数是SEL, SEL的本质的方法选择器selector; (阅读运行时的文档你会发现, 几乎所有方法调用都和selector有关系)! 所以, 我们的方法调用可以这样表示** [receiver selector] **, 那么这个selector究竟是何方神圣, 遗憾的是我在Apple和GNU提供的runtime代码中,都只找到了这一行代码.

typedef struct objc_selector *SEL;

不过Apple给了说明, 方法选择器selector就是个映射到C中的字符串. 根据我翻阅的各种资料都显示, selector就是个C字符串类型的方法名称.

Method selectors are used to represent the name of a method at runtime. A method selector is a C string that has been registered (or “mapped“) with the Objective-C runtime. Selectors generated by the compiler are automatically mapped by the runtime when the class is loaded.

说了半天, 跟我们的运行时有什么关系(objc_msgSend()是[receiver selector]编译阶段实现的)?那么, objc_msgSend()函数在运行时是如何进一步调用的呢?

首先, 通过 recevier的isa指针寻找到recevier的class(类);

其次, 先在class中的cache list(缓存列表)查找是否有对应的缓存selector;

如果在缓存列表中查找到, 那么就根据selector(key)直接执行方法对应的IMP(value);

否则, 继续在 class的method list(方法列表)中查找对应的 selector;

如果没有找到对应的selector, 就继续在它的 superclass(父类)中寻找;

最后, 如果找到对应的 selector, 直接执行 recever 对应 selector 方法实现的 IMP(方法实现)

否则, 系统进入默认消息转发机制.

我们用一张图来表示上述流程

有时候, 我们会通过super调用, 其实道理是一样的, 编译后会生成objc_msgSendSuper()函数

OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...)

__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

而objc_super结构体内部, 消息接受者仍然是receiver当前实例对象, 与上面不唯一不同的是, self是从当前对象的类对象中开始查找对应实现, 而super则是跨过当前对象的类对象直接从类对象的父类对象开始查找方法实现;

struct objc_super {

/// Specifies an instance of a class.

__unsafe_unretained id receiver;

};

2 消息转发流程

在消息的传递过程中, 我们讲到, 如果receiver 找不到对应的selector的IMP实现, 则会进入系统的默认消息转发流程. 而系统默认处理消息转发的机制就会抛出unrecognized selector sent to instance xx异常, 然后结束整个消息转发. 如果想要避免这种情况的发生, 我们就需要在如果selector找不到的情况下在运行时动态的给receiver添加实现.

幸运的是虽然系统默认默认流程是抛异常, 但是在抛异常的方法调用过程中, 系统给我们开了口子, 让我们可以通过 动态解析、receiver重定向、消息重定向等对消息进行处理, 流程如下图:

2.1 消息动态解析

在系统处理消息转发的过程中, 首先会根据调用对象类型不同分别调用如下两个api, 我们可以通过重载在这两个方法内部动态添加方法, 进而避免crash

// 找不到类方法, 重载此类方法添加类方法实现

(BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 找不到实例方法, 重载此类方法添加实例方法实现

(BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

我们以实例方法为例举个🌰

// 此处实例方法test没有方法实现

(BOOL)resolveInstanceMethod:(SEL)sel {

// 判断是否是test方法

if (sel == @selector(test)) {

NSLog(@"resolveInstanceMethod:");

// 动态添加test方法的实现

class_addMethod(self, @selector(test), testImp, "v@:");

}

return [super resolveInstanceMethod:sel];

}

void testImp (void) {

NSLog(@"test invoke");

}

2.2 消息接受者重定向

如果在resolveInstanceMethod:SEL中没有处理消息(即返回NO), 则系统会给我们第二次机会, 调用forwardingTargetForSelector:SEL! 方法返回值是个id类型, 告诉系统这个实例方法调用转由哪个对象(如果是类方法调用则返回类对象; 如果是实例方法调用, 则返回实例对象)来接受处理, 如果我们指定了新的receiver, 就把消息重新交给新的receiver处理.

同样的, 我们举个🌰

- (id)forwardingTargetForSelector:(SEL)aSelector {

if (aSelector == @selector(test)) {

NSLog(@"forwardingTargetForSelector:");

// 重定向, 让ForwardObj对象作为receiver, 接收处理这个消息

return [[ForwardObj alloc] init];

}

return [super forwardingTargetForSelector:aSelector];

}

2.3 消息重定向

如果系统给我们第二次机会时, 我们返回的对象是nil, 或者self, 那系统会最后一次给我们避免crash的机会, 即消息重定向流程, 调用methodSignatureForSelector方法, 返回值是个方法签名

继续举个🌰

// 定义函数参数和返回值类型, 并返回函数签名

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {

if (aSelector == @selector(test)) {

NSLog(@"methodSignatureForSelector:");

// v代表方法签名的返回值void, @ id类型代表self

// : SEL类型, 代表方法选择器, 其实就是@selector(test)

return [NSMethodSignature signatureWithObjCTypes:"@:"];

}

return [super methodSignatureForSelector:aSelector];

}

// 消息重定向

- (void)forwardInvocation:(NSInvocation *)anInvocation {

NSLog(@"forwardInvocation:");

ForwardObj *obj = [[ForwardObj alloc] init];

if ([obj performSelector:anInvocation.selector]) {

// 如果obj对象可以响应, 则消息转发给obj对象处理

[anInvocation invokeWithTarget: obj];

} else {

// 否则, 抛异常找不到方法对应的实现

[self doesNotRecognizeSelector:anInvocation.selector];

}

}

四 动态方法

1 动态添加方法

我们在消息转发的过程中已经用到了动态添加方法

// 动态添加底层实现

// cls: 为哪个动态添加方法

// name: 要添加的方法名称(方法选择器selector)

// IMP: 无类型函数指针地址

// types: Type Encodings 函数参数和返回值

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) {

if (!cls) return NO;

rwlock_writer_t lock(runtimeLock);

return ! addMethod(cls, name, imp, types ?: "", NO);

}

2 动态方法解析

同样的, 在消息转发过程中, 其实就是对方法的动态解析. 现在我们要讲述另一个方法动态解析的类型, @dynamic

dynamic: 这个词中文意思是动态. 什么动态? 动态运行时的动态, 动态方法的动态, 动态解析的动态, 动态语言的动态!

被@dynamic标记的属性, 在编译时并没有对其getter和setter方法做实现, 而是

动态运行时, 把其实现推迟到了运行时, 即将函数决议推迟到运行时

而静态语言, 是在编译期就进行了函数决议

https://www.icode9.com/content-4-650651.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

weblogic jms消息 删除_消息队列与消息中间件概述:消息中间件核心概念与技术选型...

什么是消息&#xff1f;“消息”是在两台计算机间传送的数据单位。消息可以非常简单&#xff0c;例如只包含文本字符串&#xff1b;也可以更复杂&#xff0c;可能包含嵌入对象。什么是队列&#xff1f;队列(Queue)队列是一种先进先出(FIFO)的数据结构。什么是消息队列&#xff…

mod游戏什么意思计算机,MOD运算

mod运算&#xff0c;即求余运算&#xff0c;是在整数运算中求一个整数 x 除以另一个整数y的余数的运算&#xff0c;且不考虑运算的商。在计算机程序设计中都有MOD运算&#xff0c;其格式为&#xff1a; mod(nExp1,nExp2)&#xff0c;即是两个数值表达式作除法运算后的余数。中文…

伽马分布极大似然估计_一文通俗解释极大似然估计

我们都知道机器学习的大致流程是通过建立一个合理的模型学习现有数据集&#xff0c;然后通过该模型去完成特定的任务。其中每个模型都包含自身的一组特定参数&#xff0c;而这组参数决定着模型的本身。但这里存在一个很关键的一个问题&#xff0c;就是我们如何去找到一组参数使…

html json解析插件,jQuery插件jsonview展示json数据

本文实例为大家分享了jQuery插件jsonview展示json数据的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下项目中要展示json数据&#xff0c;自己写一套html来展示太麻烦&#xff0c;可以使用jquery的插件jsonview来解决这个问题。首先&#xff0c;去jquery官网下载最新…

python3.5安装pygame_python怎么安装pygame

Pygame 是一种流行的 Python 包&#xff0c;用于编写游戏-鼓励学生学习编程&#xff0c;同时创建有趣的东西。 Pygame 在新窗口中显示图形&#xff0c;因此它将 无法在 WSL 的命令行方法下运行。 但是&#xff0c;如果您通过本教程中所述的 Microsoft Store 安装了 Python&…

所属的用户_关于chmod(变更用户对此文件的相关权限)超详细说明,小白秒懂

Linux下一切都是文件,通过ls -l或者别名ll可以查看文件的详细信息:drwxr-xr-x 第一个字符d指的是目录文件;第2-4个字符rwx&#xff1a;指的是u(user,owner)对这个文件具有可读可写可执行的权限;第5-7字符r-x&#xff1a;指的是g(group)对这个文件具有可读可执行权限&#xff1b…

台式计算机计量单位,计算机的计量单位以及常见的数据类型

为什么会写这篇文章&#xff0c;面试官问数据类型占字节大小干什么。实际开发中&#xff0c;你肯定计算预估过一些数据具体要占多少磁盘&#xff0c;或者是内存。如果你没有不去在意这些东西&#xff0c;很多服务器资源&#xff0c;都会被无形的浪费掉。我们知道计算机的世界其…

cad线性标注命令_CAD常用标注快捷键和命令

点击上方 “CAD自学网 ” → 点击右上角“...” → 点选“设为星标 ★ ”为CAD自学网加上星标&#xff0c;即可及时收到干货啦&#xff01;左下角阅读原文看CAD视频站长推荐&#xff1a;1、CAD2014快速精通进阶提高教程&#xff1a;点击查看 2、室内设计全屋定制全套视频教程&…

计算机怎么设置网络共享,局域网共享设置,教您电脑怎么设置局域网共享

前两天&#xff0c;遇到位朋友说他刚买了台新的电脑&#xff0c;加上原来家里原有的两台电脑了&#xff0c;就三台了&#xff0c;现在想要三台电脑都能够进行一个共享职员的这么设置&#xff0c;就是不知道如何在局域网里怎么设置共享&#xff0c;下面&#xff0c;小编就来跟大…

python中的类装饰器应用场景_Python 自定义装饰器使用写法及示例代码

1、Python装饰器简介 python的装饰器就是一个Python函数&#xff0c;它可以让其他函数在不需要做任何代码变动的前提下增加额外功能&#xff0c;装饰器的返回值也是一个函数对象。简单的说装饰器就是一个用来返回函数的函数。 它经常用于有切面需求的场景&#xff0c;比如&…

html引用外部导入式css文件夹,css文件内引用外部资源文件的相对路径

1.default.css文件内容(位于css文件夹下)&#xff1a;.ClassName .ClassName .ClassName.page-sidebar .sidebar-search .submit {--该图片相对于css文件所在的位置。不是使用本css文件的html文件位置。background-image: url(../image/search-icon.png);}2.使用本css文件的htm…

京东五星电器送扫地机器人_家电也流行“套餐”,京东五星电器吹响国庆家装“集结号”...

“70吋超薄激光电视、大容量多门法式冰箱、烘干一体滚筒洗衣机、蒸烤一体机、扫地机器人……”一系列的新房采购清单让宿迁的谭小姐直呼头痛&#xff0c;“挑选品牌和型号&#xff0c;比较价格&#xff0c;还要想着跟装修风格是否搭配&#xff0c;好浪费时间。”在线下实体店迎…

c++设置单元格填充色_格式函数text,设置自定义格式的万金油

在日常的数据处理统计中&#xff0c;我们知道&#xff0c;数据格式标准统一&#xff0c;是很重要的前提&#xff0c;如果数据混乱&#xff0c;那么&#xff0c;在后期的数据处理分析&#xff0c;excel就会耍脾气出错的。所以设置好数据格式&#xff0c;是相当重要的。今天&…

生成html_听说你不会用Python将字符串生成PDF?来,我教你!

这是恋习Python推荐的第118篇好文来源&#xff1a;Python爬虫与算法作者&#xff1a;jclian笔者在今天的工作中&#xff0c;遇到了一个需求&#xff0c;那就是如何将Python字符串生成PDF。比如&#xff0c;需要把Python字符串‘这是测试文件’生成为PDF, 该PDF中含有文字‘这是…

大学计算机基础python第二次作业_python第二次作业-titanic数据集练习

一、读入titanic.xlsx文件&#xff0c;按照教材示例步骤&#xff0c;完成数据清洗。 titanic数据集包含11个特征&#xff0c;分别是&#xff1a; Survived:0代表死亡&#xff0c;1代表存活 Pclass:乘客所持票类&#xff0c;有三种值(1,2,3) Name:乘客姓名 Sex:乘客性别 Age:乘客…

大一计算机上机试题2017,2017历年全国计算机二级ACCESS上机试题及答案

可通过实际操作,锻炼Access的学习能力,动手能力,编程思想锻炼等,也可以作为access 数据库国际二级考试使用呢历年Access操作试题解析(2017.5.8整理&#xff0c;可能为2015年以前的)第一套一、基本操作题(计30分)(1)新建数据库"学生.mdb"&#xff0c;将考生文件夹中的…

怎么做手机的上下滑动_手机视频恢复怎么做?删除时间较久的找回方法

手机视频恢复&#xff1f;话说到手机视频误删的情况对于一些用户来说是常态&#xff0c;手机视频多又杂&#xff0c;经常全选删除&#xff0c;不小心多选了没有发现就给一起删除了&#xff01;事隔多个月以后突然要用到&#xff0c;却怎么也找不到了&#xff0c;最近删除相册也…

大唐发电厂计算机考试题,全国计算机等级考试上机考试与题库解析:一级B

全国计算机等级考试上机考试与题库解析&#xff1a;一级B语音编辑锁定讨论上传视频《全国计算机等级考试上机考试与题库解析一级B》是2011年北京邮电大学出版社出版的图书。本书在研究历年上机真题(库)的基础上&#xff0c;将常考题型提炼出来&#xff0c;对其进行了细致深入的…

opencv利用矩形框选中某一区域_【从零学习OpenCV】4Ubuntu系统中安装OpenCV 4

经过几个月的努力&#xff0c;小白终于完成了市面上第一本OpenCV 4入门书籍《从零学习OpenCV 4》。为了更让小伙伴更早的了解最新版的OpenCV 4&#xff0c;小白与出版社沟通&#xff0c;提前在公众号上连载部分内容&#xff0c;请持续关注小白。Ubuntu系统中安装OpenCV 4前面我…

python中str和int区别_python中eval与int的区别浅析

python中eval和int的区别是什么&#xff1f;下面给大家介绍一下: 1.eval()函数 eval(<字符串>)能够以Python表达式的方式解析并执行字符串&#xff0c;并将返回结果输出。eval()函数将去掉字符串的两个引号&#xff0c;将其解释为一个变量。 作用&#xff1a; a. 处理数字…