必备知识架构-①语言-消息转发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类族.

必备知识架构-①语言-请求拦截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内的网络请求失败率

必备知识架构-①语言

[Toc]

知识架构

iOS知识库

Android知识库

目录

1
2
3
4
5
6
7
8
9
10
11
1、为什么说Objective-C是一门动态的语言
①、概念介绍
②、Objective-c的动态性
Objective-C 可以通过Runtime 这个运行时机制,在运行时动态的添加变量、方法、类等,所以说Objective-C 是一门动态的语言。
ObjC之所以说是面向Runtime的语言,最重要的特征是其消息传递机制。
Objective-C的动态性,主要体现在3个方面:动态类型、动态绑定、动态载入。
>
2、消息转发
3、为什么说 Objective-C 没有真正的私有方法和私有变量
4、消息结构
>
1
2
1、浅谈runtime运行时机制
>
1
2
3
4
5
6
7
1、#import、#include、@class](#import、#include、@class
2、const 与define使用的区别
3、==、 isEqualToString、isEqual区别
4、字符串的比较
5、isMemberOfClass 和 isKindOfClass 的联系和区别
6、nil, Nil,NULL 与 NSNull 的区别
>

一、为什么说Objective-C是一门动态的语言?

< 返回目录

问:我们说的OC是动态运行时语言是什么意思?

1、之所以叫做动态,是因为必须到运行时(run time)才会做一些事情。主要是将数据类型的确定由编译时,【推迟到了运行时】。简单来说, 运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。

所以说NSString *obj = [[NSData alloc] init] 在编译时是NSString类型,在运行时是NSData类型。

2、另外,OC还可以通过Runtime 这个运行时机制,在运行时【动态的添加】变量、方法、类等。

1、Objective-C是一门”动态”的语言,动态语言是什么意思?

OC中的对象,都是用指针表示
OC中方法的调用,是基于消息机制实现

①、”动态”概念介绍

(1)动态类型语言:动态类型语言是指在运行期间才去做数据类型检查的语言,Python和Ruby就是一种典型的动态类型语言,其他的各种脚本语言如VBScript也多少属于动态类型语言。(自己概括为动态语言指的是不需要在编译时确定所有的东西,在运行时还可以动态的添加变量、方法和类,而Objective-C 可以通过Runtime 这个运行时机制,在运行时动态的添加变量、方法、类等,所以说Objective-C 是一门动态的语言

(2)静态类型语言:静态类型语言与动态类型语言刚好相反,它的数据类型是在编译其间检查的,也就是说在写程序时要声明所有变量的数据类型,C/C++是静态类型语言的典型代表,其他的静态类型语言还有C#、JAVA等。

*obj
1
2
3
4
5
6
7
8
9
10


###### ②、Objective-c的动态性
Objective-C的动态性,让程序在运行时判断其该有的行为,而不是像c等静态语言在编译构建时就确定下来。**Objective-C的动态性主要体现在3个方面:动态类型、动态绑定、动态载入**。

> 以下内容来自:[OC是动态运行时语言是什么意思?什么是动态识别,动态绑定?](https://www.cnblogs.com/dxb123456/p/5525343.html)
>
* 1.动态类型:如id类型。
>
简单点的如id类型在编译器编译的时候是不能被识别的,要等到运行时(run time),即程序运行的时候才会根据语境来识别。

id obj = someInstance;
if ([obj isKindOfClass:someClass]) {
someClass classSpecifiedInstance = (someClass )obj;
}

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

* 2.动态绑定:
>在OC中,其实是没有函数的概念的,我们叫“消息机制”,所谓的函数调用就是给对象发送一条消息。
**OC可以先跳过编译,在运行时才决定要调用什么方法,需要传什么参数进去,这就是动态绑定**。要实现他就必须用SEL变量绑定一个方法。最终形成的这个SEL变量就代表一个方法的引用。(这里要注意一点:SEL并不是C里面的函数指针,虽然很像,但真心不是函数指针。SEL变量只是一个整数,他是该方法的ID,@selector()就是取类方法的编号。以前的函数调用,是根据函数名,也就是 字符串去查找函数体。但现在,我们是根据一个ID整数来查找方法,整数的查找字自然要比字符串的查找快得多!所以,动态绑定的特定不仅方便,而且效率更高。)

* 3.动态载入。

>根据需求加载所需要的资源,这点很容易理解,对于iOS开发来说,基本就是根据不同的机型做适配。最经典的例子就是在Retina设备上加载@2x的图片,而在老一些的普通屏设备上加载原图。




笔试题:对于语句`NSString *obj = [[NSData alloc] init];` obj在编译时和运行时分别是什么类型的对象?

> 答:编译时是NSString的类型;运行时是NSData类型的对象。



#### 2、怎么理解Objective-C"运行时"

基于Runtime的消息传递机制

**ObjC之所以说是面向Runtime的语言,最重要的特征是其消息传递机制。
什么是消息传递?消息传递不就和C语言的函数调用一个意思么?**

>在C语言中,我们调用函数时,必须先声明函数(或者自上而下),而实际上,声明函数就是获取函数地址,调用函数就是直接跳到地址执行,代码在被编译器解析、优化后,便是成为一堆汇编代码,然后连接各种库,完了生成可执行的代码(即是静态的)。
>
>在ObjC中,首先要搞清楚为什么不用Function Call 而用 Messaging 呢?一般调用函数(方法),讲的就是object对象,
>
>事情证明,在编译阶段,oc可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错,而c语言在编译阶段或报错

######



# 1,给分类添加属性

OC分类不能添加属性,如果你在分类中添加了属性,编译器就会报警告。(记得导入 #import <objc/runtime.h>)

// UIButton+CJMoreProperty.h

![runtime添加属性](1语言/runtime_add1.png)

// UIButton+CJMoreProperty.m
![runtime添加属性](1语言/runtime_add2.png)



为分类添加属性,就用运用到OC的运行时来解决。

```csharp
// 设置关联
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
// 通过关联获取属性
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);

runtime添加属性

2、消息转发

ObjC之所以说是面向Runtime的语言,最重要的特征是其消息传递机制

1
2
3
4
5
6
7
8
Runtime的特性主要是消息(方法)传递,如果消息(方法)在对象中找不到,就进行转发。
1.1系统首先找到消息的接收对象,然后通过对象的isa找到它的类。
1.2在它的类中查找method_list,是否有selector方法。
1.3没有则查找父类的method_list。
1.4找到对应的method,执行它的IMP。
1.5转发IMP的return值。

如果找遍了父类,仍没找到方法,则报错。当然我们也可以通过消息转发,去其他类中寻找。

延展:消息转发机制

因为在运行期还可以继续向类中添加方法,所以编译器在编译时还无法确定类中是否有某个方法的实现。对于类无法处理一个消息就会触发消息转发机制。

消息转发步骤(如果前一套方案实现后一套方法就不会执行)

内容来自:轻松学习之 Objective-C消息转发

方案一:首先,系统会调用resolveInstanceMethod(当然,如果这个方法是一个类方法,就会调用resolveClassMethod)让你自己为这个方法增加实现

1
2
3
4
5
6
7
8
9
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(run)) {
class_addMethod(self, sel, (IMP)run, @"v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}

+ (BOOL)resolveClassMethod:(SEL)sel

方案二:如果不去对方案一的resolveInstanceMethod做任何处理,直接调用父类方法。可以看到,系统已经来到了forwardingTargetForSelector方法,这个方法返回你需要转发消息的对象。

1
2
3
4
//返回你需要转发消息的对象
- (id)forwardingTargetForSelector:(SEL)aSelector {
return [[Car alloc] init];
}

方案三:如果如果我们也不实现forwardingTargetForSelector,系统就会调用方案三的两个方法methodSignatureForSelector和forwardInvocation。其中methodSignatureForSelector用来生成方法签名,这个签名就是给forwardInvocation中的参数NSInvocation调用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//生成方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSString *sel = NSStringFromSelector(selector);
//判断你要转发的SEL
if ([sel isEqualToString:@"run"]) {
//为你的转发方法手动生成签名
return [NSMethodSignature signatureWithObjCType:@"v@:"];
}
return [super methodSignatureForSelector:selector];
}

// 附:关于生成签名的类型"v@:"解释一下。每一个方法会默认隐藏两个参数,self、_cmd,self代表方法调用者,_cmd代表这个方法的SEL,签名类型就是用来描述这个方法的返回值、参数的,v代表返回值为void,@表示self,:表示_cmd。


- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = [anInvocation selector];
//新建需要转发消息的对象
Car *car = [[Car alloc] init];
if ([car respondsToSelector:selector]) {
//唤醒这个方法
[invocation invokeWithTarget:car];
}
}

消息转发分为两大阶段:

“动态方法解析”:先征询接收者,所属的类,能否动态添加方法,来处理这个消息,若可以则结束,如不能则继续往下走

“完整的消息转发机制”:
请接收者看看有没其他对象能处理这条消息,若有,就把这个消息转发给那个对象然后结束
运行时系统会把与消息有关细节全部封装到NSInvocation 对象中,再给对象最后一次机会,令其设法解决当前还未处理的这条消息

其他文章:NSInvocation简单使用

在 iOS中可以直接调用 某个对象的消息 方式有2中
一种是performSelector:withObject:
再一种就是NSInvocation
第一种方式比较简单,能完成简单的调用。但是对于>2个的参数或者有返回值的处理,那就需要做些额外工作才能搞定。那么在这种情况下,我们就可以使用NSInvocation来进行这些相对复杂的操作。
NSInvocation可以处理参数、返回值。会java的人都知道凡是操作,其实NSInvocation就相当于反射操作。

1
2
3
4
5
6
7
8
9
10
11
//如果此消息有参数需要传入,那么就需要按照如下方法进行参数设置,需要注意的是,atIndex的下标必须从2开始。原因为:0 1 两个参数已经被target 和selector占用
if (strcmp(retType, @encode(BOOL)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:&params atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
BOOL result = 0;
[invocation getReturnValue:&result];
return @(result);
}

3、为什么说 Objective-C 没有真正的私有方法和私有变量

以下内容摘自:为什么说 Objective-C 没有真正的私有方法和私有变量

私有的定义:私有是指只能够在本类内部使用或访问,但是不能在类的外部被访问。

在 Objective-C 中,对象调用方法是以发送消息的形式实现的。所有方法的调用最终都会转化为发送消息的形式,原型如下:

id objc_msgSend(id self, SEL op, …)

Objective-C 为什么能够实现访问「私有方法」呢?其实这跟 Objective-C 语言的动态特性有密切的关系,对象在运行的时候才会去查找方法。Objective-C 对象有一个 isa 指针指向其父类,在向该实例发送消息的时候,若它自己不能识别回到父类中去查找该消息。

综上,OC中其实并无真正意义上的的私有方法和私有属性。但是在实际使用中,我们应遵守规则,不能调用的方法,不调用。

4、消息结构

Objective-C 使用的是 “消息结构” 并非 “函数调用”。(使用消息结构的的语言,其运行时所应执行的代码由运行期决定;而使用函数调用的语言,则由编译器决定)

以下内容摘自:OC消息机制和动态运行时

1
2
3
4
5
6
7
8
9
10
11
12
13
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; //协议链表
}

二、Runtime

< 返回目录

Runtime 4种用法

1
2
3
4
1)替换系统方法,[数组越界苹果就会直接crash](http://blog.csdn.net/u012103785/article/details/50817876)
2)字典转model,
3)归档, 
4)万能控制器跳转

你会遇到的runtime面试题(详)

OBJC_ASSOCIATION_ASSIGN造成的崩溃

objc_setAssociatedObject 关联对象的学习
CJKeyboardAvoidingTableView增加cjKeyboardAvoidingOffset属性的时候,使用OBJC_ASSOCIATION_ASSIGN而造成崩溃,参考FDTemplateLayoutCell源码,改成使用OBJC_ASSOCIATION_RETAIN,问题解决。原因是

详情查询:objc_setAssociatedObject 关联对象的学习

1、浅谈runtime运行时机制

浅谈runtime运行时机制02-runtime简单使用

OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法。利用runtime机制让我们可以在程序运行时动态修改类、对象中的所有属性、方法。

iOS运行时初探-使用运行时机制向Category中添加属性

在一般情况下,我们是不能向Category中添加属性的,只能添加方法,但有些情况向,我们确实需要向Category中添加属性。而且很多系统的API也有一些在Category添加属性的情况,例如我们属性的UITableView的section和row属性,就是定义在一个名为NSIndexPath的分类里的,如下:
使用运行时机制向Category中添加属性
那这到底是怎么实现的呢?

+load +initialize的区别

\1. load方法只会调用一次

\2. +(void)load方法:程序一运行就会把所有的类加载进内存,调用这个方法

(表叙:只要程序一运行,就会调用类的load方法,目的:把这个类加载进内存)

\3. load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用,所以没有被引用进项目,就不会调用load方法,但即使类文件只被引用进来,而没有使用,那么initialize也不会被调用

  • +load方法的调用是在main() 函数之前。load 方法内部一般用来实现 Method Swizzle(面向切面AOP变成)

    调用main() 函数之前的什么时候?答:+load方法会再这个类或者其分类被添加到运行时的时候调用。

    附:所有类的 load 方法都会被调用,先调用父类、再调用子类,多个分类会按照Compile Sources 顺序加载。

  • initialize一般在main()函数之后。Initialize方法一般用来初始化全局变量或者静态变量。

    问是在main() 函数之后的什么时候?答:在第一次主动使用当前类的时候。

    附:initialize一般在main()函数之后的情况是什么?A类的 load 方法中调用 B 类的类方法,导致在调用A的Load 方法之前,会先调用一下B类的initialize 方法,但是B类的load 方法还是按照 Compile Source 顺序进行加载

    附2:Initialize 方法会被覆盖,子类父类分类中只会执行一个

  • load和initialize都不能主动调用。

  • initialize不是init,initialize在程序运行过程中,它会在你程序中每个类调用仅一次initialize。这个调用的时间发生在你的类接收到消息之前,但是在它的父类接收到initialize之后。
    init则是你手动初始化几次就调用几次,和普通方法一样

三、KVC与KVO

1、KVO

KVO的概述与使用 内容如下

一、概述

KVO,即键值观察者(key-value-observing),它提供一种观察者的机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。

二、KVO原理/本质

Aspect 的实现很想 KVO的本质:详见 Aspect 与 消息转发

KVO是使用isa-swizzling的技术实现的:当某个类的对象第一次被观察时,系统在运行时会通过Runtime动态创建一个该类的派生类,并将这个对象的isa指针指向这个派生类,派生类的isa指针指向原来的类,这样修改对象a的属性即对象a调用set方法就会变成是调用的是NSKVONotifying_A这个类的set方法。

随后在该派生类中重写了该对象的setter方法,并且在setter方法中实现了通知的机制。

派生类重写了class方法,以“欺骗”外部调用者他就是原先那个类。系统将这个类的isa指针指向新的派生类,因此该对象也就是新的派生类的对象了。因而该对象调用setter就会调用重写的setter,从而激活键值通知机制。此外派生类还重写了delloc方法来释放资源。

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
> #pragma mark - 注册观察者 - 函数式编程
> - (void)cjl_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath handleBlock:(LGKVOBlock)block {
> ......
> //2、动态生成子类
> Class newClass = [self createChildClassWithKeyPath:keyPath];
> object_setClass(self, newClass); // 将self的isa指向由原有类改为指向创建出来的派生类/中间类
>
> // 给子类NSKVONotifying_A添加方法一个名为setterSel的方法,这个方法的实现是cjl_setter。
> SEL setterSel = NSSelectorFromString(setterForGetter(keyPath));
> Method method = class_getInstanceMethod([self class], setterSel);
> const char *type = method_getTypeEncoding(method);
> class_addMethod(newClass, setterSel, (IMP)cjl_setter, type);
> }
>
> static void cjl_setter(id self, SEL _cmd, id newValue){
> ......
> }
>
>
>
> #pragma mark - 动态生成子类
> - (Class)createChildClass {
> // 动态创建子类
> NSString *oldClassName = NSStringFromClass([self class]); //获取原本的类名
> NSString *newClassName = [NSString stringWithFormat:@"NSKVONotifying_%@", oldClassName];//拼接新的类名
> Class newClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
> objc_registerClassPair(newClass);
> // 给子类NSKVONotifying_A添加方法一个名为class的方法,这个方法的实现是cjl_class。
> SEL classSel = @selector(class);
> Method classMethod = class_getInstanceMethod([self class], classSel);
> const char *classType = method_getTypeEncoding(classMethod);
> class_addMethod(newClass, classSel, (IMP)cjl_class, classType);
>
> return newClass;
> }
>
> Class cjl_class(id self, SEL _cmd){
> return class_getSuperclass(object_getClass(self));//通过[self class]获取会造成死循环
> }
>

以上实现参考自:iOS-底层原理 23:KVO 底层原理 自定义KVOiOS 探究 OC对象、isa指针及KVO实现原理

其他参考:KVO原理及自定义KVO

答. 当一个对象使用了KVO监听,iOS系统会修改这个对象的isa指针,改为指向一个全新的通过Runtime动态创建的子类,子类拥有自己的set方法实现,set方法实现内部会顺序调用willChangeValueForKey方法、原来的setter方法实现、didChangeValueForKey方法,而didChangeValueForKey方法内部又会调用监听器的observeValueForKeyPath:ofObject:change:context:监听方法。

如何手动触发KVO 答. 被监听的属性的值被修改时,就会自动触发KVO。如果想要手动触发KVO,则需要我们自己调用willChangeValueForKey和didChangeValueForKey方法即可在不改变属性值的情况下手动触发KVO,并且这两个方法缺一不可。

isa指针

isa是结构体指针,只要是OC对象都有isa

  1. 实例对象的isa指向它的类对象。
  2. 类对象的isa指向元类对象(元类是类的类,它存储了类的方法和属性信息。为什么要设计元类,为了复用消息传递功能)。

当调用对象方法时,实例对象内存并没有包含对象方法,而是通过它内部的isa指针找到它的类对象,从而在类对象中找到对象方法的实现进行调用;

当调用类方法时,类对象并没有类方法的信息,而是通过类对象的isa找到元类对象,最后找到类方法的实现进行调用

  1. instance调用对象方法的轨迹:isa找到class,方法不存在,就通过superclass找父类
  2. class调用类方法的轨迹:isa找meta-class,方法不存在,就通过superclass找父类

isa&superclass

三、使用方法

系统框架已经支持KVO,所以程序员在使用的时候非常简单。

  1. 注册,指定被观察者的属性,
  2. 实现回调方法
  3. 移除观察

四,实例:

假设一个场景,股票的价格显示在当前屏幕上,当股票价格更改的时候,实时显示更新其价格。

2、KVC

KVC 就是键值编码(key-value-coding)

KVC 的主要作用:

(1)通过键值路径为对象的属性赋值。主要是可以为私有的属性赋值。

(2)通过键值路径获取属性的值。主要是可以通过key获得私有属性的值。

1
2
3
4
5
6
7
8
9
10
11
12
// valueForKeyPath:(多级访问,不仅可以对当前对象属性进行赋值,也可以对“深层”对象的属性进行取值)
Person *person1 = [[Person alloc] init];
person1.name = @"李四";
person1.car.name = @"BMW";
NSString *carName = [person valueForKeyPath:@"car.name"];


// 当集合中元素类型一致,可以通过KVC的getter方法valueForKeyPath:来获取所有集合元素中的属性,生成一个新的元素属性集合。(这里的集合一般为NSArray,NSSet,不包括NSDictionary)
NSArray *personArray = [NSArray arrayWithObjects:person1, nil];

NSArray *personNameArray = [personArray valueForKeyPath:@"name"];
NSArray *carNameArray = [personArray valueForKeyPath:@"car.name"];

其他参考资料:

四、Objective-C和C++

< 返回目录

1、Objective-C与C++之间的差异

最大的区别就是Objective C的成员函数调用是基于动态绑定的,类本身并没有限制对象可以有什么函数。相对于C++类会规定好成员有什么函数。这使得Objective C的灵活性很大,但是执行效率也很低。

2、Object-c的类可以多重继承么?

答: Object-c的类不可以多重继承;

面向对象(OOP)

面向对象的三个特性:封装、继承和多态(没有继承就没有多态)。

所谓封装,也就是把客观事物封装成抽象的类;

所谓多态就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。

没有继承就没有多态。代码的体现:父类类型的指针指向子类对象。

没使用多态前:

1
2
3
4
5
6
7
8
9
10
11
12
Person.m
- (void)printWithBlack:(Printer *)printer {
[printer print];
}
- (void)printWithColor:(Printer *)printer {
[printer print];
}



[person printWithBlack:blackPrint]; //调用黑白打印机
[person printWithColor:colorPrint]; //调用彩色打印机

使用多态后:

1
2
3
4
5
6
7
8
9
10
11
12
Person.m
- (void)print:(Printer *)printer {
[printer print];
}


// 使用时候:
Printer *p1 = [[ColorPrinter alloc] init];
Printer *p2 = [[BlackPrinter alloc] init];

[person doPrint:p1];
[person doPrint:p2];

常见笔试/面试题

< 返回目录

1、#import、#include、@class

可参考:#import、#include、@class、#import<>和#import””的区别

1、#import介绍:
In the C language, the #include pre-compile directive always causes a file’s contents to be inserted into the source. at that point, Objective-C has the equivalent #import directive except each file is included only once per compilation unit, obviating(去除) the need for include guards.

即在C语言中,#include预编译指令经常引起一个文件的被重复引用。基于这一点,OC有一个等价的预编译指令#import来防止文件重复导入。所以OC给出来的新的预处理指令import是防止重复导入,避免程序员去书写头文件卫士,降低程序员的负担。

2、#import和#include的异同点:

①#include是C语言中的,需要条件编译语句控制重复包含问题。

②在Objective-C中,#import是OC中对#include的改进版本,#import使得我们不需要写条件编译语句就可以确保引用的文件只会被包含一次,不会陷入递归版包含的问题。即import与#include比较的优点:会自动防止重复导入,使得不会引起交叉编译

#import不会引起交叉编译的问题。因为在Objective-C中会存在C/C++和Object-C混编的问题,如果用#include引入头文件,会导致交叉编译。

举例:三个文件①文件A.h、②文件B.h、③文件C.h。其中文件C.h需要引入A.h、B.h,而文件B.h也需要引入文件A.h,这样在C.h中就重复引用了A.h两次,使用#import可以进行优化

3、#import和#include的相同点:

①都可以用在OC程序中起到导入文件的作用

②同样的 包含系统文件都是<>,是包本地文件都用””
例如:系统文件#import<Foundation/Foundation.h>, #include<stdio.h>
​ 本地文件#import”test.h”, #include”test.h”

到底该如何选择使用呢
一般来说,你包含的是C语言中的文件就用#include,你用的是OC中的文件就用#import

4、#import与@class二者的区别

#import会链入该头文件的全部信息,包括实体变量和方法等;而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑。在头文件中, 一般只需要知道被引用的类的名称就可以了。不需要知道其内部的实体变量和方法,所以在头文件中一般使用@class来声明这个名称是类的名称。 而在实现类里面,因为会用到这个引用类的内部的实体变量和方法,所以需要使用#import来包含这个被引用类的头文件。

附:@class能解决循环包含的问题:当两个类文件有循环依赖关系 (A引用B,B引用A)时,需要用@class

5、import<> 和 import””
①<> : 引用系统文件,它用于对系统自带的头文件的引用,编译器会在系统文件目录下去查找该文件.

②””: 用户自定义的文件用双引号引用,编译器首先会在用户目录下查找,然后到安装目录中查

4、字符串的比较

字符串compare;
字符串compare的比较是一个字符串一个字符串比较的,所以才会出现1814比860小的错误情况。

5、isMemberOfClass 和 isKindOfClass 的联系和区别

  • 联系:两者都能检测一个对象是不是某个类的成员

  • 区别:isKindOfClass不仅能确定一个对象是不是某个类(即不算父类)的成员,也能确定一个对象是否是派生自该类的类的成员,而isMemberOfClass只能做到前者.

a.为什么基本类型和C数据类型的修饰用assign?

因为基本数据类型不是对象,不需要引用计数,所以使用assign(附:在内存中创建和使用后,在一个方法体结束后就被删除。)。

iOS NS/CG/CF前缀代表什么意思

NS/CG/CF前缀代表什么意思。

回答:NS是前缀,代表它是来自Cocoa Foundation框架,这个框架是OC的基础库

UI—UIKit框架,UI库
CF—Core Foundation框架
NS—Cocoa Foundation框架,OC的基础库。
CG—CoreGraphics.frameworks框架,用于渲染的库。
CA—CoreAnimation.frameworks框架

END

< 返回目录

1
2


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

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,参数等。将消息转发给多个对象

知识架构–即时通信

以下内容摘自:开发即时通信是选择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

[toc]

Flutter MaterialApp

Flutter MaterialApp的initialRoute属性和home属性的区别

1、home 是应用程序默认的路由小部件,如果指定了 home,那么 route 中就不能包含 /,home 会取代 /。
2、除非指定了 initialRoute(会先执行home,再执行initialRoute,在initialRoute页返回会回到home页),否则应用程序会首先显示 home 对应的小部件,如果 initialRoute 不能正常显示,也会显示 home。
使用场景:
若APP启动时需要根据是否登录来决定首先显示的页面,可在路由监听中判断 token,若未登录,则 initialRoute 设置登录页对应的别名,则会打开登录页。

End