c++中operator的重载

两种函数允许编译器进行隐士类型转换

  1. 单一参数调用成功的constructors
  2. 隐士转换操作符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Ration {
public:
//1 隐士构造函数
Ration(int a) {

}
//2 隐士类型转换函数
operator double() const {
return 5.0f;
}
};


int main()
{
Ration ration(1);
cout << ration << endl;
return 0;
}

如何阻止构造函数发生不期望的隐士类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Ration {
public:
explicit Ration(int a) {

}
};

int main()
{
Ration ration(1);

//构造函数声明为explicit 阻止隐士类型转换 ration == 2 会编译报错
if (ration == 2) {

}
return 0;
}

重载操作符可以在globe scope或者class scope中进行,但是切记不要重载 && || 操作符, 原因是改变了短路运算的语义,变成函数调用。

这里还有一个细节,c++中并未明确定义函数调用动作中各参数的评估顺序,而短路运算是从左到右的。

###重载(),当重载 () 时,不是创造了一种新的调用函数的方式,相反地,这是创建一个可以传递任意数目参数的运算符函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Ration {
public:
explicit Ration(int a) {

}

int operator() (int a , int b, int c)
{
return 10;
}

int operator() (int a , int b)
{
return 5;
}
};

int main()
{
Ration ration(1);
ration(1, 2, 3);
ration(1, 3);
return 0;
}

C++ 构造函数

C++ 构造函数

  1. 默认构造
  2. copy构造
  3. 移动构造
  4. operator= 赋值函数

说明

  1. 对于赋值函数和copy构造函数来说,直接实现实现const的版本即可,如果参数不是const,会调用const,只有实现了非const的参数,才会调用
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
class Person
{
public:
Person() {
cout << "Person()" << endl;
};
~Person() {
cout << "~Person()" << endl;
};

Person(const Person& Person) {
cout << "Person(Person& Person)" << endl;
}

Person(Person&& Person) {
cout << "Person(Person&& Person)" << endl;
}

Person& operator=(const Person&)
{
cout << "operator=(const Person&)" << endl;
return *this;
}
};

  1. 对于构造函数,copy构造函数和移动构造函数来说,只要实现其中任何一个,剩余其他的,编译器就不会帮助生成。
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
class Person
{
public:
// Person() {
// cout << "Person()" << endl;
// };
~Person() {
cout << "~Person()" << endl;
};

// Person(const Person& Person) {
// cout << "Person(Person& Person)" << endl;
// }

Person(Person&& Person) {
cout << "Person(Person&& Person)" << endl;
}

Person& operator=(const Person&)
{
cout << "operator=(const Person&)" << endl;
return *this;
}
};

int main()
{
Person person; //编译报错,找不到匹配的构造函数
return 0;

}

iOS运行时消息转发

最近读了一遍apple 文档,对于iOS运行时消息转发机制发现有些细节还是理解有所偏差,写此文章加深理解

iOS 方法调用探讨

这个话题还要从OC是一门动态语言说起,OC的动态性体现在编译和链接期,并没有直接绑定函数调用关系,编译器将方法调用转成objc_msgSend(receiver, selector, arg1, arg2, …)方法这种方式

在运行时,通过isa对象通过从子类到父类的方法查找,找到具体的函数入口进行调用,这其中还包括cache等机制,这里不在赘述,这次主要讨论的是Dynamic Method Resolution和Message Forwarding

如果调用的Seletor在类的方法列表中找不到,以实例对象的方法为例,将进入如下流程:

image.png

主要流程可以总结为,先进入消息动态处理流程,再进入消息转发流程

1.消息动态处理流程:resloveInstanceMethod中可以通过class_addMethod为此对象动态添加方法,这样就使该对象正常响应此方法

1
2
3
4
5
6
7
8
9
10
+ (BOOL)resolveInstanceMethod:(SEL)aSEL{

if (aSEL == @selector(notFoundFunctiion)) {

class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}

return [super resolveInstanceMethod:aSEL];
}

​ 需要注意的是,不仅[self notFoundFunctiion]会触发这里,在

1
[self respondsToSelector:@selector(notFoundFunctiion)]

​ 这句代码中,同样会触发消息动态处理流程,这和后面讲的消息转发有很大区别。

2.如果上面的消息处理流程返回NO,那么就会进入消息转发流程

可以将此消息转发给另外一个对象进行执行。首先进入的是forwardingTargetForSelector,这里可以返回一个可以响应此消息的对象,如果仍然返回nil,就会进入methodSignatureForSelector和forwardInvocation的流程,其实这两种方式本质的意义相同,只不过一个是通过另外的对象selector调用,一个是通过invocation的方式调用,但是要注意,这里的selector对于原来的对象来讲,respondsToSelector返回为NO

参考文章:

Objective-C Runtime Programming Guide

iOSCALayer的presentLayer那点事

最近开发过程中遇到的一个问题,要求在一个View的动画过程中,获取它的实时位置,这里讲如何解决

动画过程中,如何获取它的实时位置

这个话题涉及对CALayer的理解和使用

CALayer内部系统维护着三种LayerTree,分别为modelLayer,presentLayer和renderLayer,renderLayer为系统渲染时内部维护,对于开发者来讲是透明不可见的,这里指讨论modelLayer和presentLayer

  • modelLayer 实际上就是通常操作的layer,我们可以修改这个layer的各种属性,可以理解这个layer只保存数据
  • presentLayer 是当使用CoreAnimation做动画时,每一帧动的位置都可以从这个layer中读取到,我们可以通过下面的代码来测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
UIView* view = [[UIView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];

view.backgroundColor = [UIColor redColor];

[self.view addSubview:view];

NSLog(@"model Layeer = %@", NSStringFromCGRect([view.layer modelLayer].frame));

NSLog(@"present Layeer = %@", NSStringFromCGRect([view.layer presentationLayer].frame));

[UIView animateWithDuration:10 animations:^{

view.frame = CGRectMake(200, 100, 100, 100);

} completion:^(BOOL finished) {
}];

[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {

NSLog(@"model Layeer = %@", NSStringFromCGRect([view.layer modelLayer].frame));
NSLog(@"present Layeer = %@", NSStringFromCGRect([view.layer presentationLayer].frame));
}];

通过控制台日志可以分析看到,presentLayer在没有做动画的时候是nil,在有动画时才有数值,并且是实时的view的位置

Mac环境下安装Ruby

使用rvm来安装ruby

step1

1
$ curl -L get.rvm.io | bash -s stable  

step2

1
2
3
$ source ~/.bashrc $ source ~/.bash_profile$ rvm -v  
$ source ~/.bash_profile
$ rvm -v

step3

1
$ rvm list known  

step4

1
$ rvm install 2.2.0  

如果Step4失败,可以进行如下尝试

1
2
3
4
5
6
sudo chown -R $(whoami):admin /usr/local
cd /usr/local
git remote set-url origin git://mirrors.ustc.edu.cn/brew.git
brew update
sudo chown root:wheel /usr/local
rvm install 2.2.0

iOS11模拟器运行OpenGL相关程序卡顿

最近升级Xcode9.0后发现模拟器上运行OpenGL程序非常卡顿,查了一下原因,原来是苹果的一处bug

具体的解决方案是

替换此路径下的文件用附件文件,替换此路径下文件即可

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/OpenGLES.framework/libCoreVMClient.dylib

libCoreVMClient.dylib下载链接

参考文献

苹果开发者论坛

iOS不受用户手动修改时间的影响的计时技术

iOS不受用户手动修改时间的影响的计时技术

前言

这几天的项目中,要求实现一个用户计时的功能,其中遇到了经理了几种技术方法,分别是利用NSTimer,NSDate,都有各自的问题,Timer计时不够精准,以及需要程序在后台执行,NSDate会受到用户修改系统时间,而导致计时不准,最后找到了一种完美的解决方案

解决问题

直接说最终方案

[[NSProcessInfo processInfo] systemUptime];

这个函数返回的是开机到当前的时间,秒数为单位,并且不受系统时钟的影响,在一些需要准确记录时间长度的场景里,配合Timer非常适合,解决了前沿描述的所有痛点.

参考文献

苹果接口文档

iOS手势事件分发原理

HitTest的主要目的就是找到对于UIEvent的响应者,本文实现代码是根据apple文档描述的一种猜测实现,帮助大家理解原理

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
- (UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event{

//apple文档描述,不接受事件的情况
if (self.userInteractionEnabled == NO || self.isHidden == YES || self.alpha < 0.01) {
return nil;
}

//如果当前View包含此Point
if ([self pointInside:point withEvent:event]) {

//遍历子View,这里注意要从后往前遍历,因为后面的是越靠近用户的
for (NSInteger i=self.subviews.count-1; i>=0; i--) {

UIView* subView = [self.subviews objectAtIndex:i];

//将父View的Point转换成子View坐标系的Point
CGPoint pointInSubView = [subView convertPoint:point fromView:self];

//递归子View调用HitTest:
UIView* resultView = [subView hitTest:pointInSubView withEvent:event];

//找到了子View可以响应
if (resultView) {
return resultView;
}

}

//没有找到可以响应的子View,返回自己
return self;
}

//返回nil,告诉上一级自己无法响应此事件
return nil;
}

流程图总结

image.png

tweak心得

1 关于ssh

一般形式 ssh root@192.168.2.17或者ssh mobile@192.168.2.17

root和mobile分别为iOS上默认用户,alpine是默认密码

可以通过ssh-gen 分别在Mac和iOS上生成密钥对,然后将Mac上的公钥拷贝到手机上,这样配之后,每次ssh不会再提示输入密码

2 scp source dest

一般为

scp ~/123.txt mobile@192.168.2.17:/usr/bin

3 Makefile

可以配置手机的IP,framework,arch等参数

THEOS_DEVICE_IP = 192.168.31.202
ARCHS = armv7 arm64
TARGET = iphone:latest:8.0

iOSREGreetings_FRAMEWORKS = UIKit

4 关于bundleID

.plist中的bundle就是你想hook的程序的bundleID

tweak环境搭建

0 设置环境变量 export THEOS=/opt/theos

可以设置~/.zshrc中添加,修改后用source命令重新加载

1下载theos(~/jailbreak目录下已经下载过),放在/opt/theos下

2 下载ldid,放到/opt/theos/bin下

sudo chmod 777 /opt/theos/bin/ldid

3 配置CydiaSubstrate

在Cydia中安装CydiaSbustrate,然后scp 讲iPhone上的 /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate 拷贝到Mac /opt/theos/lib/下,并重命名为libsubstrate.dylib

并将头文件substrate.h也scp 到/opt/theos/include下

sudo /opt/theos/bin/bootstrap.sh substrate

4 将dm.pl重命名为dpkg-deb,cp到/opt/bin/

suodo chmod 777 /opt/bin/dpkg-deb

基本就搭建完成,可以练习创建工程

/opt/theos/bin/nic.pl

然后进行make package

make package install