注册
环信即时通讯云

环信即时通讯云

单聊、群聊、聊天室...
环信开发文档

环信开发文档

环信FAQ

环信FAQ

集成常见问题及答案
RTE开发者社区

RTE开发者社区

汇聚音视频领域技术干货,分享行业资讯
技术讨论区

技术讨论区

技术交流、答疑
资源下载

资源下载

收集了海量宝藏开发资源
iOS Library

iOS Library

不需要辛辛苦苦的去找轮子, 这里都有
Android Library

Android Library

不需要辛辛苦苦的去找轮子, 这里都有

千里之行,始于发心

“千里之行,始于足下”,这两句话出自《道德经·第 64 章》,每个人小时候都会被问及:长大了想做什么?想成为什么样的人?我记得喜之郎之前有一则广告:长大后我要当太空人,爷爷奶奶可高兴了... 每个孩子都梦想着自己长大能够成为警察、科学家、作家、医生.........
继续阅读 »

“千里之行,始于足下”,这两句话出自《道德经·第 64 章》,每个人小时候都会被问及:长大了想做什么?想成为什么样的人?我记得喜之郎之前有一则广告:长大后我要当太空人,爷爷奶奶可高兴了...


每个孩子都梦想着自己长大能够成为警察、科学家、作家、医生......然而,当我们长大后,又有多少人能够实现自己的愿望呢?老子在道德经中点明了踏上成功之路的方法:千里之行始于足下。再类比到学习上来,难道不是这样吗?有了学习的目标还要有行动,立即开始就是迈向成功的第一步,也就是说要 “始于足下”。


老子说的“千里之行始于足下”,的确没错,但是我认为真正的千里之行,应该始于发心,只有我们拥有做好这件事的心,即便千里之行遇到各种困难,最终我们会坚持下去,直到成功的那一刻。


在开始分享学习方法之前,我们先思考一个问题:什么是学习?大家可以在脑海中过一遍,从上学到现在工作,我们基本都在不停地学习新的知识,看起来学习不就是一种行为嘛,那到底是怎样的一种行为?


我先给学习下个定义,它分为三个过程,第一个过程是理解,第二个过程叫记忆,第三个过程叫应用。一个事情你理解了,并且过了一段时间之后你记住了,再过一段时间后能熟练应用了,这才是一个完整的学习过程。不管少了哪个环节,学习都不能持久,最后的结果就是没学习到,这就是对学习的定义。


之前也看到不少小伙伴们在群里问 :编程怎么学啊?学完后又忘了!买了直播课听老师讲的时候都能听懂,自己写就不会了!我怎么这么笨啊!是不是脑子有什么问题啊等等。其实最主要的原因就是你以为自己学明白了,理解了,但其实你并没有真正的理解,你所谓的学习只停留在学习过程中第一个环节,只有当你理解了,记住了,并且能熟练应用了,这才能称你学会了。接下来给大家分享一个我用了两年的学习方法,个人觉得挺不错的,这也是世界公认的一个高效学习方法。


费曼学习法


这个方法我还是从我老师那里听来的,费曼学习法是由理查德·费曼提出来的,1965 年的时候也获得诺贝尔物理学奖,爱因斯坦曾说过:“If you can't explain it simply, you don't understand it well enough.” 意思就是说如果不能向他人简单解释一件事,就还没有真正弄懂它,如果你想弄清楚某个知识点,那就把它解释清楚!实际上,把自己正在学的知识教给他人,也正是费曼学习法的核心理念!费曼学习法是一种以教代学的学习方式。


假如我们通过直播课或者技术书学习某个前端技术,当学完某个知识点后,我们的大脑有可能会给自己一个错觉,就是自认为我学会了,学懂了,因为你还处在学习的一个过程理解阶段,真正的学习是分为三个阶段的理解、记忆、应用。检测自己是否真正学会了的方式就是利用费曼学习法,当你在给对方阐述的过程中,如果你对其中的知识点有不理解的地方,你会产生断层,会讲不下去,会给自己讲蒙了,当你发现讲不下去的时候,就是你对知识点理解不透彻的时候,然后回去接着学,学完之后再重新整理一遍继续给别人讲,直到可以重头到尾能给被人讲的明明白白了,甚至讲到被人听明白的时候,就证明你对这个知识点学习没有问题了,这就起到了查漏补缺的作用。为了让别人听得懂,首先自己得懂。在分享前,你会在大脑重新过一遍知识点,这就加深了对知识的理解,分享时,我们需要充分调动和提取大脑中的知识,这能够加强对知识的记忆和理解。


别着急记笔记


请大家先闭眼,在脑海中回想一个情景:语文老师正在讲课,讲台下的学生们握着笔杆子不停地写来写去,生怕错过一个知识点,仿佛只有全都记下来心里才会踏实,内心会有一丝欣慰......


接下来分享的学习方法也就一句话:第一次学习的时候不要记笔记,第一次学的时候,认真听,认真看,能记住多少算多少,就这么简单。


这是为什么呢?首先第一遍记的东西可能不是重点,会记很多笔记,第二个,我们需要了解一下我们的大脑,其实我们的大脑是非常喜欢省事的,当我们把知识点记到笔记本上,此时我们的大脑会记住,这些知识都记在本子上,就不会帮我们记在大脑里,我们如何将一个东西记得深刻,只有一个办法,就是让你的大脑不安全,当大脑没有安全感的时候,让你的内心无处安放的时候,反复记两遍准能记住,所以不要事事都给自己充分的安全感,尤其是记忆这个东西,当你记在本子上,笔记特别全,等你回顾的时候,什么也想不起来,而你记得最多就是这些知识点我记在本子上了,那么这些东西什么时候才能成为自己的呢?记都记不住,等到应用的时候怎么可能灵活呢!说这些不是说不让大家记笔记,只是第一遍学习不要记笔记,一些重点知识,容易忘的东西还是需要记下来的,以便之后的复习浪费时间。


我们应该放好自己的心态,也要认清一个事实:不管学任何东西都不要指望一遍就能学会学通,至少要抱着学两遍的心态去学习,所以第一遍的时候不要记笔记,能理解多少算多少,记不住没关系,等第二遍学的时候,看自己能想起哪些知识点,什么是清晰的?什么是模糊的?什么是根本想不起来的?然后把清晰的东西验证一遍,模糊的东西再学一遍,记不住的东西标记一下,这时就需要记笔记了,对于那些模糊记不住的重点记下来,这样的话,学习就能把握一个很好的节奏,知识重点拿下了,记不住的也拿下了,那以后基本上就是忘记这些东西了,以后忘了再看看就行了,这就要比你第一次记笔记牢靠得多,深刻得多,第一次记笔记觉得哪哪都是重点,内心也不放心,恨不得全都记下来。


我们需要经常这么锻炼,你越让自己内心不安全,战战兢兢,大脑越让你记忆深刻,不断这么训练自己,你的记忆力以后会非常强大。


保持独立思考


什么才是独立思考?比如说:当我们面对同样的信息时,有些人就能产出独到的见解,令人印象深刻。在做项目开发时,有的人就能给出新颖可行的方案,我们通常将这些人归结为懂得如何思考的人。


有这样一句话:“当我们一旦融入某个群体中,那么你就会传染上他们习惯以及思维方式,做出一些荒谬绝伦却毫不自知的事情”。


我们每天接收的信息量非常多,但大多数人在看到这些信息之前就已经停止了思考,无论是看新闻,还是刷抖音短视频,获得的知识都只是碎片化信息,但是很多人都把这些碎片化信息当成了知识的全部,缺乏思考的能力,某个博主或者专家说什么就是什么。在思考面前,我们停下了前进的脚步。孰不知,我们接收到的信息都是经过加工的,甚至我们看到那些到处炫耀生活的短视频,都是其他人经过包装后,潜移默化间灌输给我们的。当我们逐渐依赖其他人给出的“答案”时,我们的独立思考能力,就会在这种思维影响下一点一点地消失了。如果我们任其发展不反抗,无异于是对自我的扼杀。一个真正有思想的人,一定是懂得如何独立思考且拥有独立人格的人,他们看待事物不会透过有色眼镜歪曲揣测。而我们应该抱着敞开心扉,拥抱多元化的态度,以一种客观的方式分析遇到的每一件事,在反复的锤炼中,也许我们会发现自己已经不知不觉拥有了独立思考的能力。


坚持不懈


坚持这件事,归根结底就是意志力的较量,谁能挺到最后,谁就是胜利者。而常常放弃,或许不是因为我们无法坚持,而是给自己的退路太多。我觉得这对于 21 世纪的年轻人来说这是世界性的一个难题,我们大多很难去坚持一件事情,但是反观父母年代的人,他们貌似跟我们这个年代的人不太一样,他们坚持一件事情好像比我们容易点。这其实是有一个深刻的原因的,说简单点就是时代变化太快了,在父母那个年代生活条件有限,做什么都不方便,过节走个亲戚,一走就是几十里路,买个东西要走很远才能到镇上,收割庄稼基本都是人工,在父母那个年代里,生活节奏很慢,看起来干什么都像似在浪费时间,但是恰恰他们很勤奋,正是因为他们的生活节奏慢,所以他们的忍耐能力增加了,所以他们成长的快,他们可以接受一切比较慢的事情,需要坚持的事情,在父母那个年代,能上学,能读书,他们会觉得特别好,他们会珍惜这样的机会。


再看看我们现在的生活,一切都变得非常快,做什么都很方便,出门有滴滴打车,饿了有美团送上门,网速提升到 5G 了,但是像学习、减肥还像父母年代那样原始,依然需要我们付出时间和精力,它并没有随着时代的发展而被赋能,也没有随着时代的进步而被简洁化。但是请看看现在的我们,现在的我们变得浮躁了,没有办法接受一切慢的事物,在这种快节奏的时代下,那些需要花时间才能厚积薄发的事情我们如何坚持下去?那这是不是我们这个时代面临最严峻的问题?


世界上一切修行的方法都可以用金刚经里的一句话来总结,叫作善护念。就是说你想做任何事情,想在任何方面取得成就,无论是事业,还是爱情,都可以用善护念来直达最后的高度。什么是善护念呢?用一句话来说就是保护好我们的初心,一个人是否能够坚持下去,取决于自己的发心,他是真正热爱并且发自内心真正想做好这件事情,他坚持的动力都是从内心中生出来的,而不是说外在强加在身上的,更不是说我们喊着去坚持,但内心不想做的事情。


华严经里释迦摩尼也说过一句话:“众生皆具如来智慧德相”。每个人心中都有一座佛,每个人都想成功,那为什么就不能坚持做好一件事情?只因妄想执著,不能证得,各种各样的诱惑蚕食着本心,以至于走着走着却忘了自己的初心。六祖慧能受五祖弘忍大师点拨的时候恍然大悟,说了五句感慨了一下:“何期自性,本自清净;何期自性,本不生灭;何期自性,本自具足;何期自性,本无动摇;何期自性,能生万法”。其中第三句“何期自性,本自具足”跟王阳明的“圣人之道,悟性自足”其实是一样的意思,虽然说一个修佛,一个修儒家,但他们最终修炼到正果的时候,真正取得成果的时候,得出的结论几乎是一样的。只要能守护好自己的初心,坚持是件很简单的事情,最终的成功自然水到渠成。


最后的话


有一首诗叫做《纽约比加州时间早三小时》,它结尾处写道:


其实每个人在自己的时区有自己的步程。不用嫉妒或嘲笑他们。


他们都在自己的时区里,你也是!


生命就是等待正确的行动时机。


所以,放轻松。


你没有落后。


你没有领先。


在命运为你安排的属于自己的时区里,一切都准时。


我很喜欢这首诗,它时刻提醒着我,每个人都有自己的时区,不必着急,未来之路,愿与君共勉!🤝


作者:法医
链接:https://juejin.cn/post/7213022838365618234
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

《最近解决的一个bug与最近蹦出的一些想法》

0、一句话概括bug的原因 项目更换了邮箱服务器,原服务器支持的账号格式在新服务器上不被支持;即发送给新服务器的账号错误。 1、最近解决的一个bug (1)bug: java程序通知阿里云邮箱服务器发送邮件失败。 异常报错信息:AuthenticationFa...
继续阅读 »

0、一句话概括bug的原因


项目更换了邮箱服务器,原服务器支持的账号格式在新服务器上不被支持;即发送给新服务器的账号错误。


1、最近解决的一个bug


(1)bug:


java程序通知阿里云邮箱服务器发送邮件失败。

异常报错信息:AuthenticationFailedException: 526 Authentication failure[0]。


(2)背景:


弃用原邮箱服务器、更换为阿里云邮箱服务器后,所有版本的项目向服务器发出的邮件请求均无响应。


(3)排错:


AuthenticationFailedException,翻译过来就是认证不通过异常;认证不通过的原因一般是:服务器错误、用户名错误、用户名密码不匹配。

阿里云官方排错参考连接:阿里邮箱如何通过SMTP程序发信

使用参数在Foxmail中配置,可成功进行SMTP发信;这一步,确定了服务器无错、用户名无错、用户名与密码匹配。

那么,哪里出了问题?

翻阅官网原文: 



经排查,SMTP服务器配置、端口没有错误;那么问题就藏在代码逻辑和参数中。

当时对代码逻辑和参数并未产生质疑:代码延用的是之前对接服务器的部分;需要变动的参数都存在了数据库,并且这些参数在Foxmail上已被验证通过。把问题甩给阿里云人工,工程师查看操作日志后确定服务器接收的账号密码出错。基于出错点,重新复盘:服务器没问题,数据库的帐号密码没问题,那就是java程序处理后并向服务器发送的账号密码出了问题!

程序拿到了正确的帐号密码,却向服务器发送了错误的。在可能出错的代码块内排查:从src文件夹代码到hutool工具类库源码一路debug,发现阿里云邮箱服务器识别不了邮件账号;同样的代码逻辑,发送给原服务器的有效账号是“tairui”,而阿里云服务器需要的是“tairui@aliyun.com”。

最终重新拼接邮件账号字符串,问题解决。


2、最近蹦出的一些想法


(1)软件工程师,是一个什么样的职业?


软件工程师,听上去就是一群建库删库、增删改查数据、开发软件的哥们。

程序员可以像创造了一个又一个世界的操盘手。这个世界的规则都由他说了算:每个对象都是这个社会中的个体,每名个体通过传递消息建立他们的父子、兄弟、恋爱关系;每名个体的本质在于其所处的社会关系,整个社会的本质又是个体间关系的总和。

程序员也仅仅是社会分工的一个角色。他是一名与一个挖水沟的工人并没有太大区别的工人,同样从事着机械性的造轮子工作,同样为社会分工的目的而劳动。


(2)如何从事这样的职业?


跳过基础入门、背八股、刷面经的步骤,假设X已经顺利入职并从事着软件开发的工作,问初入职场的X如何在这个岗位上发热?

得意识到学习能力才是终身竞争力。剔除天赋、运气的因素,剩下的能让X在职场里披荆斩棘的可控因素中,主要因素就是学习能力。

得想明白程序员需要学习的到底是什么。语言是一个工具,框架更是;框架每年都在变,语言的核心思想却贯穿始终。X至少得吃透一门编程语言的教材,形成一个系统的编程思维,以便将来使用其他语言工具时能够一通百通。


(3)不断解决bug的感觉,就像精神鸦片,给平平无奇的工作添加了欢乐。


在毕业后工作满一年的时间跨度里,常常因为解决了一个问题而兴奋,不断地收获工作中的小确幸。

希望每一名劳动者能够在岗位上找到兴趣点,这就像是:在一个六年级毕业的暑假,午后阳光炙热,你怀揣着印着周杰伦半身像的雪碧,一路小跑到大伯家,按下乳白色主机和大屁股显示屏的开关键,伴随着XP系统的开机声急促地呼吸,在IE浏览器上输入www.4399.com;此刻,渴求的眼神、激动的指关节和涌上脸颊的绯红,让你忘记阳光的毒辣、酸胀的肌肉和在气管上切割的空气。

.

.

.

工作满一周年记

20230610 19:10


作者:水煮鸡蛋不加蛋
链接:https://juejin.cn/post/7242911173724930105
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

在 SwiftUI 中创建一个环形 Slider

iOS
前言 Slider 控件是一种允许用户从一系列值中选择一个值的 UI 控件。在 SwiftUI 中,它通常呈现为直线上的拇指选择器。有时将这种类型的选择器呈现为一个圆圈,拇指绕着圆周移动可能会更好。本文介绍如何在 SwiftUI 中定义一个环形的 Slider...
继续阅读 »


前言


Slider 控件是一种允许用户从一系列值中选择一个值的 UI 控件。在 SwiftUI 中,它通常呈现为直线上的拇指选择器。有时将这种类型的选择器呈现为一个圆圈,拇指绕着圆周移动可能会更好。本文介绍如何在 SwiftUI 中定义一个环形的 Slider。


初始化环形轮廓


ZStack中的三个圆环开始。一个灰色的圆环代表滑块的路径轮廓,一个淡红色的圆弧代表沿着圆环的进度,一个圆圈代表当前光标或拇指的位置。将滑块的范围设置为0.0到1.0,并硬编码一个直径和一个的当前位置进度 - 0.33。

struct CircularSliderView1: View {
let progress = 0.33
let ringDiameter = 300.0

private var rotationAngle: Angle {
return Angle(degrees: (360.0 * progress))
}

var body: some View {
VStack {
ZStack {
Circle()
.stroke(Color(hue: 0.0, saturation: 0.0, brightness: 0.9), lineWidth: 20.0)
Circle()
.trim(from: 0, to: progress)
.stroke(Color(hue: 0.0, saturation: 0.5, brightness: 0.9),
style: StrokeStyle(lineWidth: 20.0, lineCap: .round)
)
.rotationEffect(Angle(degrees: -90))
Circle()
.fill(Color.white)
.frame(width: 21, height: 21)
.offset(y: -ringDiameter / 2.0)
.rotationEffect(rotationAngle)
}
.frame(width: ringDiameter, height: ringDiameter)

Spacer()
}
.padding(80)
}
}



将进度值和拇指位置绑定


将进度变量更改为状态变量并添加默认 Slider。这个 Slider 用于修改进度值,并在圆形滑块上实现足够的代码以使拇指和进度弧响应。当前值显示在环形 Slider 的中心。

struct CircularSliderView2: View {
@State var progress = 0.33
let ringDiameter = 300.0

private var rotationAngle: Angle {
return Angle(degrees: (360.0 * progress))
}

var body: some View {
ZStack {
Color(hue: 0.58, saturation: 0.04, brightness: 1.0)
.edgesIgnoringSafeArea(.all)

VStack {
ZStack {
Circle()
.stroke(Color(hue: 0.0, saturation: 0.0, brightness: 0.9), lineWidth: 20.0)
.overlay() {
Text("\(progress, specifier: "%.1f")")
.font(.system(size: 78, weight: .bold, design:.rounded))
}
Circle()
.trim(from: 0, to: progress)
.stroke(Color(hue: 0.0, saturation: 0.5, brightness: 0.9),
style: StrokeStyle(lineWidth: 20.0, lineCap: .round)
)
.rotationEffect(Angle(degrees: -90))
Circle()
.fill(Color.white)
.shadow(radius: 3)
.frame(width: 21, height: 21)
.offset(y: -ringDiameter / 2.0)
.rotationEffect(rotationAngle)
}
.frame(width: ringDiameter, height: ringDiameter)


VStack {
Text("Progress: \(progress, specifier: "%.1f")")
Slider(value: $progress,
in: 0...1,
minimumValueLabel: Text("0.0"),
maximumValueLabel: Text("1.0")
) {}
}
.padding(.vertical, 40)

Spacer()
}
.padding(.vertical, 40)
.padding()
}
}
}


添加触摸手势


DragGesture 被添加到滑块圆圈,并且使用临时文本视图显示拖动手势的当前位置。可以看到 x 和 y 坐标围绕包含环形 Slider 的位置中心的变化情况。

struct CircularSliderView3: View {
@State var progress = 0.33
let ringDiameter = 300.0

@State var loc = CGPoint(x: 0, y: 0)

private var rotationAngle: Angle {
return Angle(degrees: (360.0 * progress))
}

private func changeAngle(location: CGPoint) {
loc = location
}

var body: some View {
ZStack {
Color(hue: 0.58, saturation: 0.04, brightness: 1.0)
.edgesIgnoringSafeArea(.all)

VStack {
ZStack {
Circle()
.stroke(Color(hue: 0.0, saturation: 0.0, brightness: 0.9), lineWidth: 20.0)
.overlay() {
Text("\(progress, specifier: "%.1f")")
.font(.system(size: 78, weight: .bold, design:.rounded))
}
Circle()
.trim(from: 0, to: progress)
.stroke(Color(hue: 0.0, saturation: 0.5, brightness: 0.9),
style: StrokeStyle(lineWidth: 20.0, lineCap: .round)
)
.rotationEffect(Angle(degrees: -90))
Circle()
.fill(Color.blue)
.shadow(radius: 3)
.frame(width: 21, height: 21)
.offset(y: -ringDiameter / 2.0)
.rotationEffect(rotationAngle)
.gesture(
DragGesture(minimumDistance: 0.0)
.onChanged() { value in
changeAngle(location: value.location)
}
)
}
.frame(width: ringDiameter, height: ringDiameter)

Spacer().frame(height:50)

Text("Location = (\(loc.x, specifier: "%.1f"), \(loc.y, specifier: "%.1f"))")

Spacer()
}
.padding(.vertical, 40)
.padding()
}
}
}


为不同的坐标值设置滑块位置


圆形滑块上有两个表示进度的值,用于显示进度弧度的progress值和用于显示滑块光标的rotationAngle。应该只有一个属性来保存滑块进度。视图被提取到一个单独的结构中,该结构具有圆形滑块上进度的一个绑定值。


滑块的range的可选参数也是可用的。这需要对进度进行一些调整,以计算已设置的角度以及拇指在圆形滑块上位置的旋转角度。另外调用onAppear根据View出现前的进度值计算旋转角度。

struct CircularSliderView: View {
@Binding var progress: Double

@State private var rotationAngle = Angle(degrees: 0)
private var minValue = 0.0
private var maxValue = 1.0

init(value progress: Binding<Double>, in bounds: ClosedRange<Int> = 0...1) {
self._progress = progress

self.minValue = Double(bounds.first ?? 0)
self.maxValue = Double(bounds.last ?? 1)
self.rotationAngle = Angle(degrees: progressFraction * 360.0)
}

private var progressFraction: Double {
return ((progress - minValue) / (maxValue - minValue))
}

private func changeAngle(location: CGPoint) {
// 为位置创建一个向量(在 iOS 上反转 y 坐标系统)
let vector = CGVector(dx: location.x, dy: -location.y)

// 计算向量的角度
let angleRadians = atan2(vector.dx, vector.dy)

// 将角度转换为 0 到 360 的范围(而不是负角度)
let positiveAngle = angleRadians < 0.0 ? angleRadians + (2.0 * .pi) : angleRadians

// 根据角度更新滑块进度值
progress = ((positiveAngle / (2.0 * .pi)) * (maxValue - minValue)) + minValue
rotationAngle = Angle(radians: positiveAngle)
}

var body: some View {
GeometryReader { gr in
let radius = (min(gr.size.width, gr.size.height) / 2.0) * 0.9
let sliderWidth = radius * 0.1

VStack(spacing:0) {
ZStack {
Circle()
.stroke(Color(hue: 0.0, saturation: 0.0, brightness: 0.9),
style: StrokeStyle(lineWidth: sliderWidth))
.overlay() {
Text("\(progress, specifier: "%.1f")")
.font(.system(size: radius * 0.7, weight: .bold, design:.rounded))
}
// 取消注释以显示刻度线
//Circle()
// .stroke(Color(hue: 0.0, saturation: 0.0, brightness: 0.6),
// style: StrokeStyle(lineWidth: sliderWidth * 0.75,
// dash: [2, (2 * .pi * radius)/24 - 2]))
// .rotationEffect(Angle(degrees: -90))
Circle()
.trim(from: 0, to: progressFraction)
.stroke(Color(hue: 0.0, saturation: 0.5, brightness: 0.9),
style: StrokeStyle(lineWidth: sliderWidth, lineCap: .round)
)
.rotationEffect(Angle(degrees: -90))
Circle()
.fill(Color.white)
.shadow(radius: (sliderWidth * 0.3))
.frame(width: sliderWidth, height: sliderWidth)
.offset(y: -radius)
.rotationEffect(rotationAngle)
.gesture(
DragGesture(minimumDistance: 0.0)
.onChanged() { value in
changeAngle(location: value.location)
}
)
}
.frame(width: radius * 2.0, height: radius * 2.0, alignment: .center)
.padding(radius * 0.1)
}

.onAppear {
self.rotationAngle = Angle(degrees: progressFraction * 360.0)
}
}
}
}


CircularSliderView 的三种不同视图被添加到View中以测试和演示 Circular Slider 视图的不同功能。

struct CircularSliderView5: View {
@State var progress1 = 0.75
@State var progress2 = 37.5
@State var progress3 = 7.5

var body: some View {
ZStack {
Color(hue: 0.58, saturation: 0.06, brightness: 1.0)
.edgesIgnoringSafeArea(.all)

VStack {
CircularSliderView(value: $progress1)
.frame(width:250, height: 250)

HStack {
CircularSliderView(value: $progress2, in: 1...10)

CircularSliderView(value: $progress3, in: 0...100)
}

Spacer()
}
.padding()
}
}
}


总结


本文展示了如何定义响应拖动手势的圆环滑块控件。可以设置滑块视图的大小,并且滑块按预期工作。可以向控件添加更多参数以设置颜色或圆环内显示的值的格式。


作者:Swift社区
链接:https://juejin.cn/post/7215416975605661755
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

DNS

DNS DNS:Domain Name System 域名系统,应用层协议,是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网,基于C/S架构,服务器端:53/udp, 53/tcp实际上,每一台 DNS 服务器都...
继续阅读 »

DNS


DNS:Domain Name System 域名系统,应用层协议,是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网,基于C/S架构,服务器端:53/udp, 53/tcp实际上,每一台 DNS 服务器都只负责管理一个有限范围(一个或几个域)内的主机域 名和 IP 地址的对应关系,这些特定的 DNS 域或 IP 地址段称为 zone(区域)。根据地址解 析的方向不同,DNS 区域相应地分为正向区域(包含域名到 IP 地址的解析记录)和反向区 域(包含 IP 地址到域名的解析记录)

根域: 全球根服务器节点只有13个,10个在美国,1个荷兰,1个瑞典,1个日本


  • 一级域名:Top Level Domain: tld
  • 三类:组织域、国家域(.cn, .ca, .hk, .tw)、反向域
  • com, edu, mil, gov, net, org, int,arpa
  • 二级域名:magedu.com
  • 三级域名:study.magedu.com
  • 最多可达到127级域名

ICANN(The Internet Corporation for Assigned Names and Numbers)互联网名称与数字地址分配机构,负责在全球范围内对互联网通用顶级域名(gTLD)以及国家和地区顶级域名(ccTLD)系统的管理、以及根服务器系统的管理


DNS服务器类型


  • 缓存域名服务器:只提供域名解析结果的缓存功能,目的在于提高查询速度和效率, 但是没有自己控制的区域地址数据。构建缓存域名服务器时,必须设置根域或指定其他 DNS 服务器作为解析来源。
  • 主域名服务器:管理和维护所负责解析的域内解析库的服务器
  • 从域名服务器 从主服务器或从服务器"复制"(区域传输)解析库副本

序列号:解析库版本号,主服务器解析库变化时,其序列递增

刷新时间间隔:从服务器从主服务器请求同步解析的时间间隔

重试时间间隔:从服务器请求同步失败时,再次尝试时间间隔

过期时长:从服务器联系不到主服务器时,多久后停止服务

通知机制:主服务器解析库发生变化时,会主动通知从服务器


DNS查询类型及原理


查询方式

  • 递归查询:一般客户机和本地DNS服务器之间属于递归查询,即当客户机向DNS服务器发出请求后,若DNS服务器本身不能解析,则会向另外的DNS服务器发出查询请求,得到最终的肯定或否定的结果后转交给客户机。此查询的源和目标保持不变,为了查询结果只需要发起一次查询。(不需要自己动手)

  • 迭代查询:一般情况下(有例外)本地的DNS服务器向其它DNS服务器的查询属于迭代查询,如:若对方不能返回权威的结果,则它会向下一个DNS服务器(参考前一个DNS服务器返回的结果)再次发起进行查询,直到返回查询的结果为止。此查询的源不变,但查询的目标不断变化,为查询结果一般需要发起多次查询。(需要自己动手)


查询原理过程


正向解析查询过程:

1 先查本机的缓存记录

2 查询hosts文件

3 查询dns域名服务器,交给dns域名服务器处理 以上过程称为递归查询:我要一个答案你直接会给我结果

4 这个dns服务器可能是本地域名服务器,也有个缓存,如果有直接返回结果,如果没有则进行下一步

5 求助根域服务器,根域服务器返回可能会知道结果的一级域服务器,让他去找一级域服务器

6 求助一级域服务器,一级域服务器返回可能会知道结果的二级域服务器让他去找二级域服务器

7 求助二级域服务器,二级域服务器查询发现是我的主机,把查询到的ip地址返回给本地域名服务器

8 本地域名服务器将结果记录到缓存,然后把域名和ip的对应关系返回给客户端


DNS的分布式互联网解析库 



正向解析


各种资源记录


区域解析库:由众多资源记录RR(Resource Record)组成

记录类型:A, AAAA, PTR, SOA, NS, CNAME, MX


  • SOA:Start Of Authority,起始授权记录;一个区域解析库有且仅能有一个SOA记录,必须位于解析库的第一条记录SOA,是起始授权机构记录,说明了在众多 NS 记录里哪一台才是主要的服务器。在任何DNS记录文件中,都是以SOA ( Startof Authority )记录开始。SOA资源记录表明此DNS名称服务器是该DNS域中数据信息的最佳来源。
  • A(internet Address):作用,域名解析成IP地址
  • AAAA(FQDN): --> IPV6
  • PTR(PoinTeR):反向解析,ip地址解析成域名
  • NS(Name Server):,专用于标明当前区域的DNS服务器,服务器类型为域名服务器
  • CNAME : Canonical Name,别名记录
  • MX(Mail eXchanger)邮件交换器
  • TXT:对域名进行标识和说明的一种方式,一般做验证记录时会使用此项,如:SPF(反垃圾邮件)记录,https验证等

安装配置实操


下载安装bind文件,关闭防火墙进行后续操作

cd到etc下的文件夹查询对应软件,编辑named.conf文件




wq保存退出,接下来修改named.rfc1912.zones文件




wq保存退出后cd到var/named的文件下,复制local的文件作为自定义网站的文件模板进行编辑



wq保存退出之后检查有效性,首先修改网卡DNS为当前主机地址然后重启



 接下来开启程序systemctl start named




反向解析


和正向相似,在named.rfc1912文件下添加一段命令之后重新创建一个zones文件,将类型A换成PTR




主从复制


首先需要两台服务器,以我自己的192.168.222.100和192.168.222.200为例

进入etc下的named.conf文件修改两个any




修改etc下的named.rfc1912文件


 

 复制一份named.localhost作为模板进行修改
 

对网卡配置进行修改,然后重启网卡和启动named程序 


 接下来对第二台从服务器进行修改
同上,对named.conf文件进行修改两个any



 接下来修改从服务器的rfc文件




此时自动在slave文件夹下生成主服务器的文件



 修改主服务器的配置,双方重启后从也会变更









作者:钟情
链接:https://juejin.cn/post/7263410834499715131
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

在前端领域摸爬滚打7年,我终于掌握了这些沉淀技巧

我做开发多年,常常有人问我「软件开发难学吗?」「前端和后端哪个比较简单?」「培训后是否好找工作呢?」这些问题单拎出来比较棘手,三言两语说不清楚,需要你对开发有一个系统了解,问题才能迎刃而解。 所以,我想和你分享我的学习和工作经历,希望这对于正在准备成为一名程序...
继续阅读 »

我做开发多年,常常有人问我「软件开发难学吗?」「前端和后端哪个比较简单?」「培训后是否好找工作呢?」这些问题单拎出来比较棘手,三言两语说不清楚,需要你对开发有一个系统了解,问题才能迎刃而解。


所以,我想和你分享我的学习和工作经历,希望这对于正在准备成为一名程序员的你有所帮助。


我的经历可能会为新手提供一些有用的建议和思路。


01 萌芽之初,点燃编程学习的梦想


对于一些90后的朋友来说,网游填满了他们的高中时期,甚至是初中。


他们经常因为不走寻常路去打游戏,在回来时被门卫大爷逮个正着。尽管我没有沉迷于游戏,但我仍然被游戏所吸引。


在游戏中,我一直认为只有玩家和 NPC 的存在,但是,玩得越多,你会发现还有一些不寻常的角色,那就是“工作室”。部分“工作室”利用一些技术手段批量、自动地在游戏中完成任务以赚取游戏产出。


虽然这种行为不可取,但是他们使用的技术确实让我感兴趣。


这时候,代码的种子已经悄悄埋藏在我的内心深处,等待发芽。


高中毕业后,卸下学业负担,我开始利用暑期学习了一些脚本精灵、Tc 简单编程和易语言编程,这也是我第一次接触编程基础语法,如条件判断、循环、遍历和条件选择,再加上社区提供的一些识图插件,我就像一个蹩脚的裁缝,东拼西凑,左缝右补,费劲巴拉缝制成一件衣服,却不合身。


虽然实现了自动登录游戏的功能,但很不幸运的是,这样的小功能也还是过不去游戏的自检程序,万物皆有裨益,万事皆可为师,正是这一次编程体验促使了我后来的专业选择。


02 踏上编程学习之路,从安卓到前端,每一步都算数


英语是我成长路上的一块绊脚石,在选择专业时,我想躲开英语,于是选择了同为计算机系下的软件外包服务专业,结果发现,只要是技术,英语的要求都是一样的。


当然,我选择这个专业还有另外一个动机 -- 它开设了Android课程。毕竟,那时我刚拿到一款安卓手机,能在手机上开发自己的App是何等酷炫的体验啊!


那时,有一本厚重的《疯狂 Android 讲义》成了我的启蒙之书,我翻过无数遍,上课、参加编程比赛、实习工作、这本书我一直在用,为我第一份工作立下了汗马功劳。


临近毕业,是先就业还是先培训,许多软件相关专业的毕业生都面临着这样的选择。


所以,你要想明白,你到底需要的是什么?


我选择参加培训是出于两个原因:第一是为了将平时自学的知识整合起来,第二是希望能够认识更多的小伙伴,以便进行技术交流。编程最忌讳的就是闭门造车,不进行沟通交流。


然而,选择参加培训并不是每个人的选择。


如果你有能力自己阅读技术书籍,并且知道如何获取最新的技术信息,那么参加培训完全没有必要。


只有当你需要别人的指点和帮助来梳理技能,或者需要更好的机会来进行技术交流时,参加培训才是一个好的选择。


但是,如果你仅仅因为听说培训完就能很赚钱而选择花钱加入,那么你就要好好思考一下了,周围打水漂的人确实不在少数。


培训结束后,2015 年 12 月 7 号,我入职了第一家公司,担任 Android 开发工程师。


人生有时候做一个决策,一个行动,当时只道是寻常,当它的价值在未来某一刻兑现时,你会感谢当时努力的自己。


如果没有大学时翻过无数遍的《疯狂 Android 讲义》,我不可能找到这份工作。


03 学前端到底在学什么


工作后,我第一次真正进入团队开发模式(我是不会告诉你我当初使用百度云盘定时同步代码的,炸过一次硬盘),由于业务需要一定的前端支持(合同模板),所以在一次小组会议上,组长建议我们要着手学习前端技术(Angular1.x)。


到了17年左右,公司的业务开始由原 Pad 端转移到手机端。我和其他几个新入职的小伙伴经过一上午的 Vuejs2.x 培训后,就开始上手开发了。


也是在这次前端项目开发中,我第一次接触到了闭包导致循环失灵的问题,第一次把一个页面写到 3 千多行(烂,不懂拆分)。


由于这次前端项目开发的经验不足,导致迭代两年后,项目能编译出 200MB 的内容。我只能通过各种查找和大量的 webpack 参数调试,将产物压缩回了20MB 左右。对于我来说,这也是一次很大的成长。


我非常推荐各位小伙伴在工作中多承担,因为开发经验绝非是你熟背八股题得到的,开发经验只能是来自大量的项目实战。


多做练习,多遇困难,多做总结,得到的才是自己的。开发经验决定了你的下一个项目能否走得更顺利。


选择成为前端程序员是一件比较苦的事情,因为这个领域的技术更新非常频繁,如果你不持续学习,那么你就会落后,这也是“前端很累”的一个根本原因。


实际上,现在还有一些人对前端存在偏见,因为他们认为不就一个 JavaScript,能有多难?


但是事实上,很多前端构建技术的底层实现并不是用 JavaScript 语言编写的,而是基于了其它编程语言如 Golang(代:ESBuild)和Rust(代表:SWC)“包装”起来的,利用这些语言的特点来弥补 JavaScript 的不足。


前端学习的基础是 JavaScript,但不仅仅是 JavaScript,如果你认为学习 JavaScript 就是学习前端,那么你可能会走进死胡同。


04 正确的学习编程方式一定是这样的


在学校里,老师一定告诉过你两个正确的学习方式,其中一个是要做笔记,另一个是要能够向同学清晰地讲解。


繁多的技术是不可能靠记忆实现的,因此做笔记和写博客是记录学习过程和分享学习成果的捷径。


现在,我也发现很多在校的同学积极在各大技术社区分享自己的学习经验,这也印证了这条成长途径的正确,同时也激励我们这些已经做了多年程序员的伙伴要更加努力。


不论你是学习新的编程语言还是新的框架,都需要为其配置对应环境,但有很多框架的环境配置其实对于第一次接触的小伙伴来说并不友好,就比如我最初在从Android转前端的时候就因为安装NodeJsNpm这些东西而烦恼,因为当时莫名其妙就提示你Python2的模块找不到了,要不就是安装依赖超时了,在环境搭建问题上花费太长时间真的不划算。


为了避免环境搭建影响学习进度,我们可以使用一些在线的 IDE 环境,例如 CodePen、CodeSandBox、Stackblitz、JSRun 等。


但是,它们在依赖安装、操作习惯和响应速度上仍然有一些上手难度。


我最近一段时间一直在使用 1024Code  社区提供的在线 IDE,它提供了很多热门语言和框架的代码空间模板,免配置环境,即开即用随时学习新技术。


它支持多人开发和在线分享,无论是和朋友一起开发项目还是找大佬请教问题,都非常轻松。


05 学习编程,高效沉淀需要技巧


我发现之前写博客时做的案例很难沉淀下来。往往只是写完一遍,很少再打开运行。


但是在 1024Code 中,可以以卡片的形式记录每一个案例,也可以将一系列案例放到一个集合中归类。


此外,1024Code 还支持在个人主页中渲染 Markdown,为小伙伴打造炫酷的个人主页提供了便利。


最令人赞叹的是,1024Code 紧跟最近比较火的 ChatGPT,将其接入到了 IDE 中,让你在编码的同时可以更快速地查找解决方案。下面我给大家简单地展示一下:


在社区主页中,案例以卡片的形式展示。你可以点击你感兴趣的案例,一键运行。边浏览源码,边跟着作者提供的 README 进行学习。


如果你想在此基础上练习或二次开发,还可以 fork 一份到自己的工作空间。如果你发现作者的代码有不合理的地方,还可以在评论区大胆地给他留言,大家可以共同成长。



1024Code 提供了众多空间模板,涵盖了多种编程语言和框架,例如针对数据统计和 AI 模型训练的 Python,以及让许多程序员感到头疼的 C++。


此外,它还支持其它主流的热门编程语言和框架。



Markdown 是编程小伙伴们最常用的笔记格式之一,因此无需专门学习其语法。只需要多看几遍,就可以自然而然地掌握。


此外,你还可以参考社区中其他小伙伴的主页,来打造自己独特的个人主页。



接下来,我要展示一段时间以来我制作的合集。


最初,这个合集是为了帮助那些不熟悉滴滴 LF 框架如何使用 Vue3+TS 编写的小伙伴们而制作的。


我还将合集地址提交到了 LF 仓库,希望能够帮助那些正在转向 Vue3+TS 的小伙伴们。



最重磅的就是 ChatGPT 了。


在使用 1024Code 的 IDE 进行开发过程中,如果遇到问题,你可以快速打开 ChatGPT 来协助你查找答案,而不需要离开当前页面。


ChatGPT 支持上下文连续问答模式,虽然它不能解决你所有的问题,甚至会给出错误的答案,但对于一些常规类编程问题或正在做毕业设计的小伙伴们,它还是能够显著提升效率的。



总结


最后,我再为你做一些总结、建议和对未来的期待:

  • 我建议你要有很强的动力来学习编程,因为坚持并不是易事;

  • 我建议你坚守自己慎重选择的专业,因为不忘初心方得始终;

  • 我建议你在面对技术培训时要清醒认知,因为明确目标的选择才适合自己;

  • 我建议你在工作中抓住一切学习的机会,因为努力的人很多,只有不断学习才能跟上技术的发展;

  • 我建议你在编程学习时要善用工具、做好笔记、写博客,不断沉淀自己的知识和经验;


最后的最后,愿我们所有付出都将是沉淀,所有美好终会如期而至。


作者:小鑫同学
链接:https://juejin.cn/post/7209648356530929721
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

再聊聊秋招焦虑

我想,关于秋招,大家是真的焦虑的。 作为一个刚起了两个月的公众号,平时写写技术相关的内容,阅读量少则几十,多则也不超过500。结果一聊到秋招这个话题,阅读量直接干到27000去了,让我硬生生地过了一把大V的瘾。(手动狗头) 现代版的“国家不幸诗家幸,赋到沧桑...
继续阅读 »

我想,关于秋招,大家是真的焦虑的。


作为一个刚起了两个月的公众号,平时写写技术相关的内容,阅读量少则几十,多则也不超过500。结果一聊到秋招这个话题,阅读量直接干到27000去了,让我硬生生地过了一把大V的瘾。(手动狗头)



现代版的“国家不幸诗家幸,赋到沧桑句便工”有没有?


所以,我决定再聊十块钱的“秋招焦虑”这个话题,以一个过来人的身份,开导开导同学们。如果同学们真的干了这碗鸡汤后,觉得暖心暖胃,精神上舒服一些,那也不枉我敲这么多字了。


首先说下,产生焦虑的原因是什么?


参照了知乎上的众多答案,我觉得最精辟的一个答案是:担心某件不好的事情,在未来的某个时间点将会发生,但主观上又拒绝接纳。


而往往焦虑的人的认知特点是:


  • 高估不好的事情的发生概率;
  • 高估不好的事情所带来的后果;
  • 低估自己应对挫折的能力;

我们把这种认知特点带入到秋招中,同学们的想法大概是这样:


卧槽,今年校招这么卷,完全就是一片哀鸿遍野的景象,那我肯定找不到工作了。一旦形成毕业即失业的情况,那我前面二十多年爬冰卧雪、寒窗苦读就完全看不到价值了。试问读书的意义是什么呢?完了,生不逢时啊,我这辈子基本上也就交待了。


而在这种焦虑的情绪下,最常见的应激反应就是回避行为。即:有条件的打算润到国外,没条件的誓死考公。


下面,开始灌鸡汤,各位同学听好。


超过80%的人,你不必焦虑


其实各行各业都一样,你只要能超过行业内80%的人,基本上就没什么好担心的,降低一些预期,找到一份差不多的工作肯定没问题。


逻辑是这样的,如果一个行业的top 20%都凉凉了,那证明这个行业不仅仅是不景气,而是被这个时代所淘汰了。如果真的到了这个地步,那最焦虑的肯定不是作为个体的你了。


按照top 20%这个人物画像进行圈选的话,很多专业能力不错,有实习经历的211 985同学,其实是都在其内的。


你如果还在焦虑的话,那就是世上本无事,庸人自扰之了。



当然,再说一句,如果你不在这20%的池子里面,那你还轮不到焦虑,你最先做好的的是四个字:反求诸己。


反求诸己,很多事情上都是这么一个道理,就是苦练基本功。


正常周期变化,你无须焦虑


一般情况下,经济和行业周期大概在8到10年左右。任何人漫长的职业生涯中,都会经历几个起起伏伏的过程,有时候早经历一次,未必是坏事。


郭德纲的原话是这样说的:


吃亏要趁早,一帆风顺不是什么好事。从小大伙娇生惯养,没有人跟他说过什么话,65岁走在街上谁瞪他眼就会猝死。从出生开始一天打八个嘴巴,这样的到25岁就是铁罗汉、活金刚一样什么都不在乎,吃亏要趁早。


其实话糙理不糙,当你经历了萧条的经济周期,再遇到经济转好,行业内一片欣欣向荣的时候,你会怀着更加感恩的心态,拥有更成熟的理性思考,再也不会把风口机遇和平台资源当做个人能力。


说得粗鄙一点儿就是,变得更加有逼数儿了,不好高骛远,不过度消费,这样反而让你以后的路走得更平稳一些。



不就是一次秋招吗,你焦虑个毛


有的同学性格过于敏感脆弱,总是会把一件不好的事情的后果,弄得跟世界末日一样。


其推理过程大概这样:


少了一枚铁钉,掉了一只马掌。掉了一只马掌,失去一匹战马。失去一匹战马,失去一场战役。败了一场战役,毁了一个王朝。



等于“千里之堤,毁于蚁穴”的道理,让他给用到这里了。


其实真的不用这么想,就算秋招不太理想,还有春招,春招就算也不理想,直接走社招投简历不就完了。退一万步说,大不了转行,只要你不懒不傻不笨,照样能在别的行业混得风生水起。


真的真的没必要,认为秋招GG了,整个人生都黯淡了。人这一辈子很长,有很多翻盘的机会,也有很多盛极而衰的事例,没必要计较一时之成败。


像男人一样,扛过焦虑期


永远不要低估人类应对挫折和低谷的能力。


想想我们的父母辈,正好赶上了国企的下岗潮,捧了半辈子的铁饭碗丢了。就像刘欢的那首《重头再来》所唱到的那样,”辛辛苦苦已度过半生,今夜重又走进风雨“。


但是,基本上没看到下岗的哪家哪户断粮饿死,最不济的也是出去打工或者做点儿小生意,还有的因此而混出一些名堂出来呢。


就像曾国藩的那句名言所说的一样:生平长进,全在受挫受辱之时,需咬牙立志,受不得穷,立不得品,受不得屈,必做不得事。


我们再来看看褚时健老爷子,以前是云南红塔集团的董事长,后来由于贪污受贿被判处无期徒刑,和妻子双双入狱,女儿自杀,他的低谷堪称绝望之谷。


2002年,74岁出狱二次创业,开始做褚橙。到了2014年,”褚橙“销售额达到了一亿多元,纯利润7000多万。因此,人们又称它为”励志橙“。



难怪见过大世面的王石都这样感慨道:


橙子挂果要6年,他那时已经75岁高龄了。试想一下,一个75岁的老人,戴一个大墨镜,穿着颇圆领衫,兴致勃勃地跟我谈论橙子挂果是什么场景。我当时就想,如果我遇到他那样的挫折,到了他那个年纪,我会想什么?我知道,我一定不会像他那样勇敢。


所以,像个男人一样,顶住压力,行动起来吧。扛过这段焦虑期吧,阴霾只是暂时的,未来终将是美好的。


作者:库森学长
链接:https://juejin.cn/post/7281256255354781732
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

Xcode 升级到14.3以后 调试与打包遇到的坑

iOS
前言 是苹果逼的,通知说2023年4月25日之后,所有的App都要在iOS16的SDK上打包。不然也不会有那么多事情(呜呜呜🥹)。 1.Xcode 14.3版本运行项目报错 问题如下:ld: file not found: /Applications/Xcod...
继续阅读 »

前言


是苹果逼的,通知说2023年4月25日之后,所有的App都要在iOS16的SDK上打包。不然也不会有那么多事情(呜呜呜🥹)。


1.Xcode 14.3版本运行项目报错


问题如下:

ld: file not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a
clang: error: linker command failed with exit code 1 (use -v to see invocation)

报错信息看,都是在链接库的时候因为找不到静态库(libarclite_iphonesimulator.a/libarclite_iphoneos.a)而报错。利用访达的前往文件夹功能快速来到报错信息中的目录,发现连 arc目录都不存在,更不用说静态库文件。


开发人员解释说,因为系统已经内置有 ARC相关的库,所以没必要再额外链接,至少Xcode 14支持的最低部署目标iOS 11及以上版本的系统肯定是没问题的。如果应用部署目标不低于iOS 11还出现问题,那么应该是第三方库的部署目标有问题。


所以解决方案也很清晰了,将所有依赖库和应用最低部署版本都限制在iOS11以上即可。


解决方案:

post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
end
end
end

2. 升级Xcode14以后项目报错 Stored properties cannot be marked potentially unavailable with '@available'


这是依赖库报错,把其中一个库升级到了最新的版本,不报错了。但是还有一个库没办法升级,因为我们的项目是Flutter项目,不知道是哪个三方库的依赖库,百度了好久没找到办法,最后还是强大的Google到方法:


在iOS目录下:

执行pod install
然后再执行pod update

最终可以了


3. Xcode升级到14.3 archieve打包失败

mkdir -p /Users/hsf/Library/Developer/Xcode/DerivedData/Ehospital-crirdmppgluxkodauexhkenjuxet/Build/Intermediates.noindex/ArchiveIntermediates/Ehospital/BuildProductsPath/Release-iphoneos/复旦云病理.app/Frameworks
Symlinked...
rsync --delete -av --filter P .*.?????? --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "../../../IntermediateBuildFilesPath/UninstalledProducts/iphoneos/AliyunOSSiOS.framework" "/Users/hsf/Library/Developer/Xcode/DerivedData/Ehospital-crirdmppgluxkodauexhkenjuxet/Build/Intermediates.noindex/ArchiveIntermediates/Ehospital/InstallationBuildProductsLocation/Applications/复旦云病理.app/Frameworks"
building file list ... rsync: link_stat "/Users/hsf/Desktop/medical/app/iOS/Ehospital/../../../IntermediateBuildFilesPath/UninstalledProducts/iphoneos/AliyunOSSiOS.framework" failed: No such file or directory (2)
done

sent 29 bytes received 20 bytes 98.00 bytes/sec
total size is 0 speedup is 0.00
rsync error: some files could not be transferred (code 23) at /AppleInternal/Library/BuildRoots/97f6331a-ba75-11ed-a4bc-863efbbaf80d/Library/Caches/com.apple.xbs/Sources/rsync/rsync/main.c(996) [sender=2.6.9]
Command PhaseScriptExecution failed with a nonzero exit code

找到...-frameworks.sh 文件,替换

source="$(readlink "${source}")"

source="$(readlink -f "${source}")"

4. The version of CocoaPods used to generate the lockfile (1.3.1) is higher than the version of the current executable (1.1.0.beta.1). Incompatibility issues may arise.


这个比较简单,更新cocoapods就行。

sudo gem install cocoapods

5. Warning: CocoaPods minimum required version 1.6.0 or greater not installed…

sudo gem install cocoapods

6. Cocoapods 更新卡死在1.5.3,但控制台一直提示说有新版本


主要就是ruby的问题了。别问我怎么知道的,花了一天的时间。

ruby -v 查看版本

若比较低,现在一般都3.x了,所以要升级


用以下命令就可以升级了,可能需要科学上网。

brew update
brew install ruby

升级完成以后,ruby -v后其实还是原来的版本👌,这是因为环境变量没有配置。因此,还有一个步骤就是配置环境变量。

vi ~/.zshrc 

拷贝 export PATH="/usr/local/opt/ruby/bin:$PATH" 放进去


英文输入法下 按下esc键 输入 :wq


最后再执行

source ~/.bash_profile

然后更新gem

gem update #更新所有包
gem update --system #更新RubyGems软件

最后再更新pod

sudo gem install cocoapods

注意现在可能会提示说更新到了1.12.1了,但实际上还是1.5.3,所以还要执行另外一个命令。

sudo gem install -n /usr/local/bin cocoapods

这个就可以有效升级了。


7. gem常用命令

gem -v #gem版本
gem update #更新所有包
gem update --system #更新RubyGems软件
gem install rake #安装rake,从本地或远程服务器
gem install rake --remote #安装rake,从远程服务器
gem install watir -v(或者--version) 1.6.2#指定安装版本的
gem uninstall rake #卸载rake包
gem list d #列出本地以d打头的包
gem query -n ''[0-9]'' --local #查找本地含有数字的包
gem search log --both #从本地和远程服务器上查找含有log字符串的包
gem search log --remoter #只从远程服务器上查找含有log字符串的包
gem search -r log #只从远程服务器上查找含有log字符串的包
gem help #提醒式的帮助
gem help install #列出install命令 帮助
gem help examples #列出gem命令使用一些例子
gem build rake.gemspec #把rake.gemspec编译成rake.gem
gem check -v pkg/rake-0.4.0.gem #检测rake是否有效
gem cleanup #清除所有包旧版本,保留最新版本
gem contents rake #显示rake包中所包含的文件
gem dependency rails -v 0.10.1 #列出与rails相互依赖的包
gem environment #查看gem的环境

结语


有些坑现在只是知道这样做就行,还不知道为什么。后面再补补吧。


作者:Jalor
链接:https://juejin.cn/post/7229223235681878071
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

iOS-解决定位权限卡顿问题

iOS
一、简介 在iOS系统中,定位权限获取是一个涉及进程间同步通信的方法,如果频繁访问可能会导致卡顿或者卡死。在一些打车或者地图类的APP中,定位权限的卡顿报错可能是大头,亟需解决! 下面是系统类提供的访问定位权限的方法:// CLLocationManager是...
继续阅读 »

一、简介


在iOS系统中,定位权限获取是一个涉及进程间同步通信的方法,如果频繁访问可能会导致卡顿或者卡死。在一些打车或者地图类的APP中,定位权限的卡顿报错可能是大头,亟需解决!
下面是系统类提供的访问定位权限的方法:

// CLLocationManager是系统的定位服务管理类
open class CLLocationManager : NSObject {
// 1.下面方法是访问系统设置中定位是否打开
@available(iOS 4.0, *)
open class func locationServicesEnabled() -> Bool

// 2.1 iOS 14.0之后,访问定位的授权状态
@available(iOS 14.0, *)
open var authorizationStatus: CLAuthorizationStatus { get }

// 2.2 iOS 14.0之后,访问定位的授权状态
@available(iOS, introduced: 4.2, deprecated: 14.0)
open class func authorizationStatus() -> CLAuthorizationStatus
}

二、从卡顿堆栈例子中分析问题


为了解决这个卡顿,首先要分析卡顿报错堆栈。接下来举一个定位权限频繁获取导致的卡顿的堆栈:

0 libsystem_kernel.dylib _mach_msg2_trap + 8
1 libsystem_kernel.dylib _mach_msg2_internal + 80
2 libsystem_kernel.dylib _mach_msg_overwrite + 388
3 libsystem_kernel.dylib _mach_msg + 24
4 libdispatch.dylib __dispatch_mach_send_and_wait_for_reply + 540
5 libdispatch.dylib _dispatch_mach_send_with_result_and_wait_for_reply + 60
6 libxpc.dylib _xpc_connection_send_message_with_reply_sync + 240
7 Foundation ___NSXPCCONNECTION_IS_WAITING_FOR_A_SYNCHRONOUS_REPLY__ + 16
8 Foundation -[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:] + 2236
9 Foundation -[NSXPCConnection _sendSelector:withProxy:arg1:arg2:arg3:] + 136
10 Foundation __NSXPCDistantObjectSimpleMessageSend3 + 76
11 CoreLocation _CLCopyTechnologiesInUse + 30852
12 CoreLocation _CLCopyTechnologiesInUse + 25724
13 CoreLocation _CLClientStopVehicleHeadingUpdates + 104440
14 MyAPPName +[ZLLocationRecorder locationAuthorised] + 40
15 ... // 以下略
  • 首先从第14行找到是ZLLocationRecorder类的locationAuthorised方法调用后,执行到了系统库函数,最终导致了卡死、卡顿。

  • 对堆栈中第0-13行中的方法做一番了解,初步发现xpc_connection_send_message_with_reply_sync函数涉及进程间同步通信,可能会阻塞当前线程点击查看官方方法说明



该函数说明:Sends a message over the connection and blocks the caller until it receives a reply.

  • 接下来添加符号断点xpc_connection_send_message_with_reply_sync, 注意如果是系统库中的带下划线的函数,我们添加符号断点的时候一般需要少一个下划线_. 执行后,从Xcode的方法调用栈视图中查看,可以发现ZLLocationRecorder类的locationAuthorised方法内部中调用CLLocationManager类的locationServicesEnabledauthorizationStatus方法都会来到这个符号断点.所以确定了是这两个方法导致的卡顿。(调试时并未发现卡顿,只是线上用户的使用环境更加复杂,卡顿时间长一点就被监控到了,我们目前卡顿监控是3秒,卡死监控是10s+)。

  • 然后通过CLLocationManager类的authorizationStatus方法说明,发现也是说在权限发生改变后,系统会保证调用代理方法locationManagerDidChangeAuthorization(_:),所以就产生了我们的解决方案,最终上线后也是直接解决了这个卡顿,并且APP启动耗时监控数据也因此变好了一些。


三、具体的解决方案



 注意点:设置代理必须在有runloop的线程,如果业务量不多的话,就在主线程设置就可以。


四、Demo类,可以直接用

import CoreLocation

public class XLLocationAuthMonitor: NSObject, CLLocationManagerDelegate {
// 单例类
@objc public static let shared = XLLocationAuthMonitor()

/// 定位服务是否可用, 这里设置成变量避免过于频繁调用系统方法时产生卡顿,系统方法涉及进程间通信
@objc public private(set) var serviceEnabled: Bool {
set {
threadSafe { _serviceEnabled = newValue }
}

get {
threadSafe { _serviceEnabled ?? CLLocationManager.locationServicesEnabled() }
}
}

/// 定位服务授权状态
@objc public private(set) var authStatus: CLAuthorizationStatus {
set {
threadSafe { _authStatus = newValue }
}

get {
threadSafe {
if let auth = _authStatus {
return auth
}
if #available(iOS 14.0, *) {
return locationManager.authorizationStatus
} else {
return CLLocationManager.authorizationStatus()
}
}
}
}

/// 计算属性,这里返回当前定位是否可用
@objc public var isLocationEnable: Bool {
guard serviceEnabled else {
return false
}

switch authStatus {
case .authorizedAlways, .authorizedWhenInUse:
return true
case .denied, .notDetermined, .restricted:
return false
default: return false
}
}

// MARK: - 内部使用的私有属性
private lazy var locationManager: CLLocationManager = CLLocationManager()
private let _lock = NSLock()
private var _serviceEnabled: Bool?
private var _authStatus: CLAuthorizationStatus?

private override init() {
super.init()
// 如果是主线程则直接设置,不是则在mainQueue中设置
DispatchQueue.main.safeAsync {
self.locationManager.delegate = self
}
}

private func threadSafe<T>(task: () -> T) -> T {
_lock.lock()
defer { _lock.unlock() }
return task()
}

// MARK: - CLLocationManagerDelegate
/// iOS 14以上调用
public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if #available(iOS 14.0, *) {
authStatus = locationManager.authorizationStatus
serviceEnabled = CLLocationManager.locationServicesEnabled()
}
}

/// iOS 14以下调用
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
authStatus = status
serviceEnabled = CLLocationManager.locationServicesEnabled()
}
}

作者:sweet丶
链接:https://juejin.cn/post/7247504715726929978
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

小米:阳了,被裁了

hi 大家好,我是 DHL。公众号:ByteCode ,专注有用、有趣的硬核原创内容,Kotlin、Jetpack、性能优化、系统源码、算法及数据结构、大厂面经。 随着防疫政策的放开,小阳人越来越多了,身边很多小伙伴都在朋友圈晒自己阳了之后的各种状态,基本上...
继续阅读 »

hi 大家好,我是 DHL。公众号:ByteCode ,专注有用、有趣的硬核原创内容,Kotlin、Jetpack、性能优化、系统源码、算法及数据结构、大厂面经。



随着防疫政策的放开,小阳人越来越多了,身边很多小伙伴都在朋友圈晒自己阳了之后的各种状态,基本上都处于一边发烧,一边坚持工作的状态,症状严重的小伙伴忍着疼痛还要处理公司的任务,把自己奉献给公司,然后收到了却是公司无情的裁员的消息。


年末将至,知乎和小米也登上了热搜。


裁员


我之前在小米的同事陆陆续续收到了通知,阳着还在工作,然后收到了裁员的消息。国内公司裁员的吃相都不怎么好看,基本上在发年终奖前会进行大比例的裁员。


2021 年的时候小米有 32000 名员工,据传 2022 年底小米要裁 6000 名员工,裁员幅度接近 20%,无论消息是否真实,但是这次裁员规模影响范围应该不小。



小米为什么要裁员


小米连续 3 个季度开始下滑,前 3 个季度,每个季度利润 20 亿,相比于去年同期的 50 亿下跌了很多,那为什么利润下跌这么多呢,主要有以下原因:


  1. 公司不赚钱,意味着主营业务开始萎缩,小米的主营业务,手机前 3 个季度卖了 4020 万部,销售额大概 425 亿,平均每部手机 1000 元,原本指望华为被制裁之后,小米能拿下这部分用户,但是最后也放弃了,这部分用户基本上都归苹果了
  2. 据调查中国的手机市场已经处于饱和状态,每年换手机的发烧友越来越少了
  3. 小米赌上全部身价大踏步地进入汽车领域,汽车是个周期长、投资大的业务,没有上百个亿,基本上不可能会有结果的
  4. 小米的股价也跌了很多,投资人很失望,我也买了很多小米的股票,基本上都是血亏

所以不得不开始降本增效,在老板的眼里,业务上升期的时候,开始疯狂砸钱招人,到达了瓶颈,业务不再增长的时候,老板就会冷静下来盘算,到底需不需要这么多人,然后开始降本增效,而裁员就是最有效的控制成本的手段。


曾经有小伙伴问过,小米的年终奖能拿多少


我在这里也只是顺口一说,大家当做饭后余兴看看就好了,小米的年终奖是 2 个月,而个人绩效是跟部门和所在事业部挂钩的,如果部门的绩效好的话,大部分人都能拿满,但是如果部门绩效不好的话,只有少数人能拿满,平均下来一个部门能拿满 2 个月的人数非常少,如果你非常的优秀,拿 3~4 个月也是有的,但是这个比例极其少,如果你和领导关系好的话,那么就另当别论了。


小米这次裁员赔偿虽然给了 N+2,但是这次裁员的吃相也比较难看,引来了小米员工的吐槽。以下图片来自网络。




而每次裁员,应届生都是最惨的,在大裁员的环境下,能不能找到工作是最大的问题,应届生和有工作经验的社招生是不一样的,无论是赔偿还是找工作的机会,相比于应届生更愿意招社招生,当然特别优秀的除外。



我之前很多在小米的同事,赔偿都给了 N + 2,但是年底被裁员时间点非常的不好,短时间内,想找到工作是非常困难的,但是先不要着急,如果你的身体还没恢复,建议先等身体恢复,在恢复期间,整理一下你的工作项目,网上搜索一下面试题,整理和回顾这些面试题,记住一定要多花时间刷算法题。


等到年后找工作会容易些,面试的成功的率也会很高,你的溢价空间也会很大,在选择公司的时候,这个阶段还是以稳为主,避开那些风险高的公司和部门。


文章的最后


遍地小阳人的冬天比以往更冷,在公司非常艰难,业务不再增长的时候,都会断臂求生,我们都要去面对被裁的风险。


站在打工者的角度,当一个人在某个环境待久了,会被表象的舒适所蒙蔽,时间久了会变得很迷茫,所以我们要想办法跳出舒适圈,保持学习的热情。

作者:程序员DHL
链接:https://juejin.cn/post/7184234182778814522
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

鸿蒙原生应用,全面启动,开发者需要抓住风口的浪尖

iOS
前言 老铁们,就在前天,9月25日,在华为秋季全场景新品发布会上,华为常务董事、终端BG CEO、智能汽车解决方案BU董事长余承东介绍了鸿蒙系统的最新进展:HarmonyOS 4发布后,短短一个多月升级用户已经超过6000万,成为史上升级速...
继续阅读 »

前言


老铁们,就在前天,9月25日,在华为秋季全场景新品发布会上,华为常务董事、终端BG CEO、智能汽车解决方案BU董事长余承东介绍了鸿蒙系统的最新进展:HarmonyOS 4发布后,短短一个多月升级用户已经超过6000万,成为史上升级速度最快的HarmonyOS版本;余承东宣布,鸿蒙原生应用全面启动,HarmonyOS NEXT开发者预览版将在2024年第一季度面向开发者开放。



我们知道,在8月4日的华为开发者大会,华为才刚刚推出了面向开发者的 HarmonyOS NEXT 开发者预览版,如果说当时只是一个概念,那么这次,绝对是正式官宣,打响移动端第三系统的第一枪!我们有理由且必须相信,HarmonyOS NEXT开发者预览版正在急速到来,不仅仅是对手机系统的冲击、移动端的开发者也有着不小的冲击。


如果说当时刚推出,你踌躇徘徊,犹豫不定,对待HarmonyOS犹如对待外来物一样,极度的排斥,那么这次你绝对忽视不得,否则,你将错过一个时代的步伐。


短短一个多月升级用户已经超过6千万,足以打脸那些看弱HarmonyOS的人,也从另一方面说明,HarmonyOS已经得到越来越多人的喜爱,或许是有了不断攀升的用户量,才让华为手机有了信心去发展原生系统应用,并且此前有爆料数据显示鸿蒙OS5.0会取消支持安卓软件,这种爆料绝非空穴来风,可能很多人包括我在内,会觉得取消支持Android软件,是一件非常冒险的行为,但是随着华为手机的体量越来越大,生态越来越好,这个事情是必须且迟早的要做的。


鸿蒙是否有必要学习


可能鸿蒙从一诞生,就背着一个”套壳“的骂名,毕竟一直都兼容AOSP(Android 开放源代码项目),很难不令人怀疑,当然了,曾经我也有所怀疑,以至于,对于HarmonyOS保持的态度,始终都是,冷漠,不感冒,毕竟Android开发的包,在HarmonyOS上也能用,我们何必再去研究它呢?费力又费时间,还不如刷刷短视频,对吧。


但是,一旦HarmonyOS剥离AOSP,Android开发的包无法在其运行,这种情况下,身为移动端的开发者,特别是Android端的开发者,你觉得有没有必要学习?


试想这样的一个场景,当其他的应用都能在HarmonyOS上运行,而你的应用确不支持,你是什么感觉?当然了,也得问一句,你们公司是什么感觉?虽然说目前HarmonyOS国内市场占有率为8%,占有率并不是很多,但止不住它发展迅速啊,未来,20%,50%都有可能,即便是8%,这样的一个市场,你和你的公司难道会果断的放弃?如果放弃的话,确实没必要学,但是,能放弃吗?


再试想一个场景,随着HarmonyOS不断的发展,移动端三分天下,而企业考虑到成本问题,在招聘的时候,要求了必须要会HarmonyOS开发,你如何破解这个问题?


无论是自身发展还是当下的企业布局,HarmonyOS都是你躲不过的一道屏障,无非就是什么时间入手的问题,当然了,如果一个企业或者个人,对HarmonyOS,没什么业务发展,也不在乎这些市场份额,那就没必要学习,反过来,真的要静下心来,好好研究研究了,否则影响的不仅仅是一个应用,更是大量的用户流失。


可能很多人都会觉得,HarmonyOS剥离AOSP,这么冒险的事,华为大概率不会那么武断,即便升级,可能也会采取双系统并行,也就是HarmonyOS4.0 和HarmonyOS Next,继续兼容Android一段时间,当然了,不排除这种做法,我想说的是,这也只是一个广大的猜测,在其他大厂APP都跟进的情况下,如果它升级了,怎么办?哪怕概率为1%,对企业和个人的影响绝对是100%,话又说回来,它采取了双系统并行或者有其他的兼容方案,你觉得华为会一直兼容吗,所以啊,如果你想继续从事这个行业,学只不过是早晚的问题


所以啊,HarmonyOS,肯定是要学的,除非你要告别当前从事的移动端开发,如果再做一层针对性的,那就是告别Android端开发,毕竟和iOS端的冲突目前还没那么大。


不仅要学,而且还要提前进行技术储备,目的防患于未然;毕竟来年的事,谁也说不定,有条件的公司,技术储备之后,就可以复刻鸿蒙版App了,尽量赶上升级后的第一批App,这样就可以做到无缝衔接,不至于鸿蒙系统流失用户,当然了,也可以只做技术储备,隔岸观火,进一步观察HarmonyOS的下一步动作,但是,技术储备一定要做,无论来年华为升级与否,因为复刻鸿蒙版App,不是一朝一夕能够完成的,起码目前来看,还没有一件转化的功能,只能从0到1的进行开发,小体量的App还好说,大体量的App,从0到1没个半年以上还真完成不了,所以啊,哪怕华为宣布来年不强制升级,到2025年升级,留给开发者的时间还多吗?


HarmonyOS的学习路径有很多,官网也给出了详细的视频以及文档教程,大家可以直接学习即可,当然了大家也可以关注我,哈哈,我也会定时分享HarmonyOS相关的技术,目前在有序的输出。


鸿蒙未来的发展


根据华为最新公布的数据:目前鸿蒙生态设备已达7亿台,早就跨过了“生死线”;鸿蒙品牌知名度从2021年的50%升级至今年6月的85%,越来越多的用户知晓和主动拥抱HarmonyOS;HarmonyOS 3用户升级率达到85%,超过了iOS(81%)成为最新版本设备升级率最高的操作系统,而HarmonyOS 4发布后,短短一个多月升级用户已经超过6000万,可以说是,恐怖如斯,遥遥领先!


目前华为已与合作伙伴和开发者在社交、影音、游戏、资讯、金融等18个领域全面展开合作,在HarmonyOS独特的全场景分布式体验、原生智能、纯净安全、大模型AI交互等方面,HarmonyOS NEXT构筑了差异化优势,全面领先于行业。


为了更好帮助合作伙伴成长,在HDC 2023期间,华为正式发布鸿蒙生态伙伴发展计划——“鸿飞计划”,宣布未来三年将投入百亿人民币,向伙伴提供全方位的资源扶持,包括技术支持、市场推广、商业合作等,让每一位伙伴都成为鸿蒙生态的主角。


无论是企业的绝对支持,还是政府的大力推进,HarmonyOS的发展,可以说势如破竹,三分天下,也就是时间的问题。


我们都知道,操作系统生态的发展,人才是重中之重。随着鸿蒙生态的发展,专业人才需求正在呈现井喷式增长,为此,在鸿蒙人才培养方面,华为也做了全面投入,今年以来已有超过170万人参加了鸿蒙学堂的课程学习、线下活动,华为还和全国300多所高校展开了合作,鸿蒙产学合作项目超过140个,已经颁发鸿蒙学堂证书超过7万,各类开发者活动累计参加人次超过350万。


可以告诉大家的是,俺也是其中一员,哈哈~,当然了,证书并没有含金量,只是一个阶段学习的测试而已。



除此之外,近期教育部-华为“智能基座”产教融合协同育人基地2.0启动,未来双方将与72所高校合作培养鸿蒙人才,一起促进鸿蒙生态的繁荣发展。


我们总担忧鸿蒙的生态,对它不屑一顾,说它“套壳”,说它抄袭,说它迟早会死,可是,人家不吭不响,不反驳,只会默默的耕耘,以至于发展的越来越好,越来越完善,为什么鸿蒙这么自信,我们却不自信呢?我们在担忧什么?


鸿蒙的生态离不开每一个的开发者,我们有理由相信,未来的时刻,它肯定会剑指Android和iOS,我们更有理由相信,国产系统的繁荣富强,一定会到来,民族的自信心也必定到来!


鸿蒙不仅仅是一个系统,它是更长远的国家战略


国家战略说的有点大了,但是肯定是在计划之内和大力支持的,为什么这么说,从18年的中美贸易战,到22年的俄乌冲突,卡脖子的事,发生的还少吗?动不动进行制裁,动不动限制出口,美国佬龌龊的事做的还少吗?如果说一直没有自研,那么话语权始终掌握在别人手里,不仅仅是一个系统,像芯片等等,我们始终很难强大。


俄乌冲突期间,谷歌公司停止认证运行安卓操作系统的俄罗斯BQ公司的智能手机,微软宣布禁止在俄罗斯使用Windows系统,也许对于我们个人而言,觉得没什么影响,但是站在国家层面,绝对是致命的打击,如果未来,收复TW时,也来这么一下,你觉得,国家能承受的住吗?


除了各种限制和制裁之外,俄乌冲突期间最恐怖的是,谷歌地图服务提供俄罗斯所有军事和战略设施的最高分辨率卫星图像,这不就等于明牌了,你在明处,人家在暗处,所以,无论是系统,还是芯片,还是其他的技术方向,站在国家层面上,能够自研,无论是摆脱外部限制,还是自身科技发展,绝对都是划时代的意义。


所以,老铁们,对于鸿蒙,于国于人,我们都应该有充足的自信,不仅仅关系着手机系统的三分天下,更是国家安全的未来措施,政策,一定是某项事物发展的导向,跟着国家走,准没错。


开发者如何提前布局


我觉得应该从三方面入手,第一,就是技术储备,学习HarmonyOS,能够达到独立的完成项目开发;第二,就是,技术架构,组件,基础库的梳理和开发,这么做的目的,是便于日后项目的快速开发;第三,就是着手自己项目HarmonyOS版的开发了,以应对未来HarmonyOS升级。


未来是否有一键转化HarmonyOS版App的功能,这个一切未知,有的话,就太方便了,没有的话,只能从0到1进行开发了,当然了,跨平台语言的支持,也是一个突破点,比如Flutter支持HarmonyOS,那么对于原来Flutter语言的App而言,就无比轻松了,而目前来说,这些都没有一个实质性的进展,所以还是一步一步的先学习HarmonyOS开发吧。


还好,HarmonyOS主推的是ArkTs语言,其中也定义了声明式UI,和Flutter,Compose,Swift等有着异曲同工之妙,如果你有着声明式开发的经验,那么掌握HarmonyOS简直是易如反掌。


当然了,为了更好的提高开发效率,HarmonyOS采用了反推的做法,推出了自己的跨平台框架ArkUI-X,成熟之后,我们可以作为开发框架,进而兼容Android和iOS。


ArkUI-X 是 ArkUI 的跨平台框架,采用 ArkUI 开发的应用能在 HarmonyOS 上原生运行,获得极佳的性能,通过 ArkUI-X 能够在 Android 和 IOS 上跨平台运行,获得强于 Flutter、React Native 等同类竞品的性能。



总结


该说的也都说了,不该说的也说了,至于HarmonyOS,您是学习还是放弃,只能由自己决断了,可以肯定得是,您的放弃,一定是未来的错误决定。


番外


写文章的时候,电脑上老是有一种刺啦刺啦声音,这个声音很小,听的不是很清楚,一开始我总以为是敲击键盘的声音,当我凑近一听,一种熟悉的声音扑面而来:遥遥领先,遥遥领先~


作者:程序员一鸣
链接:https://juejin.cn/post/7283322449038229541
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

对不起 localStorage,现在我爱上 localForage了!

web
前言 前端本地化存储算是一个老生常谈的话题了,我们对于 cookies、Web Storage(sessionStorage、localStorage)的使用已经非常熟悉,在面试与实际操作之中也会经常遇到相关的问题,但这些本地化存储的方式还存在一些缺陷,比较明...
继续阅读 »

前言


前端本地化存储算是一个老生常谈的话题了,我们对于 cookies、Web Storage(sessionStorage、localStorage)的使用已经非常熟悉,在面试与实际操作之中也会经常遇到相关的问题,但这些本地化存储的方式还存在一些缺陷,比较明显的缺点如下:



  1. 存储量小:即使是web storage的存储量最大也只有 5M

  2. 存取不方便:存入的内容会经过序列化,当存入非字符串的时候,取值的时候需要通过反序列化。


当我们的存储量比较大的时候,我们一定会想到我们的 indexedDB,让我们在浏览器中也可以使用数据库这种形式来玩转本地化存储,然而 indexedDB 的使用是比较繁琐而复杂的,有一定的学习成本,但第三方库 localForage 的出现几乎抹平了这个缺陷,让我们轻松无负担的在浏览器中使用 indexedDB


截止今天,localForage 在 github 的 star 已经22.8k了,可以说 localForageindexedDB 算是相互成就了。


什么是 indexedDB


IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象)。


存取方便


IndexedDB 是一个基于 JavaScript 的面向对象数据库。IndexedDB 允许你存储和检索用键索引的对象;可以存储结构化克隆算法支持的任何对象。


之前我们使用 webStorage 存储对象或数组的时候,还需要先经过先序列化为字符串,取值的时候需要经过反序列化,那indexedDB就比较完美的解决了这个问题,可以轻松存取对象或数组等结构化克隆算法支持的任何对象。


stackblitz.com/ 网站为例,我们来看看对象存到 indexedDB 的表现



异步存取


我相信你肯定会思考一个问题:localStorage如果存储内容多的话会消耗内存空间,会导致页面变卡。那么 IndexedDB 存储量过多的话会导致页面变卡吗?


不会有太大影响,因为 IndexedDB 的读取和存储都是异步的,不会阻塞浏览器进程。


庞大的存储量


IndexedDB 的储存空间比LocalStorage 大得多,一般可达到500M,甚至没有上限。


But.....关于 indexedDB 的介绍就到此为止,详细使用在此不再赘述,因为本篇文章我重点想介绍的是 localForage!


什么是 localForage


localForage 是基于 indexedDB 封装的库,通过它我们可以简化 IndexedDB 的使用。



兼容性


想必你一定很关注兼容性问题吧,我们可以看下 localStorage 与 indexedDB 兼容性比对,两者之间还是有一些小差距。


image.png


但是你也不必太过担心,因为 localforage 已经帮你消除了这个心智负担,它有一个优雅降级策略,若浏览器不支持 IndexedDB 则使用 WebSQL ,如果不支持 WebSQL 则使用 localStorage。在所有主流浏览器中都可用:Chrome,Firefox,IE 和 Safari(包括 Safari Mobile)。


localForage 的使用



  1. 下载


import localforage from 'localforage'




  1. 创建一个 indexedDB


const myIndexedDB = localforage.createInstance({
name: 'myIndexedDB',
})


  1. 存值


myIndexedDB.setItem(key, value)


  1. 取值


由于indexedDB的存取都是异步的,建议使用 promise.then() 或 async/await 去读值


myIndexedDB.getItem('somekey').then(function (value) {
// we got our value
}).catch(function (err) {
// we got an error
});

or


try {
const value = await myIndexedDB.getItem('somekey');
// This code runs once the value has been loaded
// from the offline store.
console.log(value);
} catch (err) {
// This code runs if there were any errors.
console.log(err);
}


  1. 删除某项


myIndexedDB.removeItem('somekey')


  1. 重置数据库


myIndexedDB.clear()


以上是本人比较常用的方式,细节及其他使用方式请参考官方中文文档localforage.docschina.org/#localforag…



VUE 推荐使用 Pinia 管理 localForage


如果你想使用多个数据库,建议通过 pinia 统一管理所有的数据库,这样数据的流向会更明晰,数据库相关的操作都写在 store 中,让你的数据库更规范化。


// store/indexedDB.ts
import { defineStore } from 'pinia'
import localforage from 'localforage'

export const useIndexedDBStore = defineStore('indexedDB', {
state: () => ({
filesDB: localforage.createInstance({
name: 'filesDB',
}),
usersDB: localforage.createInstance({
name: 'usersDB',
}),
responseDB: localforage.createInstance({
name: 'responseDB',
}),
}),
actions: {
async setfilesDB(key: string, value: any) {
this.filesDB.setItem(key, value)
},
}
})

我们使用的时候,就直接调用 store 中的方法


import { useIndexedDBStore } from '@/store/indexedDB'
const indexedDBStore = useIndexedDBStore()
const file1 = {a: 'hello'}
indexedDBStore.setfilesDB('file1', file1)

后记


以上就是本篇文章的所有内容,感谢观看,欢迎留言讨论。


作者:阿李贝斯
来源:juejin.cn/post/7275943591410483258
收起阅读 »

解决Android13上读取本地文件权限错误记录

Android13 WRITE_EXTERNAL_STORAGE 权限失效 1. 需求及问题 需求是读取sdcard上txt文件 Android13(targetSDK = 33)上取消了WRITE_EXTERNAL_STORAGE,READ_EXTERN...
继续阅读 »

Android13 WRITE_EXTERNAL_STORAGE 权限失效


1. 需求及问题



  1. 需求是读取sdcard上txt文件

  2. Android13(targetSDK = 33)上取消了WRITE_EXTERNAL_STORAGEREAD_EXTERNAL_STORAGE权限。

  3. 取而代之的是READ_MEDIA_VIDEOREAD_MEDIA_AUDIOREAD_MEDIA_IMAGES权限

  4. 测试发现,即便动态申请上面三个权限,仍旧无法读取本地txt文件


image.png


2. 解决方案



  1. AndroidManifest.xml中增加


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.LocationDemo"
tools:targetApi="31">


<activity
android:name=".MainActivity"
android:exported="true">

<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>


  1. Activity中新增代码


// 方案一:跳转到系统文件访问页面,手动赋予
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse("package:" + this.getPackageName()));
startActivity(intent);

Screenshot_20230927-131444[1].png


// 方案二:跳转到系统所有需要文件访问页面,选择你的APP,手动赋予权限
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);

image.png


作者:OpenGL
来源:juejin.cn/post/7283152332622610492
收起阅读 »

傻吗?谈男人们饭桌的拼酒现象

过年了,亲朋好友们聚在一起,免不了会喝酒。对于喝酒,尤其是人多的时候,更尤其是多数人都喝的时候,男性朋友们近乎是往“死”里喝。 这一点,女性朋友们很难理解。 首先,酒喝多了肯定是伤害身体的。它会危害肝、胃、心脑血管。并且还让人神志不清,容易出错,增大发生危险的...
继续阅读 »

过年了,亲朋好友们聚在一起,免不了会喝酒。对于喝酒,尤其是人多的时候,更尤其是多数人都喝的时候,男性朋友们近乎是往“死”里喝。


这一点,女性朋友们很难理解


首先,酒喝多了肯定是伤害身体的。它会危害肝、胃、心脑血管。并且还让人神志不清,容易出错,增大发生危险的概率。酗酒伤身体,这已经是没有什么争议的事情了。那些说接触烟酒能长寿的人和例子,也并非是假的。只是长寿和影响健康,是可以同时发生的。


但是,少喝一点,从缓解焦虑情绪和分散注意力来讲,对心理是有帮助的。这作用等同于听歌和看电影。


我们在生活中,经常会发现这样一个现象。那就是饭局中会有拼酒现象。尤其是同平级伙伴吃饭,一定要分出个高下。仿佛谁喝的多,谁就厉害,谁就是王。这个现象在男性群体中尤为明显。


可能你也不知道为什么要拼酒,但还是不自主地加入了这个队伍


其实,这可能是个高端局。下面咱从三个方面,分析这个事情。


第一:争强斗胜是人的本性。人在还是动物的时候,就用尽各种方式相互斗争、比赛,目的就是脱颖而出,获得好的资源。野蛮的时候,主要途径是肢体上的搏斗和厮杀。这也是很多体育竞技项目产生的原因。


然而现在的文明社会,很难再体现上面的冲突了。尤其是饭局上,你肯定不能打一架。现代文明,需要一种有难度又印象深刻的表现形式。看谁吃得多,肯定不行。然而喝酒,就是一个很好的表现形式。


第二:反映一个人的自控能力。人都有想干的事情和不想干的事情。同样,酒喝到一定程度,也会不想喝。不想喝的时候就不喝吗?那么不想加班的时候就不加班吗?不想早起的时候就不早起吗?这是一种承受和应对压力的能力。同时,喝完酒会意识模糊,在这种情况下需要控制自己的言行举止,稍有不当,将会贻笑大方。所以,这也是一个体现个人对自身控制能力的筛选项。


第三:快速拉近彼此间的距离。一群陌生人吃饭,即使有一个好的话题讨论,彼此之间也会有一个陌生的安全距离。然而很多时候,饭桌的上的人,不是有经常相聚的机会的。但是这时还带着各自的目的和任务。因此,在短时间内搞好关系,变得尤为重要。通过酒,可以让相敬如宾变为勾肩搭背,要个号码,打听个事情,变得简单起来。


拼酒,是一种肢体搏斗在如今文明世界的延续和替代。因此,它又是另一种你死我活。它具备了通过非暴力手段就可区分出层次的指标。以上三点看似合理,也不排除有曲解之嫌。不可否认,拼酒这种杀敌一百,自损三千的方式,几千年了依然存在,也有它存在的道理。


你愿意拼就拼,不愿意就撤。这个没啥,全看个人的选择。


作者:TF男孩
来源:juejin.cn/post/7192411210531209277
收起阅读 »

2023年:我成了半个外包

边线业务与主线角色被困外包; 01 2022年,最后一个工作日,裁员的小刀再次挥下; 商务区楼下又多了几个落寞的身影,办公室内又多了几头暴躁的灵魂; 随着裁员的结束,部门的人员结构简化到了极致,至少剩下的人是这么认为的; 说实话,对于当下的互联网行业来说,个...
继续阅读 »

边线业务与主线角色被困外包;




01



2022年,最后一个工作日,裁员的小刀再次挥下;


商务区楼下又多了几个落寞的身影,办公室内又多了几头暴躁的灵魂;


随着裁员的结束,部门的人员结构简化到了极致,至少剩下的人是这么认为的;


说实话,对于当下的互联网行业来说,个人感觉两极分化的有点严重;


卷的,卷到鼻青脸肿,不知道BUG和需求哪个会先来;


不卷,感觉随时失业,不知道明天和裁员哪个会先来;


最近这几年,裁员的故事已经不新奇了;


比较热的话题反而是留下的那些人,如何应对各种此起彼伏的事情;


裁员,对于走的人和留的人来说,都是正面暴击;


走的人,虽然拿着赔偿礼包,但是要面对未来工作的不确定性,尤其是在当下的环境中;


留的人,要兜底很多闪现过来的事项,未来一段时间会陷入混乱的节奏中;


对于公司来说;


裁员之后如何应对业务,没有一丝丝迟疑,会做出了完全出于本能的决定;


内部团队能应对的就自己解决,解决不了就交给外包方处理;


整体的策略就是:核心业务领域之外的需求,选择更低成本的解决手段;



02



公司裁员之后,本意还是想专注自己的核心业务;


至于为何要接其他公司的需求,这里就涉及很多社会上的人情世故了;


比如一些重要关系或者流水大的客户;


缺乏互联网方面的专业团队,合作时会偶尔抛出研发或其他方面的需求;


对于公司来说,接手吃力不讨好,不接手又怕影响客户关系维护;


最好的选择就是寻求外包解决;


基于公司的研发团队,替客户进行相关需求的落地把控;


虽然接收外包需求流水抽成不高,但是可以更加紧密的维持客户合作关系;


允许质疑外包的质量和效率,但是不能否认长期的整体成本;


在裁员之后,团队介入的外包项目越来越多,形成主线和外包业务五五开的魔幻局面;


外包项目的合作形式大致分为两种;




  • 甲乙双方:甲方的业务与公司主线业务相关联,通常由团队自己开发;

  • 甲乙丙三方:甲方的业务比较独立,乙方接手之后再转交给丙方开发;


在这种合作中,如果只涉及甲乙两方,流程还是顺畅的;


但是对于甲乙丙三方的合作模式,如果再关联其他对接方,简直就是离谱踹门而入,离谱想拆家;


在经历几次甲乙丙三方的合作过程中,对于夹板气的体会已经是铭刻在心了;


甲乙双方对于丙方来说,是提供需求单的甲方;乙丙双方对于甲方来说,是落地需求单的外包方;


合作过程中拉扯出个精分现象,都习以为常了;


下面基于甲乙丙三方合作的模式,来聊一聊外包所踩到的坑坑洼洼;



03



【如何选择外包公司】


在甲乙丙三方合作中,甲方交给乙方的业务,可能是基于信任关系,或者成本原因;


但是乙方想再找一个靠谱的外包团队,难度就会大很多;


乙方既然承接需求,最终都是想交付高质量的结果,从而加强双方的合作关系;


如果没有一个靠谱的外包团队介入,所谓高质量的结果根本无从谈起;


通常会先从过往的合作过且靠谱的外包团队中寻找,但是能找到的概率其实并不大,这里的影响因素有很多;


需求本身的复杂度,外包团队能不能承接,是一方面;


甲方对于需求落地的预期时间,与外包团队的介入时间是否符合,也是一方面;


乙方对于外包团队的报价能否接受,又是一方面;


如果合作过的团队中没有,则会优先从公司内部寻求推荐,比盲寻一个不知底的团队要靠谱很多;


这里存在一个关键的卡点因素;


虽然研发团队接触的外包人员多,但是碍于怕麻烦的心理,乐意介入的人很少;


所以需求最终交给一个新的外包团队的概率很大,也为后续的诸多问题埋下隐患;



04



【三方合作的流程机制】


首先还是先说一个基本原则,在复杂的协作中,明确流程是最基础的事项;


三方合作,实现需求,获取利益回报;


流程上看可能并不复杂,然而在实际协作过程中,又十分的曲折;


在明确协作的流程时,需要把握需求的三个关键阶段:排期、研发、交付;


这里阶段划分是站在研发的角度拆解,从项目经理或者决策层看又是另一个说法了;



在研发视角下,虽然依旧是围绕排期、研发、交付三个阶段;


但由于涉及三方协同,各个阶段的事项都会变的繁杂;


流程的推进和问题解决,都要进行三方的统筹协调,麻烦事也从不缺席;


排期阶段



  • 乙方接受甲方的需求单和报价,并寻求丙方做需求实现;

  • 丙方围绕需求单进行拆解,输出项目计划书以及报价,乙方认同后达成初步合作意向;

  • 乙丙双方就排期与甲方达成共识后,三方就各自的合作签订外包合同;


研发阶段



  • 丙方就需求完成设计,在甲乙双方评审通过后,正式进入开发阶段;

  • 丙方需要定期将开发进度同步给乙方,乙方确认后也需要定期汇报给甲方;


交付阶段



  • 理论上丙方在自测完成后,再交付给乙方进行验收;

  • 乙方在验收阶段承担的压力比较大,本着对客户关系负责的态度,需要实现高质量的交付;

  • 甲方验收通过后,进行线上部署并交付项目材料,最终完成合同的结算流程;


流程终究只是对协作的预期设定;


在实际的执行中,会有各种问题层出不穷;


很容易把各方都推到情绪的边缘,进而导致系列关联的效应问题;



05



【三方合作的沟通问题】


如果从三方合作的问题中,选一个最大的出来,不用证明都确定是沟通问题;


沟通不到位,问题容易说不清楚,解决问题的很多动作可能都是抓瞎;


由于三方的合作是远程在线模式,不是当面表达;


沟通频率本来就低,等到发现问题解决思路不对时,耽误的时间已经久了;


如果返工;


那排期又需要重新协商,又会引起一系列必要的麻烦问题;


这种情况,对于乙方的项目经理来说;


身处甲丙两方的极限拉扯之中,会经常在离职和跳槽的情绪中不断徘徊;


然而也不乏一些花哨的操作,将甲乙丙三方拉扯到一个协作群中;


如果甲方不介意乙方寻找外包实现需求,那么三方在群里及时沟通和解决问题的效率也会高很多;


但是大部分的甲方还是介意的,很多沟通都是由丙方到乙方,乙方再转述给甲方;


传话游戏玩到最后,驴头不对驴嘴的现象十有八九;


所以,很多的外包合作群中;


可能都是存在着甲乙丙三方人员,只是乙丙对甲方语调统一,以此避免信息传递的问题;



06



【需求落地的质量问题】


对于三方合作实现的需求,质量高不高?


比较肯定的回答;


可能有一定的质量,但是高质量的期望建议打消,说不定还有一丝惊喜;


质量依赖靠谱的外包合作方,这本身就是一件有难度的事,看脸和运气都没用;


专业负责的外包团队少有,所以其团队的业务有持续性;


在实际协作过程中出现的问题少,才可能更加专注于需求本身的落地实现上;


然而真实的现状是;


外包团队会在需求排期内尽快完成,投入越少,收益越大;


比如:实现一个需求,估时30天,费用10W;


如果在15天内完成需求,相当于成本投入缩减一半,这样在30天内可能实现多个需求;


鉴于这种策略之下,很多需求的实现可能都是仓促的,质量上自然很难保证;


所以对于质量问题的把关,压力会给到乙方,在交付验收时做好时间差管理;



乙方预留一部分时间段,对丙方交付进行验收,如果出现问题及时修改,避免传递到甲方;


当然了,混乱验收和测试也是常见的骚操作;


不乏一些丙方拿乙方的验收当测试,乙方拿甲方的验收当测试,以此来降低自己的时间成本;


由此导致三方合作裂开,尾款结算的问题,甚至对簿公堂也不少见;


虽然不是三方负责人乐意见到的,但又是三方都很难把控的事;


最终结果就是,不但成本没少,事情还更多了;



07



业务需求外包,是比较常见的一种手段,只是过程与结果的把控难度较大;


对于甲乙两方来说;


可能是利益驱动,可能是社会的人情世故,从而建立了合作关系;


对于乙丙两方来说;


则是单纯的利益考量,从而形成了短期的合作;


然而对于那些身处甲乙丙三方合作的网友们,只能在内心轻轻的嘀咕一句:人在社会,身不由己


作者:知了一笑
来源:juejin.cn/post/7203377276557852730
收起阅读 »

大龄,掘金,疫情,酒店,转型,前端满两年,搞公司后端两个月,年后离职还是继续等待?

大家好,我是 那个曾经的少年回来了。10年前我也曾经年轻过,如今已步入被淘汰的年龄,但现在幡然醒悟,所以活在当下,每天努力一点点,来看看2024年的时候自己会是什么样子吧,2024年的前端又会是什么样子,而2024年的中国乃至全球又会变成什么样子,如果你也有想...
继续阅读 »


大家好,我是 那个曾经的少年回来了。10年前我也曾经年轻过,如今已步入被淘汰的年龄,但现在幡然醒悟,所以活在当下,每天努力一点点,来看看2024年的时候自己会是什么样子吧,2024年的前端又会是什么样子,而2024年的中国乃至全球又会变成什么样子,如果你也有想法,那还不赶紧行动起来。期待是美好的,但是更重要的是要为美好而为之奋斗并付诸于行动。



喜欢的可以到创作者榜单点点我,估计也没几个人点我哈哈,自己点自己嘞


1、前言


就跟随着标题一个一个的来总结一下自己的2022吧,绝望中透露着一丝的希望,让我不得不在逆境中重生,寻找新的出路。


2、欠薪6个月


今年上了12个月的班,但是呢不算12月的工资,竟然还有6个月的工资没发,公司确实欠薪了,而且也非常的难受。怎么办呢?我自己也不清楚,过完年再说吧,希望年前最后一个月还能发点工资吧。


3、大龄


88年大龄前端:转行前端不到两年|2022年年中总结


这是我在2022年年中的时候总结的文章,那个时候计划2022年下半年输出大概16篇文章,而我下半年真正输出了46篇文章,当然其中有一部分是在我脚骨折只能在家卧床的时候写的,所以从时间上来看有一些水分,但是从完成任务的角度我还是超额完成的,我对自己的表现非常满意,哈哈哈。


大龄也许就是一个分水岭,有的人踏过去了,也有的人就此放弃了,还有的人根本不当回事,那么你又是哪一种呢?


大龄,没学历,没背景,没资源就只能躺平吗?反正我觉得如果真躺平了,那就是平了,而我选择了继续努力,每天保持不断的学习努力有所成长,就会得到满足,,哪怕一点点,也经得起长时间的积累。


4、掘金



  • 收获最多的地方
    1bed61531924d964bbf75dd5d12911f.jpg


这里应该是收获最多的地方,55篇这放在任何时候想都不敢想,万万没想到竟然能输出这么多,而且还收获了掘金非常多的礼物,在此感谢掘金,感谢川哥https://juejin.cn/user/1415826704971918, 不用想肯定是你认识的那个若川视野。


61da0551e864447baa877f208eb0f43.jpg


这里的礼物只是一部分,还有另外一部分,什么背包帽子,等等的每次收到都非常的开心。


324f7d177af92efe44023043cd25583.jpg


这个创作先锋将我个人还是非常的意外,也是不经意间老婆收到的快递,简直开心到起飞。



  • 去年在掘金的阅读


image.png


2021年一年可以说是入门前端,和众多刚毕业以及毕业一两年的前端的道友们一起在这里不断的收获,这里我个人点赞(共683篇)的文章大多都是研读的文章。



  • 今年在掘金的阅读


9e851faeebda2eed0f7e074f72d93d3.jpg


同时依靠掘金我的github也竟然有了200多的小星星,实属难得


image.png


这里顺便提一下极客时间的学习


0e79faf2e59a08ba062182d24596aed.jpg


212ec2c1481895c931dd57c9f9cbee8.jpg


只能说尽力学对自己有用的,充实自己,其实很多篇我都是反复看,看的自己明明白白的。不过确实也收获到了知识。


2022年一年可以说是入门后的腾飞,不断在掘金的引领下,让我在自我思考的摸索中寻找到坚定的方向。同时在川哥的带领下我也能看懂一点牛逼开源项目的源码了,这真的可以说是比较大的突破了。同时可以发现2022年的阅读量会更大一些,由于自己也会进行输出,在输出的过程中其实更需要对知识进行再三确认。


5、疫情,酒店,转型




  • 万万没想到就在现在此时此刻,全国所有人正在经历着,或者自己的至亲正在经历着,又或者自己身边的人正在经历着“鼻子封水泥、喉咙吞刀片、内脏咳出胸、”等症状,本来这篇文章准备在12月23日发出来的,但早上一醒来就进入炼狱般的状态了,昨天一天在头痛和发烧中度过的。




  • 由于公司主营业务便是服务于酒店业务,公司在2020年和2021年的收入有所影响,但总体可控影响不大。但是时间节点来到2021年年底以及2022年的全年,各种突发情况,慢慢的让公司的收入锐减。




  • 同时公司在2020年也有了初步的判断,需要拓展业务,才有了新的业务赛道,可能是由于决策和对新赛道的陌生,也使得前期大幅投入迟迟达不到预期,迟迟也没有收入,公司也由360多人,一度减员到8月份低谷时期,总人数不到80吧。




6、前端满两年




  • 从2020年9月25日入职公司,开始接触vue2,然后着手公司pc端:vue2+elementui,微信端h5:vue2+vant, 然后android app webview嵌套 vue2+vant,期间也接触了一个react项目




  • 2021年年初开始走上,vite+vue3+echarts大屏项目,相对于熟悉了解了vue2后,直接用vue2的语法来写是没问题的,然后慢慢的也在学习vue3+setup的语法,也将某些组件进行了转换




  • 2021年4月开始一个新的pc项目,采用了qiankun微前端,主应用使用vite+vue3,其他子应用采用vuecli+vue3 + element-plus,刚使用qiankun时,还是遇到了一些问题




  • pc端项目经过几个月的时间,陆续稳定上线,然后期间封装了pc端的json form表单生成器和json table列表生成器,这两个组件节省了很多PC端重复的工作,以及bug修改,感觉封装出来还是有点成就感的,我的前端兄弟都觉得非常的nice。




  • 搞pc期间还接触了leaflet、leaflet-geoman来给地图打点或者画区域,上手略有难度,但经过几天的摸索熟悉后,能够磕磕绊绊的将需要的功能实现出来了,使用过后感觉这个类库的功能还是非常强大的。




  • 2021年年底开始在原有android app webview的基础上增加新的功能,考虑到对vue3以及qiankun的熟悉,准备添加一个子应用,使用vue3+vant的模式来处理新增的业务功能




  • 此时可着手两个组件的封装,一个当然还是json form表单生成器的,逻辑上跟pc组件是类似的,只是换了一套vant的组件。另外一个相当于pc端的table列表,但是在移动端的h5当中每个列表的样式可能不同,就单独提取了一个模板,加速充血了一波,待组件稳定后,其实大致到了2022年的3月份了。




  • 2022年4月份的时候公司有一个专门数据采集的项目,最终要的功能便是用到了根据json生成form表单的并且对接通用接口,json的生成也是通过页面进行配置。其中难度比较大的便是数据的联动控制显示隐藏,以及数据校验、正则匹配、以及将部分js代码通过界面去编写,前端解析json后再动态执行js代码也是一个不小的难点。




  • 另外一个突破便是将vant 列表数据模板,做了两个通用的,根据SQL配置 接口返回通用的数据结构列表,去匹配模板列表。其实这里也有思考通过后台配置,拖拽元素实现列表的一行数据样式展示,但是在渲染的时候我是根据屏幕宽高比去进行等比的展示,但是发现样式会有所变形,主要是通过transform: scale(0.9) 计算出比例,然后填充数值,我猜测可能是我实现的方式还存在问题,等有时间再来看看,主要是我觉得这个思路好像是没问题的。




  • 期间5、6月份开始解决vue3 移动端中 列表到详情再返回列表,并且要记录当时的位置的问题,其实解决起来还是蛮麻烦的,当时查阅资料或者水平还不够,没能实现,但是线上的问题又必须要解决,于是硬着头皮看了一下vue3 keppalive组件的源码,其实还是看了蛮久的,看完解决完问题后,我还专门写了一篇小文,一不小心算是上了掘金的头条,真的非常开心。




  • 同时解决微信小程序中嵌套webview场景中的一些小问题,最主要的一个问题其实微信中打开h5页面,如果有使用到localstorage或者cookie,再在微信小程序中嵌套h5页面,那么会存在脏读的问题。我是通过根据window.navigator.userAgent.toLowerCase() 先判断其中是否包含 'miniprogram',有则代表是在微信小程序中,再判断是否包含'micromessenger',有则代表是在微信环境中,这样针对每个环境去设置不同的key,然后在当前环境中使用当前的key就不会产生冲突了。




  • 2022年7月份意外脚骨折在家里呆了三个周吧,然后上下班打车两个月终于摆脱拐杖,不得不说真的是伤筋动骨100天呢。




  • 2022年8月和9月正常开始迭代新的需求和项目的bug修复,期间有指出有新的项目要开始了。由于自己自身的尴尬(原先前端由我来管理的,但是骨折期间和之后发生了一些令人不悦的事情,没办法我直接提出交出去吧),自己也不能闲下来,于是开始新项目的准备,前端我可以干,有时间了也开始参与后端的代码。




7、后端两个多月的时间了(从2022年10月至今)


之前使用过.net framework,而公司有个项目正好使用的是.net core,所以上手难度相对较小但由于很久没用,区别还是有的,,最大的区别当然就是跨平台了。于是在今年10月份开始接触.net core,这两个多月的时间下来对公司后端代码也算是有了更加深入的了解。之前的两年时间算是全部都花在了前端代码里。从我现在的角度来看后端,其实思路相对来说也非常的明确。




  • 熟悉操作linux常用的各种命令,因为要发布测试上线,服务器都是linux




  • 熟悉基础的后端代码,然后能够独立的实现CRUD增删改查




  • 熟悉mysql的基本操作,由于数据量比较大,所以对索引的使用也上了一个台阶,要不然严重影响接口的响应时间




  • 当然还有其他的但是目前来看还只算是皮毛,有待进一步的加强学习




8、年后离职还是继续等待?


关于这个问题其实自己思考过了,看年后一两个月的情况就可以快速决定了。没办法,从现在开始只能说我要时刻准备着,时刻准备让自己拥有更多的技能,能够让自己变得更加强大。


9、2023年计划


没有目标一切都将是空谈,给自己制定一个切实有效的目标,那么到了来年,可以跟随时间和需求的变化,再随时调整目标。


关于前端计划




  • 继续攻坚前端工程化




  • 继续攻坚前端组件的封装




  • 继续攻坚react的使用和深入,公司项目主要是vue3,自己玩无用武之地




关于后端计划




  • 微服务架构模式学习深入




  • 消息队列在项目各场景中灵活运用,比如先攻克一个rabbitmq




  • redis在项目中发挥桥梁的作用




  • mysql数据库如何在项目中发挥护城墙的作用,把好最后一道关卡




  • 项目整个架构相关的学习实战




所以最后争取吧,一年36篇小作文,也就是每个月三篇,目标不算远大,但好好的去完成也需要一些精力,关键是要对当前的自己要有用处。


10、总结




  • 35岁真的会被毕业吗?而且是会被永久毕业吗?如果身边的朋友、同学、又或者是同学的朋友、同事的朋友等等真的是大批量的都被毕业了,那么我才会觉得风险是真的来了。




  • 现在就是时刻准备着可能要发生的事情,企业如果真不行了,或者自己真的想换工作了,就提前准备不就完事了。




  • 说真的每天时间就那么有限,自从你有了家,有了娃,时间就如白驹过隙




  • 没什么负面情绪,如果有的话就转化为正面动力吧




  • 浅层的学习靠输入,深层的学习靠输出:通过几期的学习源码,能深刻感受到自己看一遍和写一遍真的是非常不一样




  • 兄弟们加油吧,也许在疫情的催化下底层人民过的将会更加艰苦,多关照一下家里的老年人




  • 在疫情的催化下我们也要重新考虑一下我们的工作和生活方式了




  • 喜欢的可以到创作者榜单点点我,估计也没几个人点我哈哈,自己点自己嘞




作者:那个曾经的少年回来了
来源:juejin.cn/post/7181095134758387773
收起阅读 »

工作 7 年的老程序员,现在怎么样了

犹记得高中班主任说:“大家努力考上大学,大学没人管,到时候随便玩”。我估计很多老师都这么说过。 我考上大学(2010年)之前也是这么过的。第一年哥哥给买了个一台华硕笔记本电脑。那个年代买华硕的应该不少,我周边就好几个。有了电脑之后,室友就拉着我一起 cs,四个...
继续阅读 »

犹记得高中班主任说:“大家努力考上大学,大学没人管,到时候随便玩”。我估计很多老师都这么说过。


我考上大学(2010年)之前也是这么过的。第一年哥哥给买了个一台华硕笔记本电脑。那个年代买华硕的应该不少,我周边就好几个。有了电脑之后,室友就拉着我一起 cs,四个人组队玩,那会觉得很嗨,上头。


后来看室友在玩魔兽世界,那会不知道是什么游戏,就感觉很好玩,再后来就入坑了。还记得刚开始玩,完全不会,玩个防骑,但是打副本排DPS,结果还被人教育,教育之后还不听(因为别的职业不会玩),就经常被 T 出组。之后,上课天天看游戏攻略和玩法,或者干脆看小说。前两年就这么过去了


1 跟风考研


大三开始,觉得这么混下去不行了。在豆瓣上找了一些书,平时不上课的时候找个自习室学习。那会家里打电话说有哪个亲戚家的孩子考研了,那是我第一次知道“考研”这个词。那会在上宏微观经济学的课,刚好在豆瓣上看到一本手《牛奶面包经济学》,就在自习室里看。刚好有个同院系的同学在里面准备考研,在找小伙伴一起战斗(毕竟,考研是一场长跑,没有同行者,会很艰难)。我一合计,就加入了他们的小团队。从此成为“中国合伙人”(刚好四个人)中的一员。


我那会也不知道毕业了之后能去哪些公司,能找哪些岗位,对于社会完全不了解,对于考研也是完全不了解。小团队中的三个人都是考金融学,我在网上查,知道了学硕和专硕的区别,也知道专硕学费贵。我家里没钱,大学时期的生活费都是自己去沃尔玛、麦当劳、发传单挣得,大学四年,我在沃尔玛工作超过了 2 年、麦当劳半年,食堂倒盘子半年,中途还去发过传单,暑假还去实习。没钱,他们考金融学专硕,那我就靠经济学学硕吧,学硕学费便宜。


从此开始了考研之路。


2 三次考研


大三的时候,报名不是那么严格,混进去报了名,那会还没开始看书,算是体验了一把考研流程;


还记得那次政治考了 48 分,基本都过了很多学校的单科线,那会就感觉政治最好考(最后发现,还是太年轻)。


大四毕业那年,把所有考研科目的参数书都过了 2 遍,最后上考场,最后成绩也就刚过国家线。


毕业了,也不知道干啥,就听小伙伴的准备再考一次,之前和小伙伴一起来了北京,租了个阳台,又开始准备考研。结果依然是刚过国家线。这一年也多亏了一起来北京的几个同学资助我,否则可能都抗不过考试就饿死街头了。


总结这几次考研经历,失败的最大原因是,我根本不知道考研是为了什么。只是不知道如果工作的话,找什么工作。刚好别人提供了这样一个逃避工作的路,我麻木的跟着走而已。这也是为什么后面两次准备的过程中,一有空就看小说的原因。


但是,现在来看,我会感谢那会没有考上,不然就错过了现在喜欢的技术工作。因为如果真的考上了经济学研究生,我毕业之后也不知道能干啥,而且金融行业的工作我也不喜欢,性格上也不合适,几个小伙伴都是考的金融,去的券商,还是比较了解的。


3 入坑 JAVA 培训


考完之后,大概估了分,知道自己大概率上不了就开始找工作了。那会在前程无忧上各种投简历。开始看到一个做外汇的小公司,因为我在本科在一个工作室做过外汇交易相关的工作,还用程序写了一段量化交易的小程序。


所以去培训了几天,跟我哥借了几千块钱,注册了一个账号,开始买卖外汇。同时在网上找其他工作。


后面看介绍去西二旗的一家公司面试,说我的技术不行,他们提供 Java 培训(以前的套路),没钱可以贷款。


我自己也清楚本科一行 Java 代码没写过,直接工作也找不到工作。就贷款培训了,那会还提供住宿,跟学校宿舍似的,上下铺。


4 三年新手&非全研究生


培训四个月之后,开始找工作。那会 Java 还没这么卷,而且自己还有个 211 学历,一般公司的面试还是不少的。但是因为培训的时候学习不够刻苦(也是没有基础)。最后进了一个小公司,面试要 8000,最后给了 7000。这也是我给自己的最底线工资,少于这个工资就离开北京了,这一年是 2015 年。


这家公司是给政府单位做内部系统的,包括中石油、气象局等。我被分配其中一个组做气象相关系统。第二年末的时候,组内的活对我来说已经没什么难度了,就偷偷在外面找工作,H3C 面试前 3 面都通过了,结果最后大领导面气场不符,没通过。最后被另外一家公司的面试官劝退了。然后公司团建的时候,大领导也极力挽留我,最后没走成。


这次经历的经验教训有 2 个,第 1 个是没有拿到 offer 之前,尽量不要被领导知道。第 2 个是,只要领导知道你要离职,就一定要离职。这次就是年终团建的时候,被领导留下来了。但是第二年以各种理由不给工资。


之前自己就一直在想出路,但是小公司,技术成长有限,看书也对工作没有太大作用,没有太大成长。之后了解到研究生改革,有高中同学考了人大非全。自己也就开始准备非全的考试。最后拿到录取通知书,就开始准备离职了。PS:考研准备


在这家公司马上满 3 年重新签合同的时候,偷偷面试了几家,拿到了 2 个还不错的 offer。第二天就跟直属领导提离职了。这次不管直属领导以及大领导如何劝说,还是果断离职了。


这个公司有两个收获。一个是,了解了一般 Java Web 项目的全流程,掌握了基本开发技能,了解了一些大数据开发技术,如Hadoop套件。另外一个是,通过准备考研的过程,也整理出了一套开发过程中应该先思而后行。只有先整理出


5 五年开发经历


第二家公司是一家央企控股上市公司,市场规模中等。主要给政府提供集成项目。到这家公司第二年就开始带小团队做项目,但是工资很低,可能跟公司性质有关。还好公司有宿舍,有食堂。能省下一些钱。


到这家公司的时候,非全刚好开始上课,还好我们 5 点半就下班,所以我天天卡点下班,大领导天天给开发经理说让我加班。但是第一学期要上课,领导对我不爽,也只能这样了。


后来公司来了一个奇葩的产品经理,但是大领导很挺他,大领导下面有 60 号人,研发、产品、测试都有。需求天天改,还不写在文档上。研发都开发完了,后面发现有问题,要改回去,产品还问,谁让这么改的。


是否按照文档开发,也是大领导说的算,最后你按照文档开发也不对,因为他们更新不及时;不按照文档开发也不对,写了你不用。


最后,研发和产品出差,只能同时去一波人,要是同时去用户现场,会打架。最后没干出成绩,产品和大领导一起被干走了。


后面我们整体调整了部门,部门领导是研发出身。干了几个月之后,领导也比较认可我的能力,让我带团队做一个中型项目,下面大概有 10 号人,包括前后端和算法。也被提升为开发经理。


最后因为工资、工作距离(老婆怀孕,离家太远)以及工作内容等原因,跳槽到了下一家互联网公司。


6 入行互联网


凭借着 5 年的工作经历,还算可以的技术广度(毕竟之前啥都干),985 学校的非全研究生学历,以及还过得去的技术能力。找到了一家知名度还可以的互联网公司做商城开发。


这个部门是公司新成立的部门,领导是有好几家一线互联网经验的老程序员,技术过硬,管理能力强,会做人。组内成员都年轻有干劲。本打算在公司大干一场,涨涨技术深度(之前都是传统企业,技术深度不够,但是广度可以)。


结果因为政策调整,整个部门被裁,只剩下直属领导以及领导的领导。这一年是 2020 年。这个时候,我在这个公司还不到 1 年。


7 再前行


拿着上家公司的大礼包,马上开始改简历,投简历,面试,毕竟还有房贷要还(找了个好老婆,她们家出了大头,付了首付),马上还有娃要养,一天也不敢歇息。


经过一个半月的面试,虽然挂的多,通过的少。最终还是拿了 3 个不错的offer,一个滴滴(滴滴面经)、一个XXX网络(最终入职,薪资跟滴滴基本差不多,技术在市场上认可度也还不错。)以及一个建信金科的offer。


因为大厂部门也和上家公司一样,是新组建的部门,心有余悸。然后也还年轻,不想去银行躺平,也怕银行也不靠谱,毕竟现在都是银行科技公司,干几年被裁,更没有出路。最终入职XXX网络。


8 寒冬


入职XXX网络之后,开始接入公司的各种技术组件,以及看到比较成熟的需求提出、评估、开发、测试、发布规范。也看到公司各个业务中心、支撑中心的访问量,感叹还是大公司好,流程规范,流量大且有挑战性。


正要开心的进入节奏,还没转正呢(3 个月转正),组内一个刚转正的同事被裁,瞬间慌得一批。


刚半年呢,听说组内又有 4 个裁员指标,已经开始准备简历了。幸运的是,这次逃过一劫。


现在已经 1 年多了,在这样一个裁员消息满天飞的年代,还有一份不错的工作,很幸运、也很忐忑,也在慢慢寻找自己未来的路,共勉~


9 总结


整体来看,我对自己的现状还算满意,从一个高中每个月 300 块钱生活费家里都拿不出来;高考志愿填报,填学校看心情想去哪,填专业看专业名字的的村里娃,走到现在在北京有个不错的工作,组建了幸福的家庭,买了个不大不小的房子的城里娃。不管怎么样,也算给自己立足打下了基础,留在了这个有更多机会的城市;也给后代一个更高的起点。


但是,我也知道,现在的状态并不稳固,互联网工作随时可能会丢,家庭成员的一场大病可能就会导致整个家庭回到解放前。


所以,主业上,我的规划就是,尽力提升自己的技术能力和管理能力,争取能在中型公司当上管理层,延迟自己的下岗年龄;副业上,提升自己的写作能力,尝试各种不同的主题,尝试给各个自媒体投稿,增加副业收入。


希望自己永远少年,不要下岗~



作者:六七十三
来源:juejin.cn/post/7173506418506072101
收起阅读 »

一个97年的前端卷不动了,跑去学瑜伽?

大家好, 我是刘子弃。现在是23年5月, 裸辞已经快两个月了。 前两天看到行业前辈左耳朵耗子的不幸消息。 突然有一个想把自己这两个月来的心路历程记录一下的想法。 23年3月, 一个倒霉的周五 那是3月的一天周五,周五本来是打工人比较快乐的一天,但是还没上班就...
继续阅读 »

大家好, 我是刘子弃。现在是23年5月, 裸辞已经快两个月了。 前两天看到行业前辈左耳朵耗子的不幸消息。 突然有一个想把自己这两个月来的心路历程记录一下的想法。



23年3月, 一个倒霉的周五


那是3月的一天周五,周五本来是打工人比较快乐的一天,但是还没上班就预示那天的不平凡。 刚起床准备上班。匆忙的洗漱吃片面包就冲向地铁。刚到地铁口发现手机坏了, 读不出SIM卡。重启也无济于事。


先回家连上wifi到公司群说明情况。 就奔向家附近唯一一家手机维修点。 好不容易到了之后发现今天居然不营业。 只好去旁边花几百块买了一个红米备用机(感谢红米)。


一路坎坷到了公司,通知今天周会要宣布一个大事情。 我们项目组做的是web3相关的业务(我非常热爱这个项目组和每天做的工作内容!)。 之前就有风传出来要去香港落地。 当时还激动了一下, 结果周会宣布:“我们解散啦!”


开始毕业


由于项目突然黄了。 就要考虑转岗或者拿大礼包的事情。 显而易见我选择了拿了大礼包。 为什么不去转岗到其他组呢?其中有对前端这个方向未来发展的考虑, 最重要的考虑还是健康吧。 因为生活不规律, 陆陆续续出现过如下几种身体情况:



  1. 胸口痛

  2. 神经衰弱

  3. 颈椎痛

  4. 失眠

  5. 突然来一下全身无力

  6. 注意力难集中

  7. 脑鸣

  8. 鬼压床看到各种幻觉,甚至有几次都感觉魂都快飘出来了。


就这样, 毫无规划的我就毕业了。


计划恢复健康


本以为不上班了会好一点, 结果每天的状态还是比较差。 去体检万幸没有什么大问题。 不幸的是症状还是一如既往的存在。 最后实在不不行就去了精神科果然有了一点轻度的抑郁症。 所以在决定之后做什么之前, 我决定先养好身体, 恢复健康。 毕竟身体是最重要的。


开始卷起了瑜伽教培


由于之前接触过一些身心灵行业的人。 也有过一些冥想的经验和经历。 在健身和身心灵两个方向中我选择了一个最兼顾和均衡的方向就是练习瑜伽。 索性直接报了一个教培班。 一是恢复身体。二是系统的学习一下防止受伤、更快的练习、避免不正当的危险操作。 并且最后可以拿到认证证书打算之后作为一个长久的职业方向发展一下。 就这样报名了瑜伽教培。 现在已经完成了200小时认证, 6月完成500小时认证。目前身体经过训练确实基本健康了, 症状都没有了。身体舒适了不少。 (或许是不上班都会健康哈哈哈)。 精神也放松了许多, 每天早上起来舒爽+ 没有压力的感觉终于又回来了。


11ca0851fc75aad27b66c4510731059.jpg


之后做什么


当然还是需要考虑收入的问题。 裸辞之后, 如果不干程序员了去干什么。我想不止我一个人想过这个问题。 跑滴滴? 外卖员? 对我来说有点不现实。 但是可能也由不得我选择。现在的就业情况能有一个offer就乐上天了。 所以我想做一个实验。 借各位大佬的光。如果您也想过类似的问题。可以把建议告诉我。 我去实际操作一下。 然后再反馈给大家。


作者:刘子弃
来源:juejin.cn/post/7233589699215147069
收起阅读 »

优 雅 被 裁

后疫情时代的影响,互联网行业每况愈下,而重庆这个地方更是互联网荒漠一般的存在。 上一份工作换的时间是 2022 年的 6 月,仅仅过了一年 3 个月,我又要换工作了,不过这次是被动的。 💡 希望我的经历能给那些正在经历同样遭遇的打工人提供一些参考和启示。 第一...
继续阅读 »

后疫情时代的影响,互联网行业每况愈下,而重庆这个地方更是互联网荒漠一般的存在。


上一份工作换的时间是 2022 年的 6 月,仅仅过了一年 3 个月,我又要换工作了,不过这次是被动的。


💡 希望我的经历能给那些正在经历同样遭遇的打工人提供一些参考和启示。


第一章 - 裁员来袭,初尝挫败



“最初,没有人在意这场灾难,这不过是一场山火,一次旱灾,一个物种的灭绝,一座城市的消失。直到这场灾难和每个人息息相关。” ——《流浪地球》



这份工作开始于 2022 年 6 月 7 日,当时面了挺多公司,那时的市场还算可以,还没有彻底到寒冬,所以手上的 offer 还能让我选选。


这个公司是当时来说给的最高的,理所当然的选了。(岗位高级前端


回看,公司很早就有裁员的预兆 🔍



  1. 22 年 年终奖金没有发全(只发了一半

  2. 业务收缩,大幅缩减新业务拓展

  3. 实习生转正率只有一半

  4. 第一波裁员,开始裁实习生

  5. 开始将开发人员的工作转交给实习生

  6. 第二波裁员,开发、产品 7. 第三波裁员,测试 8. 第四波,到我咯


⛔ 我之前就有了快要到斩杀线的感觉,


因为当你要被裁的时候,你头上就会出现 “危” 字。很难不察觉


第二章 - 未雨绸缪,寻找下家



“我希望你没有把全部鸡蛋放在一个篮子里。” ——《华尔街》



感觉到危险的气息是因为手上本就不多的工作突然加入了同事来接手。


随更新简历 📄,打开求职状态。


😢 不得不说,现在的市场真的偏向用人单位。


在去年的 6 月,每天基本能有 3-5 个 HR 主动来找我询问,当时的公司我还能选择性去面。


而今年的 9 月,基本两周才能有 3-5 个 Hr 来找我,加之现在市面上公司少之又少,刷来刷去就是那么几家。


期间一共面了三家,两家 offer。


一家上升期的公司,注重业务扩展,规模 500 左右。但是之前有朋友在里面说管理混乱加班严重,💩 屎山代码成堆,于是放弃。


另一家人员较少。技术部门 20 人不到,业务跨境金融。无需加班,管理扁平。感觉还不错。


与此同时,由于没有明确的说要裁我。每天都过的很焦虑,一想到 💰 房贷、车贷、房租水电、还有臭宝一堆花销 就开始掉头发。(本来头发就不多


也问过直系领导,但是问了个寂寞,就说人员一直都有在调整。。。


只能两手准备。


拿到 offer 的后心里踏实了不少,但是一直不说什么时候裁,搞得我没法回复那边。


最后找到 “线人” 去问了大领导,确定了月底上完(还好兄弟我平时人缘还不错


第三章 - 运气不错,无缝衔接



“看,前面漆黑一片,什么也看不到。”


“也不是,天亮后会很美的。”


——《喜剧之王》



听到了可靠消息,确定了新公司的入职时间,心里踏实了很多。


过了几天后,领导找了我私聊确定了国庆最后一天走人。


替代文本

赔偿 n+1,自己提离职,钱跟着工资一起发。


替代文本

不知道大家之前在网上看到过一个说法没:不要自己提离职,不要签字,不然赔偿拿不到。


其实心里也挺慌的,问了之前的同事:也是自己提的离职,赔偿给够。


所以就按照流程走了。希望公司还是能遵守约定吧


新公司 10 月 9 号入职。十一期间我可以多休息两天,准备把老头环好好玩玩。


对了,新公司还涨了一些,已经很知足啦。


期待能在新公司能干出一些值得骄傲的项目。


终章 - 自我反思,相信希望



“人生总是这么苦么,还是只有小时候?”


“总是如此。”


——《这个杀手不太冷》



浅浅总结一下,当然只针对我个人体质,不适合所有人哦



  • 现在的环境很复杂,最好每年都去市场上检验一下自己的价值。

  • 如果突然手上的活被人接手了,要警惕

  • 关注一下公司的财务状况,不要被打个措手不及

  • 尽量放平心态,不要再给自己增加压力啦,难度已经很高了

  • 不要放弃学习,舒适圈里待太久会丧失动力

  • 好好活着,这条适合所有人🦾


作者:前端小蜗
来源:juejin.cn/post/7283151314024497209
收起阅读 »

最近的生活

上一篇文章是8.4号写的,一个多月没有写东西了,按照现在的情况估计,年底要写到40篇有点悬,虽然有很多要写的,但现在事情太多了也太累了。今天写点随笔,写到哪算哪吧。 工作 这次参与的新项目,前景还是不错的,不过活也是真多。这段日子,很多时候十一二点才下班,有的...
继续阅读 »

上一篇文章是8.4号写的,一个多月没有写东西了,按照现在的情况估计,年底要写到40篇有点悬,虽然有很多要写的,但现在事情太多了也太累了。今天写点随笔,写到哪算哪吧。


工作


这次参与的新项目,前景还是不错的,不过活也是真多。这段日子,很多时候十一二点才下班,有的时候搞到两点,周末再加一天班。


业务快速发展,不断地有新同学加入,架构也在不断迭代,其实感觉还是蛮不错的,有点像当初刚工作负责电商的时候了,始终创业啊。


雷总曾经说过,要顺势而为,其实是对的,选对方向往往事半功倍,但选对方向需要极强的能力。新的变革已经到来了。


文化


这里的文化是指公司文化,扩展一下也指家庭文化。为什么突然聊文化?


最近感觉无论是家庭还是公司,让大家聚集在一起努力的,相同的文化或者三观是重要的一环。文化认同不一致,很难长久的在一起,这个没有对错,每个人都有选择的权利,没必要强求,很多时候祝福就好。


还是想夸一下字节的文化,虽然看过很多公司的文化宣言,感觉字节的带着哲思在里面,这种文化不是只对公司有利,而是说在自己的生活中,用这种文化来要求自己也是好的。认同这种文化的人在一起,办事效率、质量要高很多,很多时候,损失来自于内耗。


1.1追求极致


不断提高要求,延迟满足感


在更大范围里找最优解


不放过问题,思考本质


持续学习和成长


1.2务实敢为


直接体验,深入事实


不自嗨,注重效果


能突破有担当,打破定式


尝试多种可能,快速迭代


1.3开放谦逊


内心阳光,信任伙伴


乐于助人和求助,合作成大事


格局大,上个台阶想问题


对外敏锐谦虚,ego(自我) 小,听得进意见


1.4坦诚清晰


敢当面表达真实想法


能承认错误,不装不爱面子


实事求是,暴露问题,反对“向上管理”


准确、简洁、直接,有条理有重点


1.5始终创业


自驱,不设边界,不怕麻烦


有韧性,直面现实并改变它


拥抱变化,对不确定性保持乐观


始终像公司创业第一天那样思考


1.6多元兼容


理解并重视差异和多元,建立火星视角


打造多元化的团队,欢迎不同背景的人才,激发潜力


鼓励人人参与,集思广益,主动用不同的想法来挑战自己


创造海纳百川,兼容友好的工作环境


教育


最近在想,怎么教育好下一代?或者话题小一点,如何在知道A选项不好的情况下,让子女听自己的?


以前看过一篇文章,说是孩子们总归不会听你的,但他们也终会在跌跌撞撞中长大,然后他们的子女再来一次循环。


但我觉得,还是有可能教育好的,不过要付出很多,这是一个细雨润无声,充斥在点点滴滴生活中的事情,它永远不是一个一次性任务,或者说几次道理就能达成的。


拿选择来说,需要做到

  1. 父母本身就对每种选择的结果比较知晓

  2. 父母很了解子女的性格和能力

  3. 子女相对相信父母

  4. 或者 子女已经培养的很好了,有了自己的主见和三观,知道自己的性格和能力


如果能培养到第四点,那真是轻松很多。不过呀,最重要的还是得立志,论语里说:“不愤不启,不悱不发,举一隅不以三隅反,则不复也。”也是这个道理。立志能给人以动力,自己主观上想干了,才能干好。


家庭


最近媳妇工作上的事情也比较多,我感觉很神奇,好像每次事情都会像商量好似得一起来,这时候考验的就是毅力和耐力,不松气,努力干,总能顶过这一波。


或许真像媳妇说的,人生就像一场游戏,努力就完事了,别想太多。


前些日子和媳妇都阳了,好在不太严重,也不知道什么时候是个头。看到满满的小药箱,比起去年12月的时候,还是感觉安全一些的。


哦,对了,前些日子公司冷藏柜漏水,导致我摔了一跤,电脑都飞出去了。怎么说呢,幸亏电脑没事,就人伤着一点,哈哈哈。本来想投诉一下,但负责人一直在会议室门口等我们会议结束,又道歉又拿药,加了联系方式方便后面有问题及时联系;同时讲了原因和后续的改进措施。做的挺不错的。


以前对摔倒的影响概念不深,现在倒蛮有体会的了,有时候在想,如果六七十岁的人,以这个力道被摔,真的很危险。大家还是要多多注意。


作者:程序员麻辣烫
链接:https://juejin.cn/post/7276368438145859603
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

职场坐冷板凳的那些日子

曾经有一段职场生涯,坐了很长时间的冷板凳,也正是那段经历,彻底改变了整个职场生涯。今天这篇文章聊聊自己曾经的经历,也聊聊如果在职场中被坐了冷板凳该咋办。 关于冷板凳 有人的地方就有江湖。而这个江湖中是否性情相同,是否因某些事(或利益)产生矛盾,都可能造成职场坐...
继续阅读 »

曾经有一段职场生涯,坐了很长时间的冷板凳,也正是那段经历,彻底改变了整个职场生涯。今天这篇文章聊聊自己曾经的经历,也聊聊如果在职场中被坐了冷板凳该咋办。


关于冷板凳


有人的地方就有江湖。而这个江湖中是否性情相同,是否因某些事(或利益)产生矛盾,都可能造成职场坐冷板凳的情况。


冷板凳常见于上级对下级的打压。一般手段就是让你无所事事或安排一些边缘性的事务,不怎么搭理你,从团队层面排挤你,甚至否定你或PUA你,别人也不敢跟你沟通,以至于让你在团队中形成孤立的的状态。


根据矛盾或冲突的不同,冷板凳的程度也不同。常见的有:浅层次的冲突,可进行修复;不可调和,无法修复;中间的灰度状态。


通常根据具体情况,判断程度,有没有可能或必要修复,再决定下一步的行动。


第一,可修复的冷板凳


有很多同学,特别是技术人,在职场上有时候特别的“刚”,为了某个技术点跟领导争的面红耳赤的,导致被坐冷板凳。


比如有同学曾有这样的经历:领导已经拍板的决定,他很刚的去跟领导据理力争,导致起了冲突,大吵一架,领导也下不来台。随后领导好几天没搭理他。


针对这种情况,一般也就是一顿火锅的事,找领导主动沟通,重拾信任。甚至可能会出现不打不相识的情况。当然,一顿火锅不够还可以两顿。


第二,清场性质的冷板凳


这种情况常见于业绩或能力不达标,已经是深层次的矛盾,一般会空降过来一个领导,故意将其边缘化。属于清场接替工作性质的,基本上无法修复。


针对这种情况,看清局势,准备好找下家就是了。如果做得好,准备好交接工作,给彼此一个体面。毕竟,很多事情我们是无法改变的。


第三,灰度状态的冷板凳


以上两个常见都比较极端,而大多数情况下都是灰度状态的,大的可能性就是一直僵持着。这时作为下属的人,一般建议主动去沟通、修复。


如果阅历比较浅,看不出中间的微妙关系以及深层次的冲突点,就请人帮你看看,听听别人的建议和决策。再决定值不值得修复,要不要修复。


我的冷板凳


曾经我在一家公司坐的冷板凳属于第三种,但却把这个冷板凳坐到了极致。下面就讲讲我曾经的故事。


跟着一个领导到一家新公司,本来领导带领技术部门的,但由于内部斗争的失利,去带产品团队了,而我也归属到他对手的手下了。这种情况下,冷板凳是坐定了,但也不至于走人。


被新领导安排了一个很边缘的业务:对接和维护一套三方的系统。基本上处于不管不问,开会不带,接触不到核心,也与其他人无交流的状态。起初这种状态非常难受,人毕竟是社群动物,需要一个归属感和存在感的。


但慢慢的,自己找到了一些属于自己的乐趣。


首先,没人管没人问,那就可以自己掌控节奏和状态了。看他们天天加班到凌晨一两点,而自己没人管,六七点就下班了。最起码在那段持续疯狂加班的岁月里,自己保住了头发。那位大领导后来加班太多,得了重病,最终位置也没保住。


其次,有了大把的时间。上班几乎没人安排工作,于是上班的时间完全自己安排。三方服务商安排了对接人,好歹自己作为甲方,于是天天就跟服务商的技术沟通,询问他们系统的设计实现,技术栈什么的。


在那段岁月里,完成了几个改变后续职场生涯的事项。


事项一:那时Spring Boot 1.5刚刚发布,公司的技术栈还没用上,但服务商的这套系统已经用上了。感觉这玩意太好用了,于是疯狂的学学习。因为当初的学习,后来出版了书籍《Spring Boot技术内幕》那本书。


事项二:写技术博客,翻译技术文档,录技术视频。服务商的系统中还用到了规则引擎,当时市面上没有相关的中文资料。于是边跟对方技术沟通,边翻译英文文档,写博客。后来,还把整理的文档录制成视频,视频收入有几万块吧。


这算是自己第一次尝试翻译文档、录制教学视频,而且这个领域网络上后续的很多技术文章都是基于我当初写文章衍生出来的。最近,写的第二本书便是关于规则引擎的,坐等出版了。


事项三:学习新技术,博客输出。当时区块链正火爆时。由于有大量的时间,于是就研究起来了,边研究边写技术博客。也是在这个阶段,养成了写技术博客的习惯。


因为区块链的博客,也找到了下家工作。同时写了CSDN当时类似极客时间的“Chat”专栏,而且是首批作者。也尝试搞了区块链的知识星球。后来,因为区块链的工作,做了第一次公开课的分享。还是因为区块链相关,与别人合著了一本书,解释了出版社的老师,这也是走上出书之路的开始。


因为这次冷板凳,让职场生涯变得极其丰富,也扭转了大的方向,发展了副业,接触了不同行业领域的人。


最后的小结


在职场混,遇到坐冷板凳的情况不可避免,但如何化解,如何抉择却是一个大学问。尽量主动沟通,毕竟找工作并不容易,也不能保证下家会更好。同时,解决问题,也是人生成长的一部分,所以,尽量尝试化解。


但如果矛盾真的不可调和或持续僵持,那么就更好做好决策,选择对自己最有利的一面。


曾在朋友圈发过这样一段话,拿来与大家分享:


“始终难守樊登讲过的一句话:人生成长最有效的方法,就是无论命运把你抛在任何一个点上,你就地展开做力所能及的事情。


如果还要加上一句,那就是:还要占领制高点。与君共勉~”


作者:程序新视界
链接:https://juejin.cn/post/7267107420655583292
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

UIButton 扩大点击区域

iOS
在开发过程中经常会遇到设计给出的button尺寸偏小的情况.这种UIButton在使用中会非常难点击,极大降低了用户体验 解决方案一:重写UIButton的- (BOOL)pointInside:(CGPoint)point withEvent:(UIEven...
继续阅读 »

在开发过程中经常会遇到设计给出的button尺寸偏小的情况.这种UIButton在使用中会非常难点击,极大降低了用户体验


解决方案一:重写UIButton的- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event方法

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event

{

//获取当前button的实际大小
CGRect bounds = self.bounds;

//若原热区小于44x44,则放大热区,否则保持原大小不变

CGFloat widthDelta = MAX(44.0 - bounds.size.width, 0);

CGFloat heightDelta = MAX(44.0 - bounds.size.height, 0);
//扩大bounds

bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);

//如果点击的点 在 新的bounds里,就返回YES

return CGRectContainsPoint(bounds, point);

}

系统默认写法是:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
return CGRectContainsPoint(self.bounds, point);
}

其实是在判断的时候对响应区域的bounds进行了修改.CGRectInset(view, 10, 20)方法表示对rect大小进行修改


解决方案二 runtime关联对象来改变范围,- (UIView) hitTest:(CGPoint) point withEvent:(UIEvent) event里用新设定的 Rect 来当着点击范围。

#import "UIButton+EnlargeTouchArea.h"
#import <objc/runtime.h>

@implementation UIButton (EnlargeTouchArea)

static char topNameKey;
static char rightNameKey;
static char bottomNameKey;
static char leftNameKey;

- (void)setEnlargeEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left
{
objc_setAssociatedObject(self, &topNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &rightNameKey, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &bottomNameKey, [NSNumber numberWithFloat:bottom], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &leftNameKey, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (void)setTouchAreaToSize:(CGSize)size
{
CGFloat top = 0, right = 0, bottom = 0, left = 0;

if (size.width > self.frame.size.width) {
left = right = (size.width - self.frame.size.width) / 2;
}

if (size.height > self.frame.size.height) {
top = bottom = (size.height - self.frame.size.height) / 2;
}

[self setEnlargeEdgeWithTop:top right:right bottom:bottom left:left];
}

- (CGRect)enlargedRect
{
NSNumber *topEdge = objc_getAssociatedObject(self, &topNameKey);
NSNumber *rightEdge = objc_getAssociatedObject(self, &rightNameKey);
NSNumber *bottomEdge = objc_getAssociatedObject(self, &bottomNameKey);
NSNumber *leftEdge = objc_getAssociatedObject(self, &leftNameKey);
if (topEdge && rightEdge && bottomEdge && leftEdge)
{
return CGRectMake(self.bounds.origin.x - leftEdge.floatValue,
self.bounds.origin.y - topEdge.floatValue,
self.bounds.size.width + leftEdge.floatValue + rightEdge.floatValue,
self.bounds.size.height + topEdge.floatValue + bottomEdge.floatValue);
}
else
{
return self.bounds;
}
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGRect rect = [self enlargedRect];
if (CGRectEqualToRect(rect, self.bounds) || self.hidden)
{
return [super hitTest:point withEvent:event];
}
return CGRectContainsPoint(rect, point) ? self : nil;
}

@end


解决方案三:使用runtime swizzle交换IMP

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSError *error = nil;
[self hg_swizzleMethod:@selector(pointInside:withEvent:) withMethod:@selector(hitTest_pointInside:withEvent:) error:&error];
NSAssert(!error, @"UIView+HitTest.h swizzling failed: error = %@", error);
});
}

- (BOOL)hitTest_pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) {
return [self hitTest_pointInside:point withEvent:event];
}
CGRect relativeFrame = self.bounds;
CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}



category的诞生只是为了让开发者更加方便的去拓展一个类,它的初衷并不是让你去改变一个类。



技术点总结


关联对象,也就是绑定对象,可以绑定任何东西

//关联对象
objc_setAssociatedObject(self, &topNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
// self 关联的类,
//key:要保证全局唯一,key与关联的对象是一一对应关系。必须全局唯一
//value:要关联类的对象。
//policy:关联策略。有五种关联策略。
//OBJC_ASSOCIATION_ASSIGN 等价于 @property(assign)。
//OBJC_ASSOCIATION_RETAIN_NONATOMIC等价于 @property(strong, //nonatomic)。
//OBJC_ASSOCIATION_COPY_NONATOMIC等价于@property(copy, nonatomic)。
//OBJC_ASSOCIATION_RETAIN等价于@property(strong,atomic)。
//OBJC_ASSOCIATION_COPY等价于@property(copy, atomic)。

NSNumber *topEdge = objc_getAssociatedObject(self, &topNameKey);

// 方法说明
objc_setAssociatedObject 相当于 setValue:forKey 进行关联value对象

objc_getAssociatedObject 用来读取对象

objc_AssociationPolicy 属性 是设定该value在object内的属性,即 assgin, (retain,nonatomic)...等

objc_removeAssociatedObjects 函数来移除一个关联对象,或者使用objc_setAssociatedObject函数将key指定的关联对象设置为nil。

方法交换 Method Swizzling 注意点


对于已经存在的类,我们通常会在+load方法,或者无法获取到类文件,我们创建一个分类,也通过其+load方法进行加载swizzling


  • Swizzling应该总在+load中执行
  • Swizzling应该总是在dispatch_once中执行
  • Swizzling在+load中执行时,不要调用[super load]。如果多次调用了[super load],可能会出现“Swizzle无效”的假象。

交换实例方法


以class为类

void class_swizzleInstanceMethod(Class class, SEL originalSEL, SEL replacementSEL)
{
//class_getInstanceMethod(),如果子类没有实现相应的方法,则会返回父类的方法。
Method originMethod = class_getInstanceMethod(class, originalSEL);
Method replaceMethod = class_getInstanceMethod(class, replacementSEL);

//class_addMethod() 判断originalSEL是否在子类中实现,如果只是继承了父类的方法,没有重写,那么直接调用method_exchangeImplementations,则会交换父类中的方法和当前的实现方法。此时如果用父类调用originalSEL,因为方法已经与子类中调换,所以父类中找不到相应的实现,会抛出异常unrecognized selector.
//当class_addMethod() 返回YES时,说明子类未实现此方法(根据SEL判断),此时class_addMethod会添加(名字为originalSEL,实现为replaceMethod)的方法。此时在将replacementSEL的实现替换为originMethod的实现即可。
//当class_addMethod() 返回NO时,说明子类中有该实现方法,此时直接调用method_exchangeImplementations交换两个方法的实现即可。
//注:如果在子类中实现此方法了,即使只是单纯的调用super,一样算重写了父类的方法,所以class_addMethod() 会返回NO。

//可用BaseClass实验
if(class_addMethod(class, originalSEL, method_getImplementation(replaceMethod),method_getTypeEncoding(replaceMethod)))
{
class_replaceMethod(class,replacementSEL, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
}else {
method_exchangeImplementations(originMethod, replaceMethod);
}
}


这里存在的问题是继承时子类没有实现父类方法的问题:
基类A类 有方法 -(void)test
子类B类继承自基类A,但没有重写test方法,即其类[B class]中没有test这个实例方法
当我们交换子类B中的方法test,交换为testRelease方法(这必然会在子类B中写testRelease的实现),子类B中有没有test方法的实现时,就会将基类A的test方法与testRelease替换,当仅仅使用子类B时,不会有问题。
但当我们使用基类A的test方法时,由于test指向的IMP是原testRelease的IMP,而基类A中没有这个实现,因为我们是写在子类B中的。所以就出现了unrecognized selector



交换类方法


由于类方法存储在元类中,以实例方法存在,所以实质就是交换元类的实例方法
上面交换实例方法基础上,传入cls为元类即可。
获取的元类可以这样objc_getMetaClass("ClassName")或者object_getclass([NSObject class])


事件响应者链


如图所示,不再赘述



 两个重要的方法

- (nullable UIView*)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;称为方法A

- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;称为方法B

对view进行重写这两个方法后,点击屏幕后,首先响应的是方法A;

  • 如果方法A中,我们没有调用父类([super hitTest:point withEvent:event];)的这个方法,那就根据这个方法A的返回view,作为响应事件的view。(当然返回nil,就是这个view不响应)

  • 如果方法A中,我们调用了父类的方法([super hitTest:point withEvent:event];)那这个时候系统就要调用方法B;通过这个方法的返回值,来判断当前这个view能不能响应消息

  • 如果方法B返回的是no,那就不用再去遍历它的子视图。方法A返回的view就是可以响应事件的view。

  • 如果方法B返回的是YES,那就去遍历它的子视图。(就是上图我们描述的那样,找到合适的view返回,如果找不到,那就由方法A返回的view去响应这个事件。)


总结


返回一个view来响应事件 (如果不想影响系统的事件传递链,在这个方法内,最好调用父类的这个方法)

- (nullable UIView*)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event{
    return [super hitTest:point withEvent:event];
}

返回的值可以用来判断是否继续遍历子视图(返回的根据是触摸的point是否在view的frame范围内)

- (BOOL)pointInside:(CGPoint)point withEvent:(nullableUIEvent *)event;      

作者:Perry_6
链接:https://juejin.cn/post/6943628003976937508
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
收起阅读 »

手把手教你集成环信ReactNative离线推送(下)

点此链接查看:手把手教你集成环信ReactNative离线推送(上)三、从原生将device_token 传到RN 并且绑定1、原生调用方法 reactContext.getJSModule(DeviceEventManagerModule.RCTDevice...
继续阅读 »

点此链接查看:手把手教你集成环信ReactNative离线推送(上)

三、从原生将device_token 传到RN 并且绑定

1、原生调用方法


reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("deviceToken",jsonObject.toString());

通过PushModule 类进行传递,PushModule 代码如下:


package com.awesomeproject;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.xiaomi.mipush.sdk.MiPushClient;
import org.json.JSONException;
import org.json.JSONObject;

public class PushModule extends ReactContextBaseJavaModule {
private ReactApplicationContext reactContext;
public PushModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}

@Override
public String getName() {
return "PushModule";
}
/**
从RN界面里面调用该方法
**/

@ReactMethod
public void getDeviceToken(){
MainApplication.getReactPackage().mModule.sendDataToJS( MiPushClient.getRegId(MainApplication.getContext()));


}

public void sendDataToJS(String deviceToken){
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("deviceToken",deviceToken);
jsonObject.put("deviceName","2882303761517520571");

} catch (JSONException e) {
throw new RuntimeException(e);
}

this.reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("deviceToken",jsonObject.toString());
}



}

2、RN 层进行获取数据


NativeModules.PushModule.getDeviceToken();
DeviceEventEmitter.addListener('deviceToken',(res)=>{
const goosid = JSON.parse(res);
deviceToken = goosid.deviceToken;
manufacturer = goosid.deviceName;
console.log('React Native界面,收到数据:',goosid);

3、获取到数据后调用环信RN sdk 方法进行绑定

ChatClient.getInstance().updatePushConfig(push);

js 代码如下

// 导入依赖库
import React, { useEffect } from 'react';
import {
DeviceEventEmitter,
NativeModules,
SafeAreaView,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import {
ChatClient,
ChatMessage,
ChatMessageChatType,
ChatOptions,
ChatPushConfig,
} from 'react-native-chat-sdk';
// 创建 app
const App = () => {
// 进行 app 设置
const title = 'ChatQuickstart';
var deviceToken='';
var manufacturer='';
NativeModules.PushModule.getDeviceToken();
DeviceEventEmitter.addListener('deviceToken',(res)=>{
const goosid = JSON.parse(res);
deviceToken = goosid.deviceToken;
manufacturer = goosid.deviceName;
console.log('React Native界面,收到数据:',goosid);

})
const [appKey, setAppKey] = React.useState('1137220225110285#demo');
const [username, setUsername] = React.useState('p9');
const [password, setPassword] = React.useState('1');
const [userId, setUserId] = React.useState('');
const [content, setContent] = React.useState('');
const [logText, setWarnText] = React.useState('Show log area');

// 输出 console log 文件
useEffect(() => {
logText.split('\n').forEach((value, index, array) => {
if (index === 0) {
console.log(value);
}
});
}, [logText]);

// 输出 UI log 文件
const rollLog = text => {
setWarnText(preLogText => {
let newLogText = text;
preLogText
.split('\n')
.filter((value, index, array) => {
if (index > 8) {
return false;
}
return true;
})
.forEach((value, index, array) => {
newLogText += '\n' + value;
});
return newLogText;
});
};

// 设置消息监听器。
const setMessageListener = () => {
let msgListener = {
onMessagesReceived(messages) {
for (let index = 0; index < messages.length; index++) {
rollLog('received msgId: ' + messages[index].msgId);
}
},
onCmdMessagesReceived: messages => {},
onMessagesRead: messages => {},
onGroupMessageRead: groupMessageAcks => {},
onMessagesDelivered: messages => {},
onMessagesRecalled: messages => {},
onConversationsUpdate: () => {},
onConversationRead: (from, to) => {},
};

ChatClient.getInstance().chatManager.removeAllMessageListener();
ChatClient.getInstance().chatManager.addMessageListener(msgListener);
};

// SDK 初始化。
// 调用任何接口之前,请先进行初始化。
const init = () => {

let option = new ChatOptions({
autoLogin: false,
appKey: appKey
});
ChatClient.getInstance().removeAllConnectionListener();
ChatClient.getInstance()
.init(option)
.then(() => {
rollLog('init success');
this.isInitialized = true;
let listener = {
onTokenWillExpire() {
rollLog('token expire.');
},
onTokenDidExpire() {
rollLog('token did expire');
},
onConnected() {
rollLog('login success.');
setMessageListener();
},
onDisconnected(errorCode) {
rollLog('login fail: ' + errorCode);
},
};
ChatClient.getInstance().addConnectionListener(listener);
})
.catch(error => {
rollLog(
'init fail: ' +
(error instanceof Object ? JSON.stringify(error) : error),
);
});
};

// 注册账号。
const registerAccount = () => {
if (this.isInitialized === false || this.isInitialized === undefined) {
rollLog('Perform initialization first.');
return;
}
rollLog('start register account ...');
ChatClient.getInstance()
.createAccount(username, password)
.then(response => {
rollLog(`register success: userName = ${username}, password = ******`);
})
.catch(error => {
rollLog('register fail: ' + JSON.stringify(error));
});
};

// 用环信即时通讯 IM 账号和密码登录。
const loginWithPassword = () => {
if (this.isInitialized === false || this.isInitialized === undefined) {
rollLog('Perform initialization first.');
return;
}
rollLog('start login ...');
ChatClient.getInstance()
.login(username, password)
.then(() => {
rollLog('login operation success.');
let push = new ChatPushConfig({
deviceId:manufacturer,
deviceToken:deviceToken,

});
console.log("--------------------------------------------");
console.log(manufacturer);
console.log(deviceToken);
console.log("--------------------------------------------");
ChatClient.getInstance().updatePushConfig(push);
})
.catch(reason => {
rollLog('login fail: ' + JSON.stringify(reason));
});
};

// 登出。
const logout = () => {
if (this.isInitialized === false || this.isInitialized === undefined) {
rollLog('Perform initialization first.');
return;
}
rollLog('start logout ...');
ChatClient.getInstance()
.logout()
.then(() => {
rollLog('logout success.');
})
.catch(reason => {
rollLog('logout fail:' + JSON.stringify(reason));
});
};

// 发送一条文本消息。
const sendmsg = () => {
if (this.isInitialized === false || this.isInitialized === undefined) {
rollLog('Perform initialization first.');
return;
}
let msg = ChatMessage.createTextMessage(
userId,
content,
ChatMessageChatType.PeerChat,
);
const callback = new (class {
onProgress(locaMsgId, progress) {
rollLog(`send message process: ${locaMsgId}, ${progress}`);
}
onError(locaMsgId, error) {
rollLog(`send message fail: ${locaMsgId}, ${JSON.stringify(error)}`);
}
onSuccess(message) {
rollLog('send message success: ' + message.localMsgId);
}
})();
rollLog('start send message ...');
ChatClient.getInstance()
.chatManager.sendMessage(msg, callback)
.then(() => {
rollLog('send message: ' + msg.localMsgId);
})
.catch(reason => {
rollLog('send fail: ' + JSON.stringify(reason));
});
};

// UI 组件渲染。
return (
<SafeAreaView>
<View style={styles.titleContainer}>
<Text style={styles.title}>{title}</Text>
</View>
<ScrollView>
<View style={styles.inputCon}>
<TextInput
multiline
style={styles.inputBox}
placeholder="Enter appkey"
onChangeText={text => setAppKey(text)}
value={appKey}
/>
</View>
<View style={styles.buttonCon}>
<Text style={styles.btn2} onPress={init}>
INIT SDK
</Text>
</View>
<View style={styles.inputCon}>
<TextInput
multiline
style={styles.inputBox}
placeholder="Enter username"
onChangeText={text => setUsername(text)}
value={username}
/>
</View>
<View style={styles.inputCon}>
<TextInput
multiline
style={styles.inputBox}
placeholder="Enter password"
onChangeText={text => setPassword(text)}
value={password}
/>
</View>
<View style={styles.buttonCon}>
<Text style={styles.eachBtn} onPress={registerAccount}>
SIGN UP
</Text>
<Text style={styles.eachBtn} onPress={loginWithPassword}>
SIGN IN
</Text>
<Text style={styles.eachBtn} onPress={logout}>
SIGN OUT
</Text>
</View>
<View style={styles.inputCon}>
<TextInput
multiline
style={styles.inputBox}
placeholder="Enter the username you want to send"
onChangeText={text => setUserId(text)}
value={userId}
/>
</View>
<View style={styles.inputCon}>
<TextInput
multiline
style={styles.inputBox}
placeholder="Enter content"
onChangeText={text => setContent(text)}
value={content}
/>
</View>
<View style={styles.buttonCon}>
<Text style={styles.btn2} onPress={sendmsg}>
SEND TEXT
</Text>
</View>
<View>
<Text style={styles.logText} multiline={true}>
{logText}
</Text>
</View>
<View>
<Text style={styles.logText}>{}</Text>
</View>
<View>
<Text style={styles.logText}>{}</Text>
</View>
</ScrollView>
</SafeAreaView>
);
};

// 设置 UI。
const styles = StyleSheet.create({
titleContainer: {
height: 60,
backgroundColor: '#6200ED',
},
title: {
lineHeight: 60,
paddingLeft: 15,
color: '#fff',
fontSize: 20,
fontWeight: '700',
},
inputCon: {
marginLeft: '5%',
width: '90%',