iOS底层-类的三顾茅庐(二)

2023-05-16

前言

上篇文章分析了objc_class里存储数据的bits,了解到方法和属性的存储的位置class_rw_t(以下简称rw)。本文将继续研究rw里包含的其他内容。

类数据的存储

书接上文,rw结构体,找到一个class_ro_t的结构体(以下简称ro)。

image-20220427150636259

代码验证:

// 声明
NS_ASSUME_NONNULL_BEGIN

@interface FFPhone : NSObject

@property (nonatomic, copy) NSString * name;

+ (void)phoneTest;

@end

NS_ASSUME_NONNULL_END
  
// 实现
@implementation FFPhone
{
    NSString * _privateProperty;
}

-(instancetype)init {
    if (self = [super init]) {
        self.name = @"init iPhone";
    }
    return self;
}

+ (void)phoneTest {
    NSLog(@"phoneTest");
}

@end

// 测试
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 掩码:0x00007ffffffffff8ULL
        FFPhone *p = [FFPhone alloc];
        
        NSLog(@"...");
    }
    return 0;
}

LLDB指令:

// (lldb) x/6gx p.class
// (lldb) p/x (class_data_bits_t *)0x100008200
// (lldb) p *$1
// (lldb) p $2.data()
// (lldb) p *$3
// (lldb) p $4.ro()

是能获取到到ro

image-20220427152403726

既然成员变量不在rw中,会不会在ro里?

成员变量

在iOS中,ivar表示成员变量,而ro结构体里正好有个ivars:

// (lldb) p *$5
// (lldb) p $6.ivars

如图:

image-20220427152518790

得到结构体ivar_list_t类型的指针

image-20220427152855264

也是继承自模板类entsize_list_tt

struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {
    bool containsIvar(Ivar ivar) const {
        return (ivar >= (Ivar)&*begin()  &&  ivar < (Ivar)&*end());
    }
};

通过get方法拿到第一个元素,正是私有的成员变量_privateProperty,说明属性生成的成员变量才会放在class_rw_t的属性列表properties()里,而单独的成员变量只会放在ro结构体里,因为成员变量无法从外部修改,看成是只读的

image-20220427153250225

越界也会报错:

image-20220427153340127

所以属性生成的成员变量_name也在ro生成了一份。

offset对应machO文件中的内存偏移量

可以确定成员对象放在类对象里,为什么它们的值放在实例对象里?

类的本质是结构体,相当于一个模板;类里有什么属性、方法等放在模板里就好了。而实例对象是根据这个模板生成的,每个对象的值可能不一样,当然只需要存放值。

rw和ro区别

rorw有什么区别?苹果的WWDC大会有个视频Advancements in the Objective-C runtime里解释过。ro放在纯净的内存空间(clean memory),是只读的。rw在运行生成,存放在可以读写的内存空间中(dirty memory),一经使用,ro就会成为rw的一部分(通过指针引用)。

runtime既然可以动态添加属性、协议等。而ro又不允许修改,怎么办?

拷贝一份再进行修改!这样存在了2份ro数据,岂不是内存浪费?

苹果解决的方式也在视频提到,对于没有使用到的ro,可以进行移除,需要时再分配。所以rw中可以只存储一部分信息,并且rw对于真正需要修改的内容,还会拆分出class_rw_ext_t;以下简称rwe

struct class_rw_ext_t {
    DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
    class_ro_t_authed_ptr<const class_ro_t> ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    char *demangledName;
    uint32_t version;
};

// 不包括方法
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;
}

// 不包括方法
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    WrappedPtr<method_list_t, method_list_t::Ptrauth> baseMethods;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
}

视频里提到,在苹果的测试中,实际只有大约不到10%的类真正更改了它们。

ro、rw、rwe的关系

初始时,只有ro。并且Swift和objc共享这一基础结构。

image-20220506113200422

当需要修改类信息的时候,rw引用ro,并拷贝一部分信息。

image-20220506113212980

将需要动态更新的部分提取出来,存⼊rwe:

image-20220506113330669

最终链接成这样:

image-20220506113406159

当前demo里没有rwe,因为没有进行动态修改(分类、runtime的api);除此以外,触发条件还包括:分类和本类都不是懒加载的类(以后单独讲分类)。

rwe不会将成员变量剪切过去,因为无法修改。

例如获取方法列表会判断rwe: 没有才从ro获取

image-20220427163416569

rwe作为属性跟着类一起释放;

根据链接图可以看到,方法列表放在robaseMethods数组里,之后拷贝到了rwe

image-20220506113559783

总结:程序加载时方法存在ro。当类第一次使用的时候,rw就会引用ro;如果动态修改,就会从ro拷贝到rwe;修改的时候也是去操作rwe

元类设计的初衷

复用消息机制。类调用方法,实际上就是发送一条信息。系统通过objc_msgSend()找到实现。

这个函数id objc_msgSend(id self, SEL op, ...) 有2个参数:消息的接收者self,消息的方法名op

第一个参数消息的接收者的isa指针,找到对应的类,如果我们是通过实例对象调用方法,那么这个isa指针就会找到实例对象的类对象,如果是类对象,就会找到类对象的元类对象,然后再通过SEL类型的方法名找到对应的imp,就能找到方法对应的实现。

由于在oc中类方法和实例方法可以同名,通过消息接收者的isa指针来查找。类方法通过类对象的isa找到元类对象,实例方法通过实例对象的isa找到类对象。

如果没有元类的话,那这个objc_msgSend方法还得多加俩个参数,一个参数用来判断这个方法到底是类方法还是实例方法。一个参数用来判断消息的接受者到底是类对象还是实例对象。在方法内部就会有有很多的判断,影响发送效率。消息的发送,总是越快越好。

所以还是得用isa。根据单一职责,元类对象存储类方法,类对象存储实例方法。并且在不同种类的方法走的都是同一套流程,在之后的维护上也大大节约了成本。所以这个元类的出现,最大的好处就是能够复用消息传递这套机制

结合之前的探究的实例方法和类方法,说明在objc底层没有区别,都是函数,通过消息机制调用。只不过存放位置的不同,类方法存储在元类对象里,实例方法存储在类对象里。

runtime的api尝试

既然说到动态修改方法,runtime提供了很多api,这里就随便试试。

成员变量列表

通过class_copyIvarList获取成员变量列表:

// Class的成员变量
void ff_class_copyIvarList(Class ffClass) {
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(ffClass, &count);
    for (int i = 0; i < count; i ++) {
        Ivar ivar = ivars[i];
        const char *name =  ivar_getName(ivar);
        const char *type = ivar_getTypeEncoding(ivar);
        NSLog(@"ivar name = %s; ivar type = %s", name, type);
    }
    free(ivars);
}

这里为什么要通过free(ivars);手动释放变量?查看方法的源码可以看到:


/***********************************************************************
* class_copyIvarList
* fixme
* Locking: read-locks runtimeLock
**********************************************************************/
Ivar *
class_copyIvarList(Class cls, unsigned int *outCount)
{
    const ivar_list_t *ivars;
    Ivar *result = nil;
    unsigned int count = 0;

    if (!cls) {
        if (outCount) *outCount = 0;
        return nil;
    }

    mutex_locker_t lock(runtimeLock);

    ASSERT(cls->isRealized());
    
    if ((ivars = cls->data()->ro()->ivars)  &&  ivars->count) {
        // 开辟了内存,需要手动释放。
        result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));
        
        for (auto& ivar : *ivars) {
            if (!ivar.offset) continue;  // anonymous bitfield
            result[count++] = &ivar;
        }
        result[count] = nil;
    }
    
    if (outCount) *outCount = count;
    return result;
}

通过malloc开辟了内存,由于ARC只会管理OC对象,所以需要手动释放。运行结果:

image-20220428113206260

type这里对应编码类型简称:i = int,c = char, d = double, s = short;

属性列表

通过class_copyPropertyList获取属性列表:

// Class的属性
void ff_class_copyPropertyList(Class ffClass) {
    unsigned int count = 0;
    objc_property_t *perperties = class_copyPropertyList(ffClass, &count);
    for (int i = 0; i < count; i++) {
        objc_property_t property = perperties[i];
        const char *name = property_getName(property);
        const char *type = property_getAttributes(property);
        NSLog(@"property name = %s; property type = %s", name, type);
    }
    free(perperties);
}

运行结果:

image-20220428113423263

这里编码怎么理解呢,以T@"NSString",C,N,V_goodsName为例:

  • T代表类型,后面接类型名称@"NSString";
  • C代表属性的Copy关键字,是复制的;
  • N代表nonatomic,该属性是原子性的;
  • V_goodsName代表属性生成的带下划线的成员变量_goodsName

完整编码见扩展

方法列表

通过class_copyMethodList获取方法列表:

// Class的Method
void ff_class_copyMethodList(Class ffClass) {
    unsigned int count = 0;
    Method *methods = class_copyMethodList(ffClass, &count);
    for (int i = 0; i < count; i++) {
        Method method = methods[i];
        NSString *name = NSStringFromSelector(method_getName(method));
        const char *type = method_getTypeEncoding(method);
        NSLog(@"method name = %@; method type = %s",name,type);
    }
    free(methods);
}
// 方法名称是SEL类型
SEL method_getName(Method mSigned)
{
    if (!mSigned) return nil;

    method_t *m = _method_auth(mSigned);
    ASSERT(m->name() == sel_registerName(sel_getName(m->name())));
    return m->name();
}

获取到的是Method 名称是SEL类型,所以要打印的话还需要解析:NSStringFromSelector

image-20220428152111953

方法编码的含义又是什么?举个栗子:v20@0:8i16

  • @代表对象类型,@0从第0个字节开始存放;
  • :代表方法SEL类型,:8表示从第8个字节开始存放;
  • i代表int类型;i16表示从第16字节开始存放;
  • v代表void无返回值类型,v20表示方法总共占20字节;

v20@0:8i16正好对应这三种类型,加起来占20字节。

其他编码含义:

//编码值 含意
//c 代表char类型
//i 代表int类型
//s 代表short类型
//l 代表long类型,在64位处理器上也是按照32位处理
//q 代表long long类型
//C 代表unsigned char类型
//I 代表unsigned int类型
//S 代表unsigned short类型
//L 代表unsigned long类型
//Q 代表unsigned long long类型
//f 代表float类型
//d 代表double类型
//B 代表C++中的bool或者C99中的_Bool
//v 代表void类型
//* 代表char *类型
//@ 代表对象类型
//# 代表类对象 (Class)
//: 代表⽅法selector (SEL)
//[array type] 代表array
//{name=type…} 代表结构体
//(name=type…) 代表union
//bnum A bit field of num bits
//^type A pointer to type
//? An unknown type (among other things, this code is used for function pointers)

试试打印元类的方法,只有一个类方法,这也能证明元类只存放类方法。

image-20220428153137003

实例方法

类对象和元类对象,通过class_getInstanceMethod获取实例方法的方式,来获取一下实例方法和类方法(正常应该不能)。

// 获取类对象和元类的实例方法
void ff_class_getInstanceMethod(Class ffClass) {
    const char *className = class_getName(ffClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method cInstanceMethod = class_getInstanceMethod(ffClass, @selector(testInstancePrint));
    Method mInstanceMethod = class_getInstanceMethod(metaClass, @selector(testInstancePrint));
    Method cClassMethod = class_getInstanceMethod(ffClass, @selector(testClassPrint));
    Method mClassMethod = class_getInstanceMethod(metaClass, @selector(testClassPrint));
    
    NSLog(@"类对象的实例方法: %p", cInstanceMethod);
    NSLog(@"元类对象的实例方法: %p", mInstanceMethod);
    NSLog(@"类对象的类方法: %p", cClassMethod);
    NSLog(@"元类对象的类方法: %p", mClassMethod);
}

运行效果:

image-20220428154204093

为什么元类能通过class_getInstanceMethod获取类方法?正常应该是class_getClassMethod函数来获取的

查看源码可知,本质还是class_getInstanceMethod只是参数变成了元类:cls->getMeta()

/***********************************************************************
* class_getClassMethod.  Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;
    return class_getInstanceMethod(cls->getMeta(), sel);
}

那么类方法其实也是实例方法?iOS底层根本没有类方法和实例方法之分。本质都是函数,而runtime通过消息机制调用函数。

元类存放类方法,是为了复用消息机制。满足单一职责的设计原则,同时对类的结构体模板进行复用。

IMP

通过class_getMethodImplementation获取方法的实现(IMP):

// 获取方法实现IMP
void ff_class_getMethodImpl(Class ffClass) {
    const char *className = class_getName(ffClass);
    Class metaClass = objc_getMetaClass(className);
    
    IMP cInstanceMethodImpl = class_getMethodImplementation(ffClass, @selector(testInstancePrint));
    IMP mInstanceMethodImpl = class_getMethodImplementation(metaClass, @selector(testInstancePrint));
    IMP cClassMethodImpl = class_getMethodImplementation(ffClass, @selector(testClassPrint));
    IMP mClassMethodImpl = class_getMethodImplementation(metaClass, @selector(testClassPrint));
    
    NSLog(@"类对象的实例方法IMP: %p", cInstanceMethodImpl);
    NSLog(@"元类对象的实例方法IMP: %p", mInstanceMethodImpl);
    NSLog(@"类对象的类方法IMP: %p", cClassMethodImpl);
    NSLog(@"元类对象的类方法IMP: %p", mClassMethodImpl);
}

运行结果:

image-20220428160316043

元类里面也能找到实例方法的实现?而且mInstanceMethodImplcClassMethodImpl一样内存地址…

查找源码:

__attribute__((flatten))
IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    lockdebug_assert_no_locks_locked_except({ &loadMethodLock });

    imp = lookUpImpOrNilTryCache(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    // Translate forwarding function to C-callable external version
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}

if (!imp)表示没找到方法时,会返回_objc_msgForward;这个就是用来做消息转发的。

以前的文章,用到.big()来获取方法,查看big结构体可知,包含SELIMP

image-20220428160755831

总结

成员变量

成员变量存放在类对象的class_ro_t结构体里,因为它们不能被外部修改。

为什么设计元类?

  • 复用消息机制。
  • 符合设计原则中的单一职责。
  • 不同种类的方法走的都是同一套流程,易于维护。

类方法和实例方法的区别

在objc底层没有类方法和实例方法的区别,都是函数,通过消息机制调用。只不过类方法存储在元类对象里,实例方法存储在类对象里。

类信息的存储:ro、rw、rwe

class_ro_t是在编译的时候生成的。当类在编译的时候,类的属性,实例方法,协议这些内容就存在class_ro_t这个结构体里面了,这是一块纯净的内存空间,不允许被修改。

class_rw_t是在运行的时候生成的,类一经使用就会变成class_rw_t,它会先将class_ro_t的内容"拿"过去,然后再将当前类的分类的这些属性、方法等拷⻉到class_rw_t里面。它是可读写的。

class_rw_ext_t可以减少内存的消耗。苹果在WWDC2020里面说过,只有大约10%左右的类需要动态修改。所以只有10%左右的类里面需要生成class_rw_ext_t这个结构体。这样的话,可以节约很大一部分内存。

class_rw_ext_t生成的条件:

  • 用过runtime的api进行动态修改的时候。
  • 有分类的时候,且分类和本类都为非懒加载类 (实现了+ load方法)。

扩展

类型编码

官方文档:runtime类型编码

方法的类型:

image-20220506151926795

属性的类型:

image-20220506152039060

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS底层-类的三顾茅庐(二) 的相关文章

  • 浅谈linux - vs code搭建运行调试环境

    概述 在Linux开发过程中 xff0c 用不惯vim的用户往往会选择vs code软件或者source insight软件编写相关的源代码 xff0c 由于vs code对linux系统的支持及界面的美观 xff0c 所以小编往往会选择使
  • 浅谈linux - samba实现linux与windows文件共享

    概述 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件 xff0c 而SMB是Server Message Block的缩写 xff0c 即为服务器消息块 xff0c SMB主要是作为Microsoft的网络通讯协
  • mysql的update、delete、和insert into时表别名用法

    mysql数据库update delete 和insert into表的时候 xff0c 如果要使用表别名 xff0c 必须按照规范写法来写 xff1a insert xff1a 简写 xff1a insert into t a a a i
  • 浅谈linux - RT Linux解决方案

    概述 RT Linux Real Time Linux 亦称作实时 Linux xff0c 是 Linux 中的一种硬实时操作系统 xff0c 它最早由美国墨西哥理工学院的 V Yodaiken 开发 产品资料提供的 Linux RT 内核
  • 浅谈linux - 线程的基本应用

    概述 线程 xff08 英语 xff1a thread xff09 是操作系统能够进行运算调度的最小单位 它被包含在进程之中 xff0c 是进程中的实际运作单位 注意 线程和进程之间的区别 1 线程是执行的基本单位 xff1b 进程是资源分
  • 浅谈linux - mutex锁应用

    概述 互斥锁是专门用于处理线程之间互斥关系的一种方式 xff0c 它有两种状态 xff1a 上锁状态 解锁状态 如果互斥锁处于上锁状态 xff0c 那么再上锁就会阻塞到这把锁解开为止 xff0c 才能上锁 解锁状态下依然可以解锁 xff0c
  • 浅谈linux - cond条件变量应用

    概述 条件变量不是一个把锁 xff0c 它实质上一个类似信号的东西 xff0c 与锁相互配合使用 xff0c 因为锁所能达到的功能就只有加锁和解锁 xff0c 并不能实现线程之间的一些关联 xff0c 于是条件变量就出现了 xff0c 与锁
  • 设计UI - Adobe xd画板及参考线

    画板 新建画板 a 使用预设画板大小或创建自定义画板 操作步骤 xff1a 打开xd软件 xff0c 点击需要建立的画板模版 xff0c 没有则选择自定义大小 b 使用画板工具创建其它画板 操作步骤 xff1a 选中画板工具 xff0c 选
  • activity的启动模式与newIntent()

    很多知识当我们用到的时候 xff0c 才发觉有多好用 今天需要完成一个功能 xff0c 创建一个悬浮窗 xff0c 点击悬浮窗上按钮 xff0c 加载到一个包含webView 的activity xff0c 为了避免activity重复创建
  • 基于DOCKER安装华为自研数据库高斯(GAUSS)

    华为数据库裸机安装比较困难 xff0c 我们可以采用docker的安装方式 xff0c 自己学习和测试使用 安装环境 1 xff09 centos 7 9 2 xff09 docker版本 opengauss 注意 xff0c 我们在安装此
  • MariaDB 中文乱码问题解决

    编辑文件 vim etc my cnf d server cnf 在 mysqld 下面添加两行 init connect 61 39 SET NAMES utf8 39 character set server 61 utf8 重启Mar
  • 【Linux】安装kali遇到的一些细节

    前言 本文省略安装教程 xff0c 如有安装教程需求 xff0c 请自行查阅 博主安装的版本为 xff1a kali 2023 1 amd 64 一 gdm3默认管理器 情景 其它系统基础配置进度完成后 xff0c 大部分安装的人 xff0
  • Windows 2016 修改密码时提示密码不符合规则

    一 现象描述 在修改服务器密码时 xff0c 遇到如下情况 xff1a 提示密码不满足密码策略要求 二 处理方法 在电脑服务器管理中找到 工具 单击 本地安全策略 进入策略管理 在本地安全策略中找到帐户安全策略 xff0c 单击进入 帐户策
  • MATLAB即将跌出TOP 20,TIOBE 4月编程语言排行榜出炉

    CSDN 编者按 一月一次的编程语言排行榜出炉 xff01 责编 张红月 出品 CSDN xff08 ID xff1a CSDNnews xff09 在TIOBE发布的4月编程语言排行榜中 xff0c 知名数学算法分析语言MATLAB即将跌
  • python3 爬虫实战案例 (抓取淘宝信息)(淘宝加了搜索必须登录的验证,此方法所到的结果都是0)

    需求 xff1a 对比足球 xff0c 篮球 xff0c 乒乓球 xff0c 羽毛球 xff0c 网球 xff0c 相关物品的销售量保存到excle中 和抓取淘宝关键字相关信息的销售量 xff0c 这和之前抓取csdn网站浏览量取不同 xf
  • android studio中Gradle 编译需要重点注意gradle,wrapper,build tools之间的版本对应关系

    Android Studio 2 3 的平台已经已经是相对稳定的发布版 xff0c 新的功能不断推出 xff0c 包括对NDK 的完美支持 它看起来有一些重大的改变也正在等待合适的孵化时机 xff0c 如 xff1a 新的 Gradle 构
  • VNC Viewer 设置屏幕分辨率-解决屏幕分辨率问题

    先介绍一款好用的连接工具介绍一个 VNC连接工具 xff1a http fwqglgj iis7 net cp vnc tscc IIs7服务器管理工具可以批量连接并管理VNC服务器 作为服务器集成管理器 xff0c 它最优秀的功能就是批量
  • LAMP环境简单搭建

    一 简介 LAMP 是Linux Apache MySQL PHP的简写 xff0c 其实就是把Apache MySQL以及PHP安装在Linux系统上 xff0c 组成一个环境来运行php的脚本语言 Apache是最常用的WEB服务软件
  • 转身不带走一丝云彩--我的2014

    时间或许就是这样不管你愿意不愿意都会毫不犹疑的向前 xff0c 逼你成长 2014年得到了很多也失去了很多 xff0c 我对未来还是有诸多憧憬的 谨以此文献给过去的时光 xff0c 也希望对后来人能有所帮助 改变篇 相比于2013年 xff
  • 一年装三次Arch Linux,每次都有新收获

    只要跟随优秀的教程 xff0c 装机过程so easy archlinux 双系统真机安装演示 哔哩哔哩 bilibili Archlinux系统安装 xff0c 配置 xff0c 游戏 黑客驰HackerChi 哔哩哔哩 bilibili

随机推荐

  • Linux系统中PS1命令详解

    本帖主要针对经常使用Linux命令行且对命令行界面美观和可阅读性有一定要求的同学 xff0c 主要讲解如何通过修改PS1命令定义命令行的显示以及提供一条可直接复用的PS1命令 原文链接 简介 xff1a PS1命令是linux系统中的一个全
  • firewalld

    一 firewalld 介绍 firewalld 防火墙 xff0c 其实就是一个隔离工具 xff1a 工作于主机或者网络的边缘 xff0c 对于进出本主机或者网络的报文根据事先定义好的网络规则做匹配检测 xff0c 对于能够被规则所匹配的
  • yum升级CURL到最新版本的方法,非常好用

    首先 xff0c 先为你的服务器获取最新匹配的源 xff1a http mirror city fan org ftp contrib yum repo 安装新版libcurl的yum源 rpm ivh http mirror city f
  • 百度笔试题2018

    题外话 首先我要吐槽一下 xff0c csdn简直是在作死啊 xff0c 复制博文底下的那个引用太恶心了 xff0c 我复制自己的博客 xff0c 还有引用 xff0c 啥玩意啊 所以我决定换地方了 xff0c 以后github xff08
  • 你见过的最全面的Python重点知识总结!

    这是一份来自于 SegmentFault 上的开发者 64 二十一 总结的 Python 重点 由于总结了太多的东西 xff0c 所以篇幅有点长 xff0c 这也是作者 34 缝缝补补 34 总结了好久的东西 xff0c 强烈建议收藏再慢慢
  • 程序员经典语录

    程序员编程语录 1 一个好的程序员是那种过单行线马路都要往两边看的人 xff08 Doug Linder xff09 2 程序有问题时不要担心 如果所有东西都没问题 xff0c 你就失业了 xff08 软件工程的Mosher定律 xff09
  • 使用fastboot命令刷机流程详解

    一 Fastboot是什么 1 1 首先介绍Recovery模式 卡刷 在系统进行定制时 xff0c 编译系统会编译出一份ZIP的压缩包 xff0c 里面是一些系统分区镜像 xff0c 提供给客户进行手动升级 恢复系统 需要提前将压缩包内置
  • 【谷歌插件】谷歌插件制作

    文章目录 谷歌浏览器插件制作教程实现步骤成功示例问题未封装的扩展程序并非来自 Chrome 网上应用商店 谷歌浏览器插件制作 教程 教程1 xff1a https blog csdn net github 35631540 article
  • 一个刚毕业大学生的四个月苦逼程序员经历

    先来一个自我介绍 大学时排名老三 就暂且叫老三吧 xff0c 毕业于河南的一个还算可以的二本院校 xff0c 专业 地球信息科学与技术 首先介绍一下我的专业 xff0c 听着名字很高大上 xff0c 其实 xff0c 我们都叫他四不像专业
  • JS中的require、import、default、export

    刚开始学的时候经常弄混总结一下 xff1a 懒人 xff1a 1 require xff08 导入 xff09 是Commonjs的规范与module exports xff08 导出 xff09 搭配使用 2 import xff08 导
  • Ubuntu安装python3

    sudo apt get install python3 安装python3 xff0c 安装完之后系统默认还是python2 xff0c 要删除python link文件 sudo rm rf usr bin python 然后建立新连接
  • ubuntu安装shutter出现E:无法修正错误

    使用Ubuntu16 04安装shutter时出现如下错误 通过换源可以解决
  • Ubuntu不能访问Windows分区

    将Windows的快速启动关闭即可解决次问题 在电脑中安装了双系统 xff0c 但有时候在Ubuntu中访问Windows分区会出现如下错误 xff1a 以前出现过这种错误 xff0c 是因为windows系统没有完全关闭 xff0c 当时
  • Ubuntu和Windows双系统时间不对的解决办法

    在使用一系统再切换到另一个系统之后 xff0c 系统时间好像是停留在上次关闭该系统的时间 在网上的解决办法通常是 xff1a sudo gedit etc default rcS xff0c 将UTC 61 yes改成UTC 61 no 但
  • Ubuntu出现依赖关系问题 - 仍未被配置问题

    安装软件包时候出现如下错误 xff1a 但这并不是依赖问题 xff0c 使用sudo apt get f install 无法解决 其实问题是因为这六个软件包没有被完全安装或卸载 在安装其他软件的时候会出现 xff1a 就是指这六个软件 使
  • 熬夜总结!最全的Pycharm常用快捷键大全!

    版权声明 xff1a 本文为博主原创文章 xff0c 遵循 CC 4 0 BY SA 版权协议 xff0c 转载请附上原文出处链接和本声明 本文链接 xff1a https blog csdn net momoda118 article d
  • iOS底层-对象里都有什么

    前言 上篇文章说了iOS中alloc方法是怎么创建对象的 xff0c 以及对象的本质是结构体 接下来继续探究对象的内存分布 xff0c 以及对象的isa是个什么样的结构体 xff0c 存储了哪些信息 对象内存分布 已知系统给对象分配内存是1
  • iOS底层-类的三顾茅庐(一)

    前言 了解完对象的底层 xff0c 知道isa指向的是类对象 那么类 xff08 Class xff09 的本质究竟是什么 xff1f 本文顺序isa的指向 xff0c 探索类的继承链 xff0c 和类对象的结构 xff0c 并且尝试获取方
  • iOS底层-alloc方法之旅

    前言 通过汇编调试和源码分析 xff0c 介绍iOS开发当中alloc方法到底做了什么 追踪 alloc 实例化一个对象往往是通过 xxx alloc init 那么alloc和init的区别是什么 xff1f 将两个方法分开调用 xff0
  • iOS底层-类的三顾茅庐(二)

    前言 上篇文章分析了objc class里存储数据的bits xff0c 了解到方法和属性的存储的位置class rw t xff08 以下简称rw xff09 本文将继续研究rw里包含的其他内容 类数据的存储 书接上文 xff0c rw结