Runtime_Interview

Runtime

前言:

Objective C的runtime技术功能非常强大,能够在运行时获取并修改类的各种信息,包括获取方法列表、属性列表、变量列表,修改方法、属性,增加方法,属性等等,本文对相关的几个要点做了一个小结。

目录:

(1)使用class_replaceMethod/class_addMethod函数在运行时对函数进行动态替换或增加新函数

(2)重载forwardingTargetForSelector,将无法处理的selector转发给其他对象

(3)重载resolveInstanceMethod,从而在无法处理某个selector时,动态添加一个selector

(4)使用class_copyPropertyList及property_getName获取类的属性列表及每个属性的名称

(5) 使用class_copyMethodList获取类的所有方法列表

应用:

viewWillAppear 埋点

KVO的本质

问: Category的实现原理,以及Category为什么只能加方法不能直接加属性(需要自己在分类中实现setter、getter方法)?

答:分类的实现原理是将category中的方法,属性,协议数据放在category_t结构体中,然后将结构体内的方法列表拷贝到类对象的方法列表中。 Category可以添加属性,但是并不会自动生成成员变量及set/get方法。因为category_t结构体中并不存在成员变量。通过之前对对象的分析我们知道成员变量是存放在实例对象中的,并且编译的那一刻就已经决定好了。而分类是在运行时才去加载的。那么我们就无法再程序运行时将分类的成员变量中添加到实例对象的结构体中。因此分类中不可以添加成员变量。

一些参考

Objective C运行时(runtime)技术总结

需求1、当项目中,需要继承某一个父类,但是父类中并没有提供我们需要的调用方法,一般情况下,这种时候,我们可能会再派生一个类,或者会直接为这个类写一个分类(category),在这个分类中,我们可以新增或者替换某个方法(注意:不推荐在分类中重写方法,而且也无法通过 super 来获取所谓父类的方法)。大致在这两种情况下,我们可以通过 class_addMethod 来实现我们想要的效果。

先从一个场景问题带出吧,比如一个应用都是继承于UIViewController,刚开始时候不要求要转屏,到后面才决定加上旋转屏适配,这个时候如果我们去创建一个父类,并让去修改每个类的继承这个方法显然是效率太低了。而且有时候,我们也不想多创个父类,于是我们就只能决定一次过替换掉这些controller里的viewWillAppear:和 willAnimateRotationToInterfaceOrientation:duration:,换成自己的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class clazz = object_getClass((id)self);

//将系统的resolveInstanceMethod替换成自己的myResolveInstanceMethod,而系统的resolveInstanceMethod方法就是当运行时对象调用了一个找不到的方法的时候系统会去寻找的机制。
[clazz swizzleMethod:@selector(resolveInstanceMethod:) withMethod:@selector(myResolveInstanceMethod:)];
});
}


/**
* 用什么方法替换掉什么方法
*
* @param origSel 原方法选择子
* @param aftSel 新方法选择子
*/
+ (BOOL)swizzleMethod:(SEL)origSel withMethod:(SEL)aftSel {
//通过class_getInstanceMethod()拿到对应的Method
Method originMethod = class_getInstanceMethod(self, origSel);
Method newMethod = class_getInstanceMethod(self, aftSel);

if(originMethod && newMethod){ //必须两个Method都要拿到
//class_addMethod将本来不存在于被操作的Class里的newMethod的实现添加在被操作的Class里,并使用origSel作为其选择子
if(class_addMethod(self, origSel, method_getImplementation(newMethod),method_getTypeEncoding(newMethod))) {
//当addMethod成功完成后,利用class_replaceMethod换掉method_getImplaementation(oiginMethod)的选择子,将原方法的实现的SEL换成新方法的SEL:aftSel,ok目的达成了。想一想,现在通过旧方法SEL来调用,就会实现新方法的IMP,通过新方法的SEL来调用,就会实现旧方法的IMP。
class_replaceMethod(self, aftSel, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
}
return YES;
}
return NO;
}

必备知识架构-①语言-消息转发2

[toc]

一、NSProxy

NSProxy,你可以通过继承它,并重写它的以下两个方法以实现消息转发到另一个实例。说白了,NSProxy专为代理而生(负责将消息转发到真正的target的代理类)。从类名来看是代理类,专门负责代理对象转发消息的。相比NSObject类来说NSProxy更轻量级,通过NSProxy可以帮助Objective-C间接的实现多重继承的功能

1
2
3
- (void)forwardInvocation:(NSInvocation *)anInvocation;

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel;

1、通过NSProxy可以帮助Objective-C间接的实现多重继承的功能的例子

现在比较流行的说法是用它来模拟多重继承,大致过程就是让它Hold住/包含你要实现多继承的类的对象,然后被hold住的对象的行为定义在接口中,并让Proxy去实现这些接口。然后再转发的时候把消息转发到实现了该接口的对象去执行,这样就好像实现了多重继承一样。注意:这个真不是多重继承,只是包含,然后把消息路由到指定的对象而已,其实完全可以用NSObject类来实现。

天使=人+鸟

1
2
3
4
5
6
Person *person = [Person new];
Bird *bird = [Bird new];

id angelProxy = [AngelProxy proxyForObject1:person object2:bird];
[proxy speak]; // 人可以说话
[proxy fly]; // 鸟可以飞

另一个例子:NSProxy的学习使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// AngelProxy.h
#import <Foundation/Foundation.h>

@interface AngelProxy : NSProxy

+ (id)proxyForObject1:(id)obj1 object2:(id)obj2;

@end




// AngelProxy.m
#import "AngelProxy.h"

@interface AngelProxy()

@property (nonatomic, weak) id target1;
@property (nonatomic, weak) id target2;
@end

@implementation AngelProxy

- (instancetype)initWithTarget1:(id)obj1 target2:(id)obj2 {
_target1 = obj1;
_target2 = obj2;

return self;
}

+ (id)proxyForObject1:(id)obj1 object2:(id)obj2 {
return [[AngelProxy alloc] initWithTarget1:obj1 target2:obj2];
}

// 重写NSProxy如下两个方法,在处理消息转发时,将消息转发给真正的Target处理
- (void)forwardInvocation:(NSInvocation *)invocation {
id target;
if ([_target1 methodSignatureForSelector:invocation.selector]) {
target = _target1;
}else{
target = _target2;
}
[invocation invokeWithTarget:target];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
NSMethodSignature *sig;

sig = [_target1 methodSignatureForSelector:selector];
if (sig) {
return sig;
}

sig = [_target2 methodSignatureForSelector:selector];
return sig;
}


@end

2、NSProxy 和 NSObject 的比较

相比NSObject,NSProxy更轻量级,做消息转发效率更高。

NSObject寻找方法顺序:本类->(父类-)>(动态方法解析)-> 消息转发;
NSproxy顺序:本类->消息转发

3、NSProxy的用途

  1. AOP面向切片编程

    iOS中面向切片编程一般有两种方式 ,一个是直接基于runtime 的method-Swizzling.还有一种就是基于NSProxy

  2. 解决NSTimer, CADisplayLink等强引用target引起的无法释放问题。如NSTimer:利用消息转发来断开NSTimer对象与视图之间的强引用关系。初始化NSTimer时把触发事件的target替换成一个单独的对象,然后这个对象中NSTimer的SEL方法触发时让这个方法在当前的视图self中实现。

    1
    2
    3
    4
    self.timer = [NSTimer timerWithTimeInterval:1 target:self.proxy 
    selector:@selector(timerEvent) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    // 这里我们timer的target使用AYProxy对象,它会实现你这个timer想要实现的timerEvent消息转发给self。从而避免了循环引用

    协议类代码为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #import

    @interface AYProxy : NSProxy

    @property (nonatomic, weak) id obj;

    @end

    #import "AYProxy.h"

    @implementation AYProxy

    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSMethodSignature *sig = nil;
    sig = [self.obj methodSignatureForSelector:aSelector];
    return sig;
    }

    - (void)forwardInvocation:(NSInvocation *)anInvocation{
    [anInvocation invokeWithTarget:self.obj];

    }

    @end
  1. 多重继承

    实现类似CAAnimation类族.

必备知识架构-①语言-消息转发

CJBaseHelper/HookCJHelper

pod search CJBaseHelper

image-20240822165215629

库的使用方法,见我简书中的以下文章:

CJHook

pod search CJHook

image-20240822165517533

在调用原始方法之前插入额外的执行逻辑

1、使用场景

  1. 日志记录与监控: 在方法执行前后添加日志记录,可以帮助开发者监控应用的行为,特别是在调试和性能分析时。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (void)swizzleMethod:(Class)class withSelector:(SEL)selector {
    Method originalMethod = class_getInstanceMethod(class, selector);
    IMP originalIMP = method_getImplementation(originalMethod);
    IMP newIMP = imp_implementationWithBlock(^(id self) {
    NSLog(@"Before method execution");
    ((void(*)(id, SEL))originalIMP)(self, selector);
    NSLog(@"After method execution");
    });
    method_setImplementation(originalMethod, newIMP);
    }

2、调用

以在进入个人主页前,需要先判断是否登录为例

常规:使用Util式、使用宏

1
2
3
4
5
6
7
8
- (void)goMineHomePage {
// 在调用原始方法之前执行的代码(进入个人主页前,需要先判断是否登录)
bool canPush = [LoginCheckerUtil checkLogin];
if (!canPush) return;

MineHomeViewController *viewController = [[MineHomeViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
}

改进(改进后发现,其实可读性不高,所以还不如不这么改):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// LoginCheckUtil.m
#define NeedLoginAnnotation(Class originalSelOwnerClass, SEL originalSelector) \
HookCJHelper_addPreExecutionToClassMethod(originalSelOwnerClass, originalSelector, [LoginCheckUtil class], @selector(loginCheckMethod));
class LoginCheckUtil: NSObject {
+ (bool)loginCheckMethod {
if (UserManager.isLogin) {
return YES;
}
LoginViewController *viewController = [[MineHomeViewController alloc] init];
[topVC pushViewController:viewController animated:YES];
return NO;
}
}

// HomeViewController.m
+ (void) initialize {
@NeedLoginAnnotation([self class], @selector(goMineHomePage)) // 在 + (void) initialize 中调用
}

- (void)goMineHomePage {

}

3、内部核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 在调用原始方法之前插入额外的执行逻辑
bool HookCJHelper_addPreExecutionToClassMethod(Class originalSelOwnerClass, SEL originalSelector, Class preExecSelOwnerClass, SEL preExecutionSelector) {
// 原始方法检查
if (!originalSelOwnerClass || !class_getClassMethod(originalSelOwnerClass, originalSelector)) {
return false;
}
Method originalMethod = class_getClassMethod(originalSelOwnerClass, originalSelector);
IMP originalIMP = method_getImplementation(originalMethod);
if (!originalIMP) {
return false;
}

// 要插入的方法
if (!preExecSelOwnerClass || !class_getClassMethod(preExecSelOwnerClass, preExecutionSelector)) {
return false;
}

// 创建新的方法实现
IMP newIMP = imp_implementationWithBlock(^(Class self) {
// 调用额外的逻辑(预执行类方法)
((void(*)(Class, SEL))objc_msgSend)(preExecSelOwnerClass, preExecutionSelector);
// 调用原始的类方法
((void(*)(Class, SEL))originalIMP)(self, originalSelector);
});

// 替换原始方法的实现为新的实现
method_setImplementation(originalMethod, newIMP);

return true;
}
  1. 什么时候会报unrecognized selector的异常
  • 当调用该对象上某个方法,而该对象上没有实现这个方法的时候。可以通过消息转发进行解决,流程见下图

    img

    image

  • OC在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX
    但是在这之前,OC的运行时会给出三次拯救程序崩溃的机会

  • Method resolution(消息动态解析
    OC运行时会调用+resolveInstanceMethod:或者+resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则,运行时就会移到下一步,消息转发(Message Forwarding

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 重写 resolveInstanceMethod: 添加对象方法实现
    + (BOOL)resolveInstanceMethod:(SEL)sel {
    // 如果是执行 run 函数,就动态解析,指定新的 IMP
    if (sel == NSSelectorFromString(@"run:")) {
    // class: 给哪个类添加方法
    // SEL: 添加哪个方法
    // IMP: 方法实现 => 函数 => 函数入口 => 函数名
    // type: 方法类型:void用v来表示,id参数用@来表示,SEL用:来表示
    class_addMethod(self, sel, (IMP)runMethod, "v@:@");
    return YES;
    }
    return [super resolveInstanceMethod:sel];
    }

    //新的 run 函数
    void runMethod(id self, SEL _cmd, NSNumber *meter) {
    NSLog(@"跑了%@", meter);
    }
  • Fast forwarding(消息接受者重定向
    如果目标对象实现了-forwardingTargetForSelector:Runtime这时就会调用这个方法,给你把这个消息转发给其他对象的机会。只要这个方法返回的不是nilself,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。 这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点

    1
    2
    3
    4
    5
    6
    7
    8
    // 消息接受者重定向
    - (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(run:)) {
    return [[Person alloc] init];
    // 返回 Person 对象,让 Person 对象接收这个消息
    }
    return [super forwardingTargetForSelector:aSelector];
    }
    • Normal forwarding(消息重定向
      这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nilRuntime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // 获取函数的参数和返回值类型,返回签名
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if ([NSStringFromSelector(aSelector) isEqualToString:@"run:"]) {
    return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
    }

    // 消息重定向
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
    // 从 anInvocation 中获取消息
    SEL sel = anInvocation.selector;
    if (sel == NSSelectorFromString(@"run:")) {
    // 1\. 指定当前类的一个方法作为IMP
    // anInvocation.selector = @selector(readBook:);
    // [anInvocation invoke];

    // 2\. 指定其他类来执行这个IMP
    Person *p = [[Person alloc] init];
    // 判断 Person 对象方法是否可以响应 sel
    if([p respondsToSelector:sel]) {
    // 若可以响应,则将消息转发给其他对象处理
    [anInvocation invokeWithTarget:p];
    } else {
    // 若仍然无法响应,则报错:找不到响应方法
    [self doesNotRecognizeSelector:sel];
    }
    }else{
    [super forwardInvocation:anInvocation];
    }
    }

    - (void)doesNotRecognizeSelector:(SEL)aSelector {
    [super doesNotRecognizeSelector:aSelector];
    }

    既然-forwardingTargetForSelector:-forwardInvocation:都可以将消息转发给其他对象处理,那么两者的区别在哪?
    区别就在于-forwardingTargetForSelector:只能将消息转发给一个对象。而-forwardInvocation:可以把消息存储,在你觉得合适的时机转发出去,或者不处理这个消息。修改消息的target,selector,参数等。将消息转发给多个对象

必备知识架构-①语言-请求拦截NSURLProtocol

[toc]

一、NSURLProtocol

如果开发者自定义的一个 NSURLProtocol 并且注册到 app 中,那么在这个自定义的 NSURLProtocol 中我们可以拦截所有的请求((注意,不只是webView内的请求,而是整个app内的所有请求)),进行修改,或者修改 response。即NSURLProtocol这个东西的作用可以让我们在app的内部拦截一切url请求。

「或者这么说吧: NSURLProtocol 就是一个苹果允许的中间人攻击。」

注意事项:

如果我们顺序注册 A B C 三个 Protocol,那么一个 connection 在发送的时候,处理的顺序是 C B A,而且最多只有一个 Protocol 会触发处理。

拦截 UIWebview 的请求,会有被拒的风险。

附:

iOS的Foundation框架提供了 URL Loading System 这个库(后面简写为ULS),所有基于URL(例如http://,https:// ,ftp://这些应用层的传输协议)的协议都可以通过ULS提供的基础类和协议来实现。而ULS库里提供了一个强有力的武器 NSURLProtocol。

NSURLProtocol能做什么?

举几个例子:

  • 全局网络请求设置:我们的APP内的所有请求都需要增加公共的头,像这种我们就可以直接通过NSURLProtocol来实现,当然实现的方式有很多种
  • 自定义 Response (过滤敏感信息)
  • 再比如我们需要将APP某个API进行一些访问的统计
  • 再比如我们需要统计APP内的网络请求失败率

知识架构–即时通信

以下内容摘自:开发即时通信是选择UDP还是TCP协议

大规模即时通讯软件的总体架构:

1。以UDP协议作为主要数据传输协议

2。服务端使用一个数据库保存信息(分布式数据库)

3。服务端是是分布式的,通过异步多线程技术,集群服务等 通过服务器集群共同运行服务端,对外进行海量信息处理(转发,暂存,广播消息)

4。客户端也属于分布式应用程序,具有一些服务端的功能 在进行语言,视频,文件传输的时候,可由服务端协调 在两个客户端直接进行点对点通信

必备知识架构

知识架构

iOS知识库

Android知识库

2020年iOS面试反思总结–不断更新当中ing

2020年iOS面试反思总结–不断更新当中ing

目录

1
2
>
>
1
2
3
4
5
6
>线程间通信的体现:
>1. 一个线程传递数据给另一个线程
>2. 在一个线程中执行完特定任务后,转到另一个线程继续执行任务
>
>附:其他传值方法总结
>
1
2
3
4
5
6
7
8
1、通常的做法/不合理的做法
2、合理的做法(使用多线程异步执行)
1)、先说说简单的多线程同步问题(异步线程里没有再开启异步线程)
2)、真正的线程同步问题(异步线程里再开启异步线程)
3)、其他补充
附1:多个请求依次执行
附2:并发数控制
>
1
2
...
>
1
2
3
4
1、SDWebImage在图片下载及缓存的处理方法
2、SDWebImageDownloader 异步的图片下载器
3、SDWebImageDownloaderOperation 下载操作
>
1
2
3
4
1、一次完整的HTTP请求过程
2、TCP三次握手
3、TCP四次挥手
>
1
2
3
4
5
6
7
8
9
10
附:整个响应链及事件链
1、完善响应链查找知识点
2、基础概念等详解
2.1 响应者对象(UIResponder)
2.2、UITouch(点击对象)
2.2.1、UITouch的几个主要属性和方法
2.2.2、UITouch的生成场景
2.3、UIEvent(事件对象)
3、响应链的应用
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1、RunLoop概念
>
2、RunLoop和线程的关系?
>
3、RunLoop相关各类关系
3.1 CFRunLoopSourceRef
3.1.附 上题中button点击后,关于RunLoop的过程
3.2 CFRunLoopTimerRef
3.3 CFRunLoopObserverRef
>
4、RunLoop的应用
附:autoreleasepool 自动释放池
runloop、autorelease pool以及线程之间的关系
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1、内存管理
(1)、在ObjC中,对象什么时候会被释放(或者对象占用的内存什么时候会被回收利用)?
(2)、那怎么知道对象已经没有被引用了呢?
(3)、autorelease、autoreleasepool(自动释放池)
(4)、autoreleasepool(自动释放池) 
(5)、autoreleasepool里面对象的内存什么时候释放?
(6)、runloop、autorelease pool以及线程之间的关系
(7)、自动释放池怎样创建
(8)、自动释放池使用注意
(9)、自动释放池的应用/什么时候要用@autoreleasepool
>
2、如何监测内存泄漏
>
3、循环引用
(1)、NSTimer的循环引用
(2)、block的循环引用
(2.1)、Objective-C中block为何得用copy修饰,能否用其他
(2.2)、block的循环引用
(2.3)、判断该block是否会发生循环引用
(2.4)、为什么masonry的block里引用self不需要weak?
(3)、委托delegate的循环引用
>
1
2
3
4
5
6
7
8
1、NSTimer和NSRunLoop的关系?
2、NSTimer使用细节
3、NSTimer的创建
4、NSTimer的循环引用
5、NSTimer使用的优化
6、NSTimer的销毁问题
6.1子线程中NSTimer的创建和销毁问题
>
  • 十一、谈谈设计模式
  • 十二、如何优化过于臃肿的Controller
  • 十三、谈谈性能优化(功耗)
  • 十四、UITableView的优化、重用
  • 十五、布局 layoutsubview、drawrect等
  • 十六、有逼格的代码
  • 十七、单元测试
  • 十八、APP审核

必备知识架构

  1. CertificateSigningRequest:本地公钥。
  2. p12:本地私钥,可以导入到其他电脑。
  3. 证书:内容是公钥或私钥,由其他机构对其签名组成的数据包。
  4. Entitlements:包含了 App 权限开关列表。
  5. Provisioning Profile:包含了 证书 / Entitlements 等数据,并由苹果后台私钥签名的数据包。

iOS App 签名的原理

概览

四种方式安装一个APP的方式

  1. 从 AppStore 下载
  2. 开发 App 时可以直接把开发中的应用安装进手机进行调试。
  3. In-House 企业内部分发,可以直接安装企业证书签名后的 APP。
  4. AD-Hoc 相当于企业分发的限制版,限制安装设备数量,较少用。

区别:

App Store 下载的是Apple后台用私钥A对其进行重签名,

而其他的是用Xcode配置的对应该证书的本地私钥L对app进行签名。

一、App Store 下载的签名机制(最简单的签名)

当App 提交审核通过后,Apple会对App重签名,所以从App Store下载的app都是苹果的官方签名。

流程如下:

APP签名机制1

1、Apple 官方有自己固定的一对公钥和私钥,私钥A存在Apple后台,公钥A存在iOS设备

2、app审核通过后,Apple后台用私钥A对其进行重签名

3、app下载到iOS设备后,iOS设备内置的公钥A会对app的签名进行验证

4、如果验证通过,则可运行,否则不能

当然除了这个方式,还有一下三种方式安装一个app:

  1. 开发时,直接通过USB将应用安装到手机进行调试;
  2. In-House 企业内部分发,可直接安装企业证书签名的App;
  3. Ad-Hoc 相当于企业分发的限制版,限制安装设备数量。

①、KeyChain 里的“从证书颁发机构请求证书”,本地生成一对公私钥。(其中:公钥L是保存的CertificateSigningRequest(CSR),而私钥L保存在电脑本地,可以导出为p12)

附:Apple 官方也有自己固定的一对公钥和私钥,私钥A存在Apple后台,公钥A内置在iOS设备

(因为这里的这个私钥是本地Mac持有,所以团队开发时,可在KeyChain导出私钥,存为.p12文件,其他Mac即可导入这个私钥)

2、在Member Center把CertificateSigningRequest上传到苹果后台生成证书(即上传之后Apple后台用Apple 官方自己固定的私钥A对我们上传的公钥L进行签名,将得到的签名+公钥L打包起来,称为证书),下载到本地。

Apple 官方有自己固定的一对公钥和私钥,私钥A存在Apple后台,公钥A内置在iOS设备

3、在Member Center配置AppID、添加设备UUID/Entitlements, 生成app对应的 Provisioning Profile 文件,并下载到本地
4、在XCode中配置好证书,打包编译时,用Xcode配置的对应该证书的本地私钥L对app进行签名,并把 Provisioning Profile 文件一起打包进App去(文件名为 embedded.mobileprovision)。这里对App的签名数据分两部分,Mach-O 可执行文件把签名直接写进这个文件,其他资源文件则保存在_CodeSignature目录下

5、安装APP到手机上时,iOS设备内置的公钥A(指Apple官方自己固定的公钥)对embedded.mobileprovision的数字签名进行验证,同时对里面的证书的签名也会验证
如果验证通过,确保了embedded.mobileprovision的数据是苹果授权后,再取出里面数据做各种验证,包括公钥L对app签名进行验证,验证设备ID,AppID,权限开关。

iOS-常见三种加密(MD5、非对称加密,对称加密)

类微信体验

[toc]

类微信体验

前言

微信:

1、IM部分

2、发现(如”看一看”功能)

IM部分:

数据库:从数据库中取+新记录保存到数据库中

问题:

1、给人同步的感觉

2、不是每个调用失败,都要提示给用户

错误的请求,不弹toast

End

[toc]

Flutter 与原生相关

一、使用与原生有关的三方库的注意点

1、trying to create a view with an unregistered type, unregistered view type

使用三方库的时候,如果该库与原生有关。如webview插件、百度地图插件等。如果直接使用,不对工程设置的话,则会报trying to create a view with an unregistered type, unregistered view type:

解决方案:在info.plist加入

1
<key>io.flutter.embedded_views_preview</key>

原因:

2、启动的时候使用rootBundle.loadString加载本地配置json\百度地图,报错:Unhandled Exception: Null check operator used on a null value

启动的时候使用rootBundle.loadString 加载本地配置json,但是直接加载会报错:
[ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Null check operator used on a null value

解决办法:加载本地资源以前增加一行代码:

WidgetsFlutterBinding.ensureInitialized(); //解决加载json错误

例子:

1
2
3
4
5
void main() async {
WidgetsFlutterBinding.ensureInitialized(); //解决加载json错误
globals.config = jsonDecode(await rootBundle.loadString(‘assets/config.json’));
runApp(MyApp());
}

二、浅析 Flutter 与 iOS 的视图桥梁 PlatformView

三、百度地图

百度地图-Flutter插件

1)iOS权限问题可参照iOS定位SDK手动部署说明
2)iOS头文件错误:

ios1.png

ios2.png

解决办法:Xcode-TARGETS-build settings-Allow Non-modular Includes In Famework Modules设置为YES。

Allow Non-modular Includes In Famework Modules

End