UncaughtExceptionHandler

2023-10-26

61.常见的Exception Type
1 EXC_BAD_ACCESS
在开发过程中,总是会遇到各种Exception,在此总结一些常见的Exception。

NSInvalidArgumentException

错误类型 NSInvalidArgumentException
log输出 unrecognized selector sent to instance xxxx
错误释义 给实体对象发送了不认识的消息,即对象调用方法出错
错误基本原因 Objective-C的方法调用其实是基于消息传递机制,并且是动态编译。因此在编译阶段不会进行类和方法的绑定,而是在运行时执行绑定操作。当类的方法没有实现或对象
场景1 给对象发送消息时:

[self performSelector:@selector(testException:) withObject:@"string"];

-(void)testException {
    NSLog(@"%s",__func__);
}

错误分析:给对象发消息的时候,该消息是带形参的,而实现的方法却不带形参,因此产生了异常。

场景2 在集合中:
NSMutableArray *array = [NSMutableArray array];
[array addObject:nil];

错误分析:当企图给集合插入nil值的时候,实际上调用的是[__NSArrayM insertObject: atIndex: ],根据CFArray的源码推断,在该方法里面,对插入的对象进行了断言操作(仅仅是猜想,未能找到CFMutableArray的源码)。
NSRangeException
错误类型 NSRangeException
log输出 1.index 2 beyond bounds [0 … 1]
2.index x beyond bounds for …
错误释义 1.索引序号2超出0~1区间
2.索引序号超出…

常见场景
场景1 插入或获取对象:

    NSMutableArray *array = [NSMutableArray array];
    [array addObject:@"one"];
    [array addObject:@"two"];
    [array objectAtIndex:2];

错误分析:当试图从数组中获得一个对象的时候,索引大于自身容量,因而引起该异常。同样在插入的时候,不正确的索引值也会引发这个问题。

crash文件
当运行的APP发生crash的时候,如果代码里面增加对应的handler或者有第三方的crash SDK,他们会采集相关的运行堆栈,发送到对应的服务器上,然后通过开发者上传的dsym文件进行解析,得到符号化的堆栈信息,我们可以通过分析这个知道crash的原因。另外,当发生crash的时候,相应的设备上也会生成一个crash文件。我们可以通过Xcode导出crash文件。

Incident Identifier: B9C9CA64-DB6D-4EA7-AE86-A7BD841E2A50
CrashReporter Key:   6b0e0da01ba5183a74d6fb37e0ebd2b46540e89d
Hardware Model:      iPhone7,2
Process:             AlipayWallet [221]
Path:                /private/var/containers/Bundle/Application/8A3BF1BC-06B9-481F-8B33-5F51CCA1D764/AlipayWallet.app/AlipayWallet
Identifier:          com.alipay.iphoneclient
Version:             10.1.5.102407 (10.1.5)
Code Type:           ARM-64 (Native)
Role:                Non UI
Parent Process:      launchd [1]
Coalition:           com.alipay.iphoneclient [359]
 
 
Date/Time:           2017-10-28 18:27:52.9845 +0800
Launch Time:         2017-10-28 18:27:26.7691 +0800
OS Version:          iPhone OS 11.0.3 (15A432)
Baseband Version:    6.17.00
Report Version:      104
 
Exception Type:  EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d
Termination Description: SPRINGBOARD, process-exit watchdog transgression: com.alipay.iphoneclient exhausted real (wall clock) time allowance of 5.00 seconds |  | Elapsed total CPU time (seconds): 10.020 (user 10.020, system 0.000), 100% CPU | Elapsed application CPU time (seconds): 1.194, 12% CPU | 
Triggered by Thread:  0
 
Filtered syslog:
None found
 
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   CoreFoundation                  0x00000001818a057c CFAllocatorAllocate + 0
1   CoreFoundation                  0x00000001818a09bc _CFRuntimeCreateInstance + 348
2   CoreFoundation                  0x00000001818a1610 CFBasicHashCreate + 108
3   CoreFoundation                  0x0000000181961c44 __CFDictionaryCreateTransfer + 156
4   CoreFoundation                  0x00000001819462b0 __CFBinaryPlistCreateObjectFiltered + 5952
5   CoreFoundation                  0x0000000181945ed0 __CFBinaryPlistCreateObjectFiltered + 4960
6   CoreFoundation                  0x00000001819469e4 __CFTryParseBinaryPlist + 200
7   CoreFoundation                  0x00000001818a0d54 _CFPropertyListCreateWithData + 192
8   CoreFoundation                  0x00000001818ec178 _CFPropertyListCreateFromXMLData + 116
9   Foundation                      0x00000001822fa68c +[NSPropertyListSerialization propertyListFromData:mutabilityOption:format:errorDescription:] + 52
10  AlipayWallet                    0x00000001023f2058 0x100480000 + 32972888
11  AlipayWallet                    0x00000001023f0ad4 0x100480000 + 32967380

header
Incident Identifier: 事件标识符,每个crash文件对应一个唯一的标识符。
CrashReporter Key: 匿名设备标识符。
Hardware Model:设备型号;
Process: 进程名;
Identifier:app Identifier;
Exception Type: 异常类型;
Exception Codes: 异常代码;
Termination Reason:进程被结束的原因
线程堆栈信息
下面是各个线程的堆栈信息,有部分是符号化的,有的是没有符号化的,一般系统动态库相关的都已经符号化的,没有符号化的是运行APP里面的符号,我们可以通过atos或者symbolicatecrash命令逐行解析。

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   CoreFoundation                  0x00000001818a057c CFAllocatorAllocate + 0
.....
9   Foundation                      0x00000001822fa68c +[NSPropertyListSerialization propertyListFromData:mutabilityOption:format:errorDescription:] + 52
10  AlipayWallet                    0x00000001023f2058 0x100480000 + 32972888
11  AlipayWallet                    0x00000001023f0ad4 0x100480000 + 32967380
 

第一行的数字表示对应线程的调用栈顺序;
第二行表示对应的镜像文件名称,如系统的foundation等等;
第三行表示当前行对应的符号地址;
第四行: 如果是已经符号化过的,则表示对应的符号,没有符号化的是镜像的起始地址+文件偏移地址,这2个值相加实际上就是第三行的地址。

Exception
从上面的分析我们可以知道在crash文件的头部包含的Exception相关信息。下面我们来看看我们常见的几种Exception类型:在这里插入图片描述

当你的iOS应用崩溃的时候,我们需要去分析异常日志以定位根本原因。崩溃可能是 “低内存崩溃 Low Memory Crash” 或者 “普通异常崩溃”。当碰到“异常”时,更好的理解“不同类型的异常”能够真正帮助我们快速定位问题所在。

在这篇文章中,我们将研究 iOS 应用可能碰到的不同类型的“异常”,例如EXC_CRASH、EXC_BAD_ACCESS、EXC_RESOURCE、00000020 等。

崩溃日志中的“异常”
“异常”这个词在“崩溃日志”语境下更多与“Mach 异常”(以“EXC_为前缀”)和 “UNIX 信号”(如: SIGSEGV, SIGBUS等)相关。在某些情况下(应该是有对应的dSYM符号文件时)系统会通过映射将底层的 Mach 异常 翻译为 UNIX 信号。这就是为什么你能log中看到有用 “EXC_CRASH(SIGABRT)” 及 “EXC_BAC_ACCESS(SIGSEGV)” 作为 异常类型(Exception Type)。

对于某些异常,还会附带一个关联的 处理器定制异常码(processor-specific Exception Code) 或者 异常子类型(Exception Subtype),用以包含更多问题相关信息。举例来说, “EXC_BAC_ACCESS” 类型异常可能有一行如“KERN_INVALID_ADDRESS at 0x80000010”作为“异常码”; “EXC_RESOURCE” 可能有一行"WAKEUPS"作为"异常子类别"

UNIX 信号
iOS开发者常见的 UNIX 信号 如下:
UNIX 信号 注释
SIGSEGV 访问无效的内存地址。地址存在,但是应用程序无法访问。
SIGABRT 程序崩溃。由 C函数 abort() 初始化。通常意味着系统检测到某些事务出错,例如 assert() 或者 NSAssert() 校验失败。
SIGBUS 访问无效的内存地址。地址不存在,或对齐无效。(The address does not exist, or the alignment is invalid.)
SIGTRAP 调试器相关
SIGILL 尝试执行非法的、有缺陷、未知的或者需要权限的指令。

Mach 异常
Mach 异常 描述 注释
EXC_BAD_ACCESS 错误内存访问 访问“错误”内存地址。“错误”可能指“地址不存在”或者“应用没有权限访问”。因此通常与 SIGBUS 及 SIGSEGV 相关联。
EXC_CRASH 异常跳出 通常与 SIGABRT 相关联,意思是由于检测到代码抛出的未捕获异常而使应用程序异常退出。
EXC_BREAKPOINT 跟踪/断点捕获 通用与 SIGTRAP 相关联。可以由你自己的代码或者 NSExceptions 抛出时触发。
EXC_GUARD 违反了受保护资源的防护(Violated Guarded Resource Protection) 由违背受保护资源防护触发,例如‘某些文件描述符’。
EXC_BAD_INSTRUCTION 非法指令 通常与特定非法或未定义指令/操作数相关。
EXC_RESOURCE 资源限制 应用由于达到资源消耗限制而退出。
00000020 十六进制异常类型 非 ‘OS Kernel’ 异常。

异常
EXC_BAD_ACCESS(错误内存访问)
“EXC_BAD_ACCESS” 是APP崩溃时最常见的异常之一。不幸的是,调试起来却不容易。

一般有两种可能性:

访问某些尚未初始化的对象。(SIGBUS)
访问已经被 ARC 释放(导致地址变为不可访问)的对象。如果是这个情况,你通常可以在崩溃日志中的 “Backtrace” 顶部附近看到 objc_release。
示例如:

Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x6d783f44

Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x00000011

“EXC_BAD_ACCESS”也有关联的“异常码”以帮助提供额外信息。举例来说,KERN_PROTECTION_FAILURE 表示内存有效,但是不允许当前形式的访问,KERN_INVALID_ADDRESS 意思是地址当前无效。

为了辅助调试 “EXC_BAD_ACCESS” 类型异常,你可以勾选 Xcode 中的 “Enable Zombie Objects” 后再尝试。

EXC_CRASH(异常跳出)
相较于 “EXC_BAD_ACCESS”,“EXC_CRASH" 更容易遇到。它通常发生在对象接收到未实现的消息时,如 Xcode 调试器中显示的 “unrecognized selector sent to instance 0x6a33840”。

UncaughtExceptionHandler.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface UncaughtExceptionHandler : NSObject

/*!
 *  异常的处理方法
 *
 *  @param install   是否开启捕获异常
 *  @param showAlert 是否在发生异常时弹出alertView
 */
+ (void)installUncaughtExceptionHandler:(BOOL)install showAlert:(BOOL)showAlert;
@end

UncaughtExceptionHandler.m

#import "UncaughtExceptionHandler.h"
#include <libkern/OSAtomic.h>
#include <execinfo.h>

NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";
NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";

volatile int32_t UncaughtExceptionCount = 0;
const int32_t UncaughtExceptionMaximum = 10;

static BOOL showAlertView = nil;

void HandleException(NSException *exception);
void SignalHandler(int signal);
NSString* getAppInfo();


@interface UncaughtExceptionHandler()
@property (assign, nonatomic) BOOL dismissed;
@end

@implementation UncaughtExceptionHandler

+ (void)installUncaughtExceptionHandler:(BOOL)install showAlert:(BOOL)showAlert {
    
    if (install && showAlert) {
        [[self alloc] alertView:showAlert];
    }
    
    NSSetUncaughtExceptionHandler(install ? HandleException : NULL);
    signal(SIGABRT, install ? SignalHandler : SIG_DFL);
    signal(SIGILL, install ? SignalHandler : SIG_DFL);
    signal(SIGSEGV, install ? SignalHandler : SIG_DFL);
    signal(SIGFPE, install ? SignalHandler : SIG_DFL);
    signal(SIGBUS, install ? SignalHandler : SIG_DFL);
    signal(SIGPIPE, install ? SignalHandler : SIG_DFL);
}

- (void)alertView:(BOOL)show {
    
    showAlertView = show;
}

//获取调用堆栈
+ (NSArray *)backtrace {
    
    //指针列表
    void* callstack[128];
    //backtrace用来获取当前线程的调用堆栈,获取的信息存放在这里的callstack中
    //128用来指定当前的buffer中可以保存多少个void*元素
    //返回值是实际获取的指针个数
    int frames = backtrace(callstack, 128);
    //backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组
    //返回一个指向字符串数组的指针
    //每个字符串包含了一个相对于callstack中对应元素的可打印信息,包括函数名、偏移地址、实际返回地址
    char **strs = backtrace_symbols(callstack, frames);
    
    int i;
    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
    for (i = 0; i < frames; i++) {
        
        [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
    }
    free(strs);
    
    return backtrace;
}

//点击退出
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex {
#pragma clang diagnostic pop
    
    if (anIndex == 0) {
        
        self.dismissed = YES;
    }
}

//处理报错信息
- (void)validateAndSaveCriticalApplicationData:(NSException *)exception {
    
    NSString *exceptionInfo = [NSString stringWithFormat:@"\n--------Log Exception---------\nappInfo             :\n%@\n\nexception name      :%@\nexception reason    :%@\nexception userInfo  :%@\ncallStackSymbols    :%@\n\n--------End Log Exception-----", getAppInfo(),exception.name, exception.reason, exception.userInfo ? : @"no user info", [exception callStackSymbols]];
    
    NSLog(@"%@", exceptionInfo);
    //	[exceptionInfo writeToFile:[NSString stringWithFormat:@"%@/Documents/error.log",NSHomeDirectory()]  atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

- (void)handleException:(NSException *)exception {
    
    [self validateAndSaveCriticalApplicationData:exception];
    
    if (!showAlertView) {
        return;
    }
    
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    UIAlertView *alert =
    [[UIAlertView alloc]
     initWithTitle:@"出错啦"
     message:[NSString stringWithFormat:@"你可以尝试继续操作,但是应用可能无法正常运行.\n"]
     delegate:self
     cancelButtonTitle:@"退出"
     otherButtonTitles:@"继续", nil];
    [alert show];
#pragma clang diagnostic pop
    
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
    
    while (!self.dismissed) {
        //点击继续
        for (NSString *mode in (__bridge NSArray *)allModes) {
            //快速切换Mode
            CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
        }
    }
    
    //点击退出
    CFRelease(allModes);
    
    NSSetUncaughtExceptionHandler(NULL);
    signal(SIGABRT, SIG_DFL);
    signal(SIGILL, SIG_DFL);
    signal(SIGSEGV, SIG_DFL);
    signal(SIGFPE, SIG_DFL);
    signal(SIGBUS, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);
    
    if ([[exception name] isEqual:UncaughtExceptionHandlerSignalExceptionName]) {
        
        kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);
        
    } else {
        
        [exception raise];
    }
}
@end



void HandleException(NSException *exception) {
    
    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
    // 如果太多不用处理
    if (exceptionCount > UncaughtExceptionMaximum) {
        return;
    }
    
    //获取调用堆栈
    NSArray *callStack = [exception callStackSymbols];
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
    [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];
    
    //在主线程中,执行制定的方法, withObject是执行方法传入的参数
    [[[UncaughtExceptionHandler alloc] init]
     performSelectorOnMainThread:@selector(handleException:)
     withObject:
     [NSException exceptionWithName:[exception name]
                             reason:[exception reason]
                           userInfo:userInfo]
     waitUntilDone:YES];
}

//处理signal报错
void SignalHandler(int signal) {
    
    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
    // 如果太多不用处理
    if (exceptionCount > UncaughtExceptionMaximum) {
        return;
    }
    
    NSString* description = nil;
    switch (signal) {
        case SIGABRT:
            description = [NSString stringWithFormat:@"Signal SIGABRT was raised!\n"];
            break;
        case SIGILL:
            description = [NSString stringWithFormat:@"Signal SIGILL was raised!\n"];
            break;
        case SIGSEGV:
            description = [NSString stringWithFormat:@"Signal SIGSEGV was raised!\n"];
            break;
        case SIGFPE:
            description = [NSString stringWithFormat:@"Signal SIGFPE was raised!\n"];
            break;
        case SIGBUS:
            description = [NSString stringWithFormat:@"Signal SIGBUS was raised!\n"];
            break;
        case SIGPIPE:
            description = [NSString stringWithFormat:@"Signal SIGPIPE was raised!\n"];
            break;
        default:
            description = [NSString stringWithFormat:@"Signal %d was raised!",signal];
    }
    
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    NSArray *callStack = [UncaughtExceptionHandler backtrace];
    [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];
    [userInfo setObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey];
    
    //在主线程中,执行指定的方法, withObject是执行方法传入的参数
    [[[UncaughtExceptionHandler alloc] init]
     performSelectorOnMainThread:@selector(handleException:)
     withObject:
     [NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName
                             reason: description
                           userInfo: userInfo]
     waitUntilDone:YES];
}

NSString* getAppInfo() {
    
    NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@)\nDevice : %@\nOS Version : %@ %@\n",
                         [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],
                         [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],
                         [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],
                         [UIDevice currentDevice].model,
                         [UIDevice currentDevice].systemName,
                         [UIDevice currentDevice].systemVersion];
    return appInfo;
}

AppDelegate.m调用

  
#import "AppDelegate.h"
#import "UncaughtExceptionHandler.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [UncaughtExceptionHandler installUncaughtExceptionHandler:YES showAlert:YES];
    
    //用来制造一些异常
    [self performSelector:@selector(string) withObject:nil afterDelay:2.0];
    return YES;
}
@end
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UncaughtExceptionHandler 的相关文章

随机推荐

  • coco游戏android.mk

    LOCAL PATH call my dir include CLEAR VARS LOCAL MODULE game shared LOCAL MODULE FILENAME libgame LOCAL CPP EXTENSION cc
  • 4G版本云音响设置教程阿里云平台版本

    4G版本云音响设置教程介绍 第一章 介绍了在阿里云物联网平台生一个设备使用的三元素 第二章 转换阿里云三元素 为MQTT参数 并下载到设备中 第三章 阿里云物联网套件协议使用说明 如何发送数据至设备并播放 本文目录引导 目录 4G版本云音响
  • egg-jwt的使用

    安装 npm install egg jwt save 配置 config config default js config jwt secret zidingyi 自定义 token 的加密条件字符串 config plugin js j
  • RPC答疑篇

    声明 本篇文章及代码仅供学习交流 严禁用于商业用途 否则由此产生的一切后果均与作者无关 上一篇的rpc发出来后 虽然我觉得我已经写得很细致了 但还是收到了很多私信说是跑不成功 所以再发 水 一篇 关于如何操作的文章 鉴于很多人没有某店的账号
  • Spring-Cloud-Alibaba之Dubbo

    在微服务构架中 不可避免的要遇到服务间的调用 目前的方式是通过RPC或者是rest的http接口调用 spring cloud中很多都使用的feign来做服务调用 在spring cloud alibaba的套装中我们使用dubbo来替换掉
  • 登录认证功能的统一拦截技术(拦截器)

    目录 1 说明 2 使用方法 1 定义拦截器 2 注册配置拦截器 3 示例 3 interceptor详细说明 1 拦截路径 2 执行流程 3 过滤器和拦截器的区别 4 登录校验的拦截器实现 5 全局异常处理 补充说明 1 说明 拦截器是一
  • Redis的三种模式——主从复制、哨兵、集群

    目录 一 Redis模式 二 Redis主从复制 2 1 主从复制概述 2 2 主从复制 2 3 Redis主从复制流程 2 4 搭建Redis主从复制 2 4 1 安装Redis 2 4 2 修改Master节点配置文件 192 168
  • 软件测试实战项目【电商、银行、商城、金融、医药、电商】

    最近 不少读者托我找一个能实际练手的测试项目 开始 我觉得这是很简单的一件事 但当我付诸行动时 却发现 要找到一个对新手友好的练手项目 着实困难 这是博主收集了很久才弄到的 希望可以帮助到你 由于项目太多了就不一一介绍了 只介绍这一个 电商
  • MySQL适合与不适合建索引的情况

    适合建索引情况 主键自动建立唯一索引 频繁作为查询条件的字段应该创建索引 查询中与其它表关联的字段 外键关系建立索引 Where条件里用不到的字段不创建索引 单键 组合索引的选择问题 在高并发下倾向创建组合索引 查询中排序的字段 排序字段若
  • 三个例题教会你二分法

    二分法 二分法 例题 数组查找 错误版本号 插入位置 二分法 先看一个很有意思的段子 有一天小明到图书馆借了 N 本书 出图书馆的时候 警报响了 于是保安把小明拦下 要检查一下哪本书没有登记出借 小明正准备把每一本书在报警器下过一下 以找出
  • 【毕设选题】深度学习人体语义分割在弹幕防遮挡上的实现 - python

    文章目录 1 课题背景 2 技术原理和方法 2 1基本原理 2 2 技术选型和方法 3 实例分割 4 实现效果 5 最后 1 前言 深度学习人体语义分割在弹幕防遮挡上的应用 学长这里给一个题目综合评分 每项满分5分 难度系数 3分 工作量
  • ad取消覆铜_【学院推荐】PCB工程师不得不看:超级实用AD常用快捷键总结

    快捷键的实用 极大的提高了大家工作中的效率 因此小编我特意帮大家搜集整理很多关于AD方面的常用快捷键 希望对大家有所帮助 一 PCB中常用快捷键 R L 输出PCB中所有网络的布线长度 Ctrl 左键点击 对正在布的线完成自动布线连接 M
  • python之word文档生成

    python之word文档生成 python docx官方文档 python docx操作word文档 python文件读取操作 excel文件操作 python docx官方文档 Document objects python docx
  • angular html原理,angular数据双向绑定的原理是什么?

    Angular是通过脏检测来进行双向数据绑定 所谓的双向绑定 无非是从界面的操作能实时反映到数据 数据的变更能实时展现到界面 angular数据双向绑定的原理 页面中每绑定一个数据或者事件时 就会向 watch队列中加入一条 watch 当
  • 几款Android 应用自动化测试工具

    简述 本文介绍几款流行的 Android应用自动化测试工具 Monkey测试 随机测试 压力测试 运行在模拟器或实际设备中 MonkeyRunner测试 操作简单 可录制测试脚本 可视化操作 主要生成坐标的自动化操作 移植性不强 Robot
  • keil编译问题error C100: unprintable character... 和WARNING L16: UNCALLED SEGMENT...

    keil编译出现问题如下 问题 error C100 unprintable character 0xA3 skipped 原因 keil里面出现了中文输入法时写的标点 程序中不能出现全角的任何字符 包括空格在内的任何标点符号和空格都只能在
  • ant design pro v5 - 03 动态菜单 动态路由(配置路由 动态登录路由 登录菜单)

    1 动态菜单 技术思路 配置路由 用户登录后根据用户信息获取后台菜单 2 动态路由 动态菜单 技术思路 使用umijs的运行时修改路由 patchRoutes routes UMIJS 参考文档 react umi 没有守护路由的功能 直接
  • 如何进行容器镜像加速?

    容器相比虚拟机最突出的特点之一便是轻量化和快速启动 相比虚拟机动辄十几个 G 的镜像 容器镜像只包含应用以及应用所需的依赖库 所以可以做到几百 M 甚至更少 但即便如此 几十秒的镜像拉取还是在所难免 如果镜像更大 则耗费时间更长 我们团队
  • python中MySQLdb的execute和executemany的使用

    如果使用executemany对数据进行批量插入的话 要注意一下事项 conn MySQLdb connect host localhost user root passwd password db myDB charset utf8 cu
  • UncaughtExceptionHandler

    61 常见的Exception Type 1 EXC BAD ACCESS 在开发过程中 总是会遇到各种Exception 在此总结一些常见的Exception NSInvalidArgumentException 错误类型 NSInval