新年快乐”的多语言版本

新年快乐”的多语言版本

请允许我用21种编程语言,代表 Python技术团队祝大家2021年新年快乐!

Python
print(“Happy New Year!”)
java
System.out.println(“Happy New Year!”);
C
printf(“Happy New Year!”);
C++
cout<<“Happy New Year!”;
C#
System.Console.WriteLine(“Happy New Year!”)
Objective-C
NSLog(@“Happy New Year!”)
QBasic
Print “Happy New Year!”
Asp
Response.Write “Happy New Year!”
PHP
echo “Happy New Year!”;
JS
alert(“Happy New Year!”)
VBScript
MsgBox “Happy New Year!”
go
fmt.Println(“Happy New Year!”)
Delphi
ShowMessage(‘Happy New Year!’);
VB
Print “Happy New Year!”
VC
MessageBox(“Happy New Year!”);
shell
echo Happy New Year!
perl
print ‘Happy New Year!’
nodejs
console.log(“Happy New Year!”)
COBOL
DISPLAY ‘Happy New Year!
AS
Alert.show(“Happy New Year!”);
Foxpro
? 【Happy New Year!】

当代码遇到文学…

当代码遇到文学…

 

一提到程序员,大家的*印象都是性格内向,不善言语,衣服搭配全是格子衫加牛仔裤。实则不然,别看我们整天和机器打交道,一天下来可能也说不了几句话,但我们程序员群体也是人呀,也有七情六欲,也是懂浪漫的,今天派森酱就带你领略下程序员另类的浪漫。

想必大家都给自己的对象写过情诗吧,打开信封的那一刻,她激动又开心的样子一定可爱*了。今天咱们就来看看程序员用代码写给对象的情诗。

0x00
遇见你
生命才在增加
否则只是虚耗

while True:
if meet_you: life++
else: life–
0x01
在 75 亿茫茫人海中
看见你的*眼
就永远爱上了你

while random(0, 7_500_000_000) != you: continue
while True:
love(you)
0x02
自从遇见了你,就不停的想你

void missing_you() {}
for (time = meet_you; ;time++)
missing_you()
0x03
我就在这里,等风也等你

you.come() ? rain.stop() : I.wait()
0x04
爱你一万年

while year <= 10000:
print(“I Love You!”)
year++
0x05
生活中少不了意外
但只要我们共同面对
就能天长地久

try: our_life()
except: face_together()
finally: love_forever()
0x06
我向往明天的生活
可当你离开那一刻
它就停留在了昨天

MyLife = {“I”: “Yesterday”, “You”: “Tomorrow”}
del MyLife[“You”]
MyLife.values()
0x07
我是乘风破浪归来的舵手
却在你寂静深邃的眼波里
弃浆溺亡

Helmsman me = new Helmsman()
while(!me.meet(you)){me.navigate}
me.drown()
0x08
爱你到天荒地老

while(!world.destroy()){
me.loveYou()
}
0x09
夏雨雪,江水竭,冬雷震震,山无陵,天地合,乃敢与君*

if(summer.snow==True
&& river.water==None
&& winter.thunder==True
&& mountain.arris==None
&& sky.height==ground.height):
I.withyou=False
else:
I.withyou=True

弃繁就简!一行代码搞定 Python 日志!

弃繁就简!一行代码搞定 Python 日志!

 

写了这么多年的 Python ,我一直都是使用 Python 自带的 logging 模块来记录日志,每次需要写一些配置将日志输出到不同的位置,设置不同日志输出格式,或者将日志进行分文件和压缩等。这个日志模块没什么问题,直到我无意中发现了一个神器,我才发觉原来记日志可以这么简单的!这个神器就是 loguru 。

安装
这个库的安装方式很简单,直接使用 pip 就可以,我使用 Python 3 版本,安装命令如下:

pip3 install loguru
小试牛刀
安装完毕之后,我们就可以使用了,*简单的使用方式:

from loguru import logger

logger.debug(‘this is a debug message’)

无需任何配置,即取即用。上例是打印一条 debug 级别的日志,输出结果如下:

2021-03-16 22:17:23.640 | DEBUG    | __main__:<module>:8 – this is a debug message

这条输出日志信息包含了日期、时间、日志级别、日志代码行数以及日志内容信息。可以说*基本的内容都囊括了,当然你还可以打印 warning、info、error、critical、success 等级别。输出的日志在 console 中还带有高亮颜色,并且每个级别的日志颜色不一样,简直不要太酷!

日志文件
写文件
在loguru中,输出日志文件只需要一个 add() 函数即可:

logger.add(‘hello.log’)

logger.debug(‘i am in log file’)

这时候,在 console 中会正常打印日志信息,在同级目录下会生成一个日志文件 hello.log ,我们打开日志文件,可以看到内容如下:

2021-03-16 21:20:31.460 | DEBUG    | __main__:<module>:12 – i am in log file

当然,我们还可以加一些参数,来指定文件中日志输出的格式、级别:

log = logger.add(‘world.log’, format=”{time} | {level} | {message}”, level=”INFO”)

logger.debug(‘i am debug message’)
logger.info(‘i am info message’)

对应的文件输出信息如下:

2021-03-16T22:47:53.226998+0800 | INFO | i am info message

我们设置了文件只记录 info 级别的信息,所以 debug 级别的日志信息并没有写入日志文件。

我们也可以给日志文件名称加信息:

logger.add(‘hello_{time}.log’)

上面的代码运行后,会生成一个带时间的日志文件。

停止写入文件
当我们不再需要将日志写入文件时,我们随时可以停止:

id = logger.add(‘world.log’, format=”{time} | {level} | {message}”, level=”INFO”)
logger.info(‘this is a info message’)
logger.remove(id)
logger.info(‘this is another info message’)

add() 方法会返回一个日志文件的 id ,当我们需要停止写入信息时,我们使用 remove() 方法,传入 id ,即可。上面代码运行后,日志文件记录的信息如下:

2021-03-16T22:47:53.227389+0800 | INFO | this is a info message

在调用 remove() 方法后,其后面的日志信息并没有写入日志文件中。

滚动记录日志文件
我们可以配置 rotation 参数,来指定日志文件的生成方式,跟通常的日志记录一样,我们可以设置按照文件大小、时间、日期等来指定生成策略。

# 超过200M就新生成一个文件
logger.add(“size.log”, rotation=”200 MB”)
# 每天中午12点生成一个新文件
logger.add(“time.log”, rotation=”12:00″)
# 一周生成一个新文件
logger.add(“size.log”, rotation=”1 week”)

指定日志文件的有效期
我们还可以通过 retention 参数来指定日志文件的保留时长:

logger.add(“file.log”, retention=”30 days”)

通过上面的配置,可以指定日志文件*多保留30天,30天之前的日志文件就会被清理掉。

配置压缩文件
为了节省空间,我们可能存在压缩日志文件的需求,这个 loguru 也可以实现:

logger.add(“file.log”, compression=”zip”)

通过上面的配置,我们指定了日志文件的压缩格式为 zip 。

异常捕获
loguru 不仅可以记录日志,还可以捕获异常信息,这个 可以帮助我们更好地追溯错误原因。

在 loguru 模块中,我们通常有两种异常捕获方式:通过 catch 装饰器捕获和通过 exception 方法捕获。

catch 装饰器捕获异常
我们来看一个例子:

@logger.catch
def a_function(x):
return 1 / x

a_function(0)

输出信息如下:

021-03-16 23:10:28.124 | ERROR    | __main__:<module>:32 – An error has been caught in function ‘<module>’, process ‘MainProcess’ (25939), thread ‘MainThread’ (140735895298944):
Traceback (most recent call last):
File “/Users/cxhuan/Library/Application Support/JetBrains/IntelliJIdea2020.3/plugins/python/helpers/pydev/pydevconsole.py”, line 483, in <module>
pydevconsole.start_client(host, port)
│            │            │     └ 62146
│            │            └ ‘127.0.0.1’
│            └ <function start_client at 0x10fd596a8>
└ <module ‘pydevconsole’ from ‘/Users/cxhuan/Library/Application Support/JetBrains/IntelliJIdea2020.3/plugins/python/helpers/py…

……

> File “/Users/cxhuan/Documents/python_workspace/mypy/loguru/logurustudy.py”, line 32, in <module>
a_function(0)
└ <function a_function at 0x11021e620>
File “/Users/cxhuan/Documents/python_workspace/mypy/loguru/logurustudy.py”, line 30, in a_function
return 1 / x
└ 0
ZeroDivisionError: division by zero
上面的代码中,我特意造了一个 1 除以 0 的异常,我们可以看到日志输出信息非常详细,将每一步调用的错误信息都详细的列出来,并且还把参数的值也打印出来了,还有非常直观的指向性,简直是异常分析神器!

exception 方法捕获异常
我们直接看例子:

def b_function1(x):
try:
return 1 / x
except ZeroDivisionError:
logger.exception(“exception!!!”)

b_function1(0)

运行上面代码,输出信息如下:

2021-03-16 23:16:07.602 | ERROR    | __main__:b_function1:40 – exception!!!
Traceback (most recent call last):
File “/Users/cxhuan/Library/Application Support/JetBrains/IntelliJIdea2020.3/plugins/python/helpers/pydev/pydevconsole.py”, line 483, in <module>
pydevconsole.start_client(host, port)
│            │            │     └ 62254
│            │            └ ‘127.0.0.1’
│            └ <function start_client at 0x118d216a8>
└ <module ‘pydevconsole’ from ‘/Users/cxhuan/Library/Application Support/JetBrains/IntelliJIdea2020.3/plugins/python/helpers/py…
File “/Users/cxhuan/Library/Application Support/JetBrains/IntelliJIdea2020.3/plugins/python/helpers/pydev/pydevconsole.py”, line 411, in start_client
process_exec_queue(interpreter)
│                  └ <_pydev_bundle.pydev_ipython_console.InterpreterInterface object at 0x118d36240>
└ <function process_exec_queue at
0x118d21400>

……

File “/Users/cxhuan/Documents/python_workspace/mypy/loguru/logurustudy.py”, line 42, in <module>
b_function1(0)
└ <function b_function1 at 0x11913b598>
> File “/Users/cxhuan/Documents/python_workspace/mypy/loguru/logurustudy.py”, line 38, in b_function1
return 1 / x
└ 0
ZeroDivisionError: division by zero
同样地,也是很详细和直观地打印了错误详情。

开眼界!Python 遍历文件可以这样做!

开眼界!Python 遍历文件可以这样做!

 

Python 对于文件夹或者文件的遍历一般有两种操作方法,一种是至二级利用其封装好的 walk 方法操作:

import os
for root,dirs,files in os.walk(“/Users/cxhuan/Downloads/globtest/hello”):
for dir in dirs:
print(os.path.join(root, dir))
for file in files:
print(os.path.join(root, file))

上面代码运行结果如下:

/Users/cxhuan/Downloads/globtest/hello/world
/Users/cxhuan/Downloads/globtest/hello/.DS_Store
/Users/cxhuan/Downloads/globtest/hello/hello3.txt
/Users/cxhuan/Downloads/globtest/hello/hello2.txt
/Users/cxhuan/Downloads/globtest/hello/hello1.txt
/Users/cxhuan/Downloads/globtest/hello/world/world1.txt
/Users/cxhuan/Downloads/globtest/hello/world/world3.txt
/Users/cxhuan/Downloads/globtest/hello/world/world2.txt

上述程序,将 os.walk 读取到的所有路径 root 、目录名 dirs 与文件名 files ,也就是三个文件数组利用 foreach 循环输出。join方法就是将其路径与目录名或者文件名连接起来,组成一个完整的目录。

另一种是用递归的思路,写成下面的形式:

import os
files = list()
def dirAll(pathname):
if os.path.exists(pathname):
filelist = os.listdir(pathname)
for f in filelist:
f = os.path.join(pathname, f)
if os.path.isdir(f):
dirAll(f)
else:
dirname = os.path.dirname(f)
baseName = os.path.basename(f)
if dirname.endswith(os.sep):
files.append(dirname+baseName)
else:
files.append(dirname+os.sep+baseName)

dirAll(“/Users/cxhuan/Downloads/globtest/hello”)
for f in files:
print(f)

运行上面代码,得到的结果和上面一样。

这两种方法都没问题,就是写起来比较麻烦,特别是第二种,一不小心还有可能写出 bug 。

今天我们来介绍第三种方法——利用 glob 模块来遍历文件。

简介
glob 是 python 自带的一个操作文件的模块,以简洁实用著称。由于这个模块的功能比较简单,所以也很容易上手和使用。它主要用来查找符合特定规则的文件路径。使用这个模块来查找文件,只需要用到*、? 和 [] 这三个匹配符:

* : 匹配0个或多个字符;
? : 匹配单个字符;
[] :匹配指定范围内的字符,如:[0-9]匹配数字。
glob.glob 方法
glob.glob 方法主要返回所有匹配的文件路径列表。它只有一个参数 pathname ,定义了文件路径匹配规则,这里可以是*对路径,也可以是相对路径。

使用 * 匹配
我们可以用 * 匹配零个或者多个字符。

输出目录下的子目录或者文件:

for p1 in glob.glob(‘/Users/cxhuan/Downloads/globtest/*’):
print(p1)

运行上面代码,会将 globtest 文件夹下仅有的目录输出出来,输出内容如下:

/Users/cxhuan/Downloads/globtest/hello

我们也可以通过制定层级来遍历文件或者文件夹:

for p in glob.glob(‘/Users/cxhuan/Downloads/globtest/*/*’):
print(p)

上面的代码会遍历 globtest 文件夹以及子文件夹,将所有的文件或文件夹路径打印出来:

/Users/cxhuan/Downloads/globtest/hello/world
/Users/cxhuan/Downloads/globtest/hello/hello3.txt
/Users/cxhuan/Downloads/globtest/hello/hello2.txt
/Users/cxhuan/Downloads/globtest/hello/hello1.txt
我们也可以对文件或者文件夹进行过滤:

for p in glob.glob(‘/Users/cxhuan/Downloads/globtest/hello/*3.txt’):
print(p)

上面代码值匹配 hello 目录下的文件名末尾为 ‘3’ 的 txt 文件,运行结果如下:

/Users/cxhuan/Downloads/globtest/hello/hello3.txt
使用 ? 匹配
我们可以用问号(?)匹配任何单个的字符。

for p in glob.glob(‘/Users/cxhuan/Downloads/globtest/hello/hello?.txt’):
print(p)

上面的代码输出 hello 目录下的以 ‘hello’ 开头的 txt 文件,输出结果如下:

/Users/cxhuan/Downloads/globtest/hello/hello3.txt
/Users/cxhuan/Downloads/globtest/hello/hello2.txt
/Users/cxhuan/Downloads/globtest/hello/hello1.txt

使用 [] 匹配
我们可以使用 [] 来匹配一个范围:

for p in glob.glob(‘/Users/cxhuan/Downloads/globtest/hello/*[0-2].*’):
print(p)

我们想要得到 hello 目录下的文件名结尾数字的范围为 0到2的文件,运行上面代码,获得的输出为:

/Users/cxhuan/Downloads/globtest/hello/hello2.txt
/Users/cxhuan/Downloads/globtest/hello/hello1.txt
glob.iglob 方法
python 的 glob 方法可以对文件夹下所有文件进行遍历,并返回一个 list 列表。而 iglob 方法一次只获取一个匹配路径。下面是一个简单的例子来说明二者的区别:

p = glob.glob(‘/Users/cxhuan/Downloads/globtest/hello/hello?.*’)
print(p)

print(‘———————-‘)

p = glob.iglob(‘/Users/cxhuan/Downloads/globtest/hello/hello?.*’)
print(p)

运行上面代码,结果返回是:

[‘/Users/cxhuan/Downloads/globtest/hello/hello3.txt’, ‘/Users/cxhuan/Downloads/globtest/hello/hello2.txt’, ‘/Users/cxhuan/Downloads/globtest/hello/hello1.txt’]
———————-
<generator object _iglob at 0x1040d8ac0>

从上面的结果我们可以很容易看到二者的区别,前者返回的是一个列表,后者返回的是一个可迭代对象。

我们针对这个可迭代对象做一下操作看看:

p = glob.iglob(‘/Users/cxhuan/Downloads/globtest/hello/hello?.*’)
print(p.__next__())
print(p.__next__())

运行结果如下:

/Users/cxhuan/Downloads/globtest/hello/hello3.txt
/Users/cxhuan/Downloads/globtest/hello/hello2.txt
我们可以看到,针对这个可迭代对象,我们一次可以获取到一个元素。这样做的好处是节省内存,试想如果一个路径下有大量的文件夹或者文件,我们使用这个迭代对象不用一次性全部获取到内存,而是可以慢慢获取。

这就是你需要的铁饭碗

这就是你需要的铁饭碗

【大神观摩】他半年把python 学到了能出书的程度
他是知名外企技术架构师,在业余时间半年自学Python,就撰写了两部Python技术书籍,他是如何做到的?5月14日(周四)晚8点邀请您一起直播观摩。

文 | 李晓飞

来源:Python 技术「ID: pythonall」

前两天看了 码农翻身 公号作者刘欣 的一篇文件 我去北京吃了顿散伙饭

讲述了曾经理论上看起来*不会倒的部门解散的事情

理论上说,只要IBM不倒,这个部门肯定不会倒

多么有讽刺意味,再强健的组织,再权威的承诺,都是靠不住的

同时让我想到技术人的35岁坎儿

今天就技术人职场,谈谈一些看法,期望对你有所启发

专业能力
无论做什么,对于一个职场人来说,专业能力是*为基础的立足之本

有了专业的能力,才能站稳脚跟,才有后面其他发展的基础和条件

我记得毕业之初,学校有场辩论赛,主题是先就业还是先择业,当时不知如何选择,现在看来,其实选择哪个不是*重要的,因为对于一个刚走向社会的毕业生而言,无论哪个领域,*重要的就是尽快练就自己的专业能力

那么对于一个计算机领域的人来说,如何培养这个专业能力呢

专业知识
首先是专业基础知识要扎实

不可否认,现在学校里学的东西比较落后,对于市场来说有点过时,但是任何领域都有这个领域*基本的知识构建,这些*基本的知识构建,无论如何是要学且学明白的

对于计算机而言,*基础的是 计算机导论,C语言编程,和数据结构,而这些都是在学校里会教的,如果没有学明白,那么利用平时时间应该仔细深入的补上,其中包含了对计算机领域的基础理论,对编程语言的基础知识,对数据信息的基本处理

而后面的职业生涯中,所以的技术知识,都是基于这些基本知识展开的

从事行业之初,需要认真阅读经典的专业书籍,如 代码大全2、深入理解操作系统、需求分析与系统设计, 人月神话等,不一而全

代码大全,详细说明了代码如何编写,以及遇到的问题如何处理,如何写出易于维护的代码

深入理解计算机系统,深入浅出的刨析了计算机的方方面面,从信息表示,到操作系统的组成,特别是对网络编程,多线程编程的讲述,用及其简单的篇幅,深入讨论了计算机的技术技术

需求分析与系统设计,从需求到系统实现,详细的定义个各个阶段,深入讨论了这个过程的转化方法,概念,以及实践操作,特别是每个阶段的要点,目前我们公司主要的设计方法,都是基于这本书的理论框架的

人月神话,描述了系统构建过程不是一般理解的线性的,投入更多的人力无论就可以提高构建速度,阐述了效率,成本,质量三者之间的关系,是软件工程的先驱之作,是了解软件工程,做项目管理的比读

通过认真阅读专业书籍,并在日常工作中多加应用,比较能强化自己的专业基础,并且可以提高专业化的速度

业务能力
然后是业务能力要强,如果说专业能力是对计算机本身的理解和认识,那么业务能力是对现实世界的理解和认识

技术是服务于现实的,无论是几十年前出现计算机,还是现在的互联网,都是基于解决现实业务问题而产生且发展壮大的

不过需求人员(或是产品经理)和技术人员总是相互PK,几乎成为了对立的两面

需求人员天马行空,技术人员步步为营

对于技术人员来说,用*简的*省的方式实现需求就行了,这并没什么错

但如果能进一步了解业务的背景,愿景,背后的原理和逻辑,将对需要解决的问题,设计的方案,采用的技术有更好的认知和理解,更能做出符合需求的有价值的系统或者产品

即:

技术上要专业,业务上要全面

短期来看,在解决问题时,更有了目的性,长期来看,为自己全面看待问题,掌握整体的业务流程奠定了基础,为自己走向更高一层铺平了道路

独挡一面
如果有了专业的技术,又能更好的理解业务需求,那么自己的能力就已经很大程度上得到了扩展

此时就具备了独挡一面的特性了

可以和需求甚至客户直接沟通,不需要中间人协助

可以通过技术支持,方案设定,不需要太多的技术指导

而在工作中,能独挡一面,意味者效率更高,业务扩展更容易

独挡一面的人,更能给公司创造价值,那么相应的,这样的人,也会得到公司更多的青睐与机会

以人为本
技术人,一般都只关注技术,会有一种

只要技术强,走遍天下都不怕

的感觉或认识,不愿搭理不懂技术的人,三句不离本行,我之前就是这样,约同学去吃饭,讲的都是电视机开关按钮的设计原理 😉

不仅如此,还有很多人是因为感觉与人大交道难,或者不愿意更人交道,才做了技术,俗称 “技术宅”

只要能到达专业能力的要去,也无可厚非

不过我们所在的社会是以人为基础的社会,人才是现阶段社会的主宰,也就是说所谓的价值,是以对人的服务和帮助而产生的

人是价值的拥有者与支配者

前几天遇到一个博士后,他开始在银行做一个小主管,但因为不喜欢和人大交道,去了学校教书,想要一个环境单一的没有负责人事关系的地方,但后来才明白,其实无论做什么首先需要搞定的是人,那么做一个只有自己就可以做的事情,也离不开别人的支持与帮助,更不用说组织更多的力量做一件大事儿了,他现在早已没有了之前对与人大交道的畏惧,组织教师学生一起做更大的事儿了

那么对于技术为本的技术人来说,应该如何以人为本呢?

提供价值
为他人提供价值,并不是拍须遛马,或哗众取宠,而是利用自己的专业能力,为他人提供超常的服务与价值

对于一个客户,除了满足规定的服务之外,额外提供一些力所能及的帮助,例如帮助客户解决软件问题,打印机调试等

对于一个同事,除了提供分内的帮助之外,额外提供一些帮助,例如通过一份公司开发说明,帮助做个代码审查,对他遇到的问题做个分析等

对于一个不是自己分内的事情,如有必要提供支持,例如在项目策划中提供一些信息和建议,帮助其他项目做一些梳理工作等

主动做一些绩效不高甚至无绩效的对公司或者对大家有帮助的事情,例如做一个解决大家问题的小系统,整理分析一些数据等

也可以将自己的知识,经验等整理出来,分享给需要的人

这些看似简单的,或者与自己毫不相干的事情,却能给他人提供意想不到的帮助,让自己成为大家喜欢的,友好的,有用的人

扩展影响力
现在的社会关系,比起之前的树形结构,更趋于网络化和扁平化的结构,每个人就好像网络中的一个节点

在网络中的价值体现在与其他节点的链接数量上,更多的链接意味着更多的价值与影响力

更为重要的是,网络结构中,存在更多的可能性,不会让自己卡死在一个体系当中

如果是树形结构,如果某一个节点出了问题,信息将无法下达到其下的字节的们,而其下的子节点也无法与其上的其他节点通信

网络结构里就不一样了,像互联网一样,总是存在一个*优的达到通路,即使众多节点出现了故障,但总能实现互联互通

另外网络结构里,更容易开展自己的影响力,通过网络可以宣传出自己的能力,实践,乃至产品,会有更多的人知道和了解,无形中扩展了自己的影响力,为拥有更多的机会打下了基础

具体可以参考之前写的 打造个人品牌,我用 Python

总结
单纯的技术工作者,往往会有35岁的青春饭票限制,这是由于多种因素造成的

能掌握在我们手里的法宝,就是 专业能力 和 以人为本,努力提高自己的专业能力同时,打造自己的链接网络和影响力,会让自己更具优势

当逆境或灾难来临时,会多了一层保障,不会着急忙慌,手足无措,怨天尤人

其实,在这个时代里,命运是掌握在自己手里的,只不过很多时候我们没意识到,或者没有一个内在的或者外在的动力促使我们去做

提早认识到,并且坚定的的去做,永远不吃自己的散伙饭

iOS 设置导航透明的两种方法

方法一

在当前页面加入下面方法

– (void)viewWillAppear:(BOOL)animated{

[superviewWillAppear:animated];

[self.navigationController.navigationBarsetBackgroundImage:[UIImagenew]forBarMetrics:UIBarMetricsDefault];

//去掉导航栏底部的黑线

self.navigationController.navigationBar.shadowImage = [UIImage new];

}

如果仅设置当前页导航透明,需加入下面方法
– (void)viewWillDisappear:(BOOL)animated{

[superviewWillDisappear:animated];

[self.navigationController.navigationBarsetBackgroundImage:nilforBarMetrics:UIBarMetricsDefault];

[self.navigationController.navigationBarsetShadowImage:nil];

}

需要注意的是:如果返回上一页的操作有动画,则在上一页出现的时候,导航栏的颜色会逐渐变化

方法二(这个方法在iOS 11上会出现crash,iOS 11上[self.navigationController.navigationBar subviews]值为空)

– (void)viewWillAppear:(BOOL)animated{

[superviewWillAppear:animated];

[[self.navigationController.navigationBar subviews]objectAtIndex:0].alpha =0;

}

仅设置当前页面

– (void)viewWillDisappear:(BOOL)animated{

[super viewWillDisappear:animated];

[[self.navigationController.navigationBar subviews]objectAtIndex:0].alpha =1;

}

我这有一个很好的三方库:

@property (nonatomic, strong) UIColor *barColor; //NavigationBar background color, default is white
@property (nonatomic, strong) UIColor *tintColor; //NavigationBar subviews color
@property (nonatomic, strong) UIImage *backgroundImage; //default is nil
@property (nonatomic, assign) UIStatusBarStyle statusBarStyle; // default is UIStatusBarStyleDefault

@property (nonatomic, assign) float zeroAlphaOffset;//color will changed begin this offset, default is -64
@property (nonatomic, assign) float fullAlphaOffset;//color alpha will be 1 in this offset, default is 200
@property (nonatomic, assign) float minAlphaValue;//bar minAlpha, default is 0
@property (nonatomic, assign) float maxAlphaValue;//bar maxAlpha, default is 1

@property (nonatomic, strong) UIColor *fullAlphaTintColor;//if you set this property, the tintColor will changed in fullAlphaOffset
@property (nonatomic, assign) UIStatusBarStyle fullAlphaBarStyle;//if you set this property, the barStyle will changed in fullAlphaOffset

@property (nonatomic, assign) BOOL allChange;//if allchange = yes, the tintColor will changed with the barColor change, default is yes, if you only want to change barColor, set allChange = NO
@property (nonatomic, assign) BOOL reversal;//this will cause that if currentAlpha = 0.3,it will be 1 – 0.3 = 0.7
@property (nonatomic, assign) BOOL continues;//when continues = YES, bar color will changed whenever you scroll, if you set continues = NO,it only be changed in the fullAlphaOffset

+ (void)setBarColor:(UIColor *)color;
+ (void)setTintColor:(UIColor *)color;
+ (void)setBackgroundImage:(UIImage *)image;
+ (void)setStatusBarStyle:(UIStatusBarStyle)style;

+ (void)setZeroAlphaOffset:(float)offset;
+ (void)setFullAlphaOffset:(float)offset;
+ (void)setMaxAlphaValue:(float)value;
+ (void)setMinAlphaValue:(float)value;

+ (void)setFullAlphaTintColor:(UIColor *)color;
+ (void)setFullAlphaBarStyle:(UIStatusBarStyle)style;

+ (void)setAllChange:(BOOL)allChange;
+ (void)setReversal:(BOOL)reversal;
+ (void)setContinues:(BOOL)continues;

+ (void)managerWithController:(UIViewController *)viewController;//you should use this method to init MXNavigationManager

+ (void)changeAlphaWithCurrentOffset:(CGFloat)currentOffset;// implemention this method in @selectot(scrollView: scrollViewDidScroll)

+ (void)reStoreToSystemNavigationBar; //change the navigationBar to system style

#import “MXNavigationBarManager.h”

static const CGFloat kNavigationBarHeight = 64.0f;
static const CGFloat kDefaultFullOffset = 200.0f;
static const float kMaxAlphaValue = 0.995f;
static const float kMinAlphaValue = 0.0f;
static const float kDefaultAnimationTime = 0.35f;

#define SCREEN_RECT [UIScreen mainScreen].bounds
#define BACKGROUNDVIEW_FRAME CGRectMake(0, -20, CGRectGetWidth(SCREEN_RECT), kNavigationBarHeight)

@interface MXNavigationBarManager ()

@property (nonatomic, strong) UINavigationBar *selfNavigationBar;
@property (nonatomic, strong) UINavigationController *selfNavigationController;

@property (nonatomic, strong) UIImage *saveImage;
@property (nonatomic, strong) UIColor *saveColor;
@property (nonatomic, strong) UIColor *saveTintColor;
@property (nonatomic, strong) NSDictionary *saveTitleAttribute;
@property (nonatomic, assign) UIStatusBarStyle saveBarStyle;

@property (nonatomic, assign) BOOL setFull;
@property (nonatomic, assign) BOOL setZero;
@property (nonatomic, assign) BOOL setChange;

@end

@implementation MXNavigationBarManager

#pragma mark – property set
+ (void)setBarColor:(UIColor *)color {
[self sharedManager].barColor = color;
}

+ (void)setTintColor:(UIColor *)color {
[self sharedManager].tintColor = color;
[self sharedManager].selfNavigationBar.tintColor = color;
[self setTitleColorWithColor:color];
}

+ (void)setBackgroundImage:(UIImage *)image {
[[self sharedManager].selfNavigationBar setBackgroundImage:image
forBarMetrics:UIBarMetricsDefault];
}

+ (void)setStatusBarStyle:(UIStatusBarStyle)style {
[self sharedManager].statusBarStyle = style;
[[UIApplication sharedApplication] setStatusBarStyle:style];
}

+ (void)setZeroAlphaOffset:(float)offset {
[self sharedManager].zeroAlphaOffset = offset;
}

+ (void)setFullAlphaOffset:(float)offset {
[self sharedManager].fullAlphaOffset = offset;
}

+ (void)setMinAlphaValue:(float)value {
value = value < kMinAlphaValue ? kMinAlphaValue : value;
[self sharedManager].minAlphaValue = value;
}
+ (void)setMaxAlphaValue:(float)value {
value = value > kMaxAlphaValue ? kMaxAlphaValue : value;
[self sharedManager].maxAlphaValue = value;
}

+ (void)setFullAlphaTintColor:(UIColor *)color {
[self sharedManager].fullAlphaTintColor = color;
}

+ (void)setFullAlphaBarStyle:(UIStatusBarStyle)style {
[self sharedManager].fullAlphaBarStyle = style;
}

+ (void)setAllChange:(BOOL)allChange {
[self sharedManager].allChange = allChange;
}

+ (void)setReversal:(BOOL)reversal {
[self sharedManager].reversal = reversal;
}

+ (void)setContinues:(BOOL)continues {
[self sharedManager].continues = continues;
}

+ (void)reStoreToSystemNavigationBar {
[[self sharedManager].selfNavigationController setValue:[UINavigationBar new] forKey:@”navigationBar”];
}

#pragma mark – Public Method
+ (void)managerWithController:(UIViewController *)viewController {
UINavigationBar *navigationBar = viewController.navigationController.navigationBar;
[self sharedManager].selfNavigationController = viewController.navigationController;
[self sharedManager].selfNavigationBar = navigationBar;
[navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
[navigationBar setShadowImage:[UIImage new]];
}

+ (void)changeAlphaWithCurrentOffset:(CGFloat)currentOffset {
MXNavigationBarManager *manager = [self sharedManager];

float currentAlpha = [self curretAlphaForOffset:currentOffset];

if (![manager.barColor isEqual:[UIColor clearColor]]) {
if (!manager.continues) {
if (currentAlpha == manager.minAlphaValue) {
[self setNavigationBarColorWithAlpha:manager.minAlphaValue];
} else if (currentAlpha == manager.maxAlphaValue) {
[UIView animateWithDuration:kDefaultAnimationTime animations:^{
[self setNavigationBarColorWithAlpha:manager.maxAlphaValue];
}];
manager.setChange = YES;
} else {
if (manager.setChange) {
[UIView animateWithDuration:kDefaultAnimationTime animations:^{
[self setNavigationBarColorWithAlpha:manager.minAlphaValue];
}];
manager.setChange = NO;
}
}
} else {
[self setNavigationBarColorWithAlpha:currentAlpha];
}
}

if (manager.allChange) [self changeTintColorWithOffset:currentAlpha];
}

#pragma mark – calculation
+ (float)curretAlphaForOffset:(CGFloat)offset {
MXNavigationBarManager *manager = [self sharedManager];
float currentAlpha = (offset – manager.zeroAlphaOffset) / (float)(manager.fullAlphaOffset – manager.zeroAlphaOffset);
currentAlpha = currentAlpha < manager.minAlphaValue ? manager.minAlphaValue : (currentAlpha > manager.maxAlphaValue ? manager.maxAlphaValue : currentAlpha);
currentAlpha = manager.reversal ? manager.maxAlphaValue + manager.minAlphaValue – currentAlpha : currentAlpha;
return currentAlpha;
}

+ (void)changeTintColorWithOffset:(float)currentAlpha {
MXNavigationBarManager *manager = [self sharedManager];
if (currentAlpha >= manager.maxAlphaValue && manager.fullAlphaTintColor != nil) {
if (manager.setFull) {
manager.setFull = NO;
manager.setZero = YES;
} else {
if (manager.reversal) {
manager.setFull = YES;
}
return;
}
manager.selfNavigationBar.tintColor = manager.fullAlphaTintColor;
[self setTitleColorWithColor:manager.fullAlphaTintColor];
[self setUIStatusBarStyle:manager.fullAlphaBarStyle];
} else if (manager.tintColor != nil) {
if (manager.setZero) {
manager.setZero = NO;
manager.setFull = YES;
} else {
return;
}
manager.selfNavigationBar.tintColor = manager.tintColor;
[self setTitleColorWithColor:manager.tintColor];
[self setUIStatusBarStyle:manager.statusBarStyle];
}
}

#pragma mark – private method
+ (MXNavigationBarManager *)sharedManager {
static MXNavigationBarManager *manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[MXNavigationBarManager alloc] init];
[self initBaseData:manager];
});
return manager;
}

+ (void)initBaseData:(MXNavigationBarManager *)manager {
manager.maxAlphaValue = kMaxAlphaValue;
manager.minAlphaValue = kMinAlphaValue;
manager.fullAlphaOffset = kDefaultFullOffset;
manager.zeroAlphaOffset = -kNavigationBarHeight;
manager.setZero = YES;
manager.setFull = YES;
manager.allChange = YES;
manager.continues = YES;
}

+ (void)setTitleColorWithColor:(UIColor *)color {
NSMutableDictionary *textAttr = [NSMutableDictionary dictionaryWithDictionary:[self sharedManager].selfNavigationBar.titleTextAttributes];
[textAttr setObject:color forKey:NSForegroundColorAttributeName];
[self sharedManager].selfNavigationBar.titleTextAttributes = textAttr;
}

+ (void)setNavigationBarColorWithAlpha:(float)alpha {
MXNavigationBarManager *manager = [self sharedManager];
NSLog(@”alpha = %f”, alpha);
[self setBackgroundImage:[self imageWithColor:[manager.barColor colorWithAlphaComponent:alpha]]];
}

+ (void)setUIStatusBarStyle:(UIStatusBarStyle)style {
[[UIApplication sharedApplication] setStatusBarStyle:style];
}

+ (UIImage *)imageWithColor:(UIColor *)color {
CGRect rect = CGRectMake(0, 0, 1, 1);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[color setFill];
CGContextFillRect(context, rect);
UIImage *imgae = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return imgae;
}

 

iOS中atomic和nonatomic区别及内部实现

前言
iOS开发中使用@property声明属性时,经常用到atomic与nonatomic两个关键字

@property(nonatomic,strong)UIImage *icon;
@property(strong)UIImage *icon1;//不写默认atomic
atomic与nonatomicd的主要区别就是系统自动生成的getter/setter方法不一样

atomic系统自动生成的getter/setter方法会进行加锁操作
nonatomic系统自动生成的getter/setter方法不会进行加锁操作

atomic
系统生成的getter/setter方法会进行加锁操作,注意:这个锁仅仅保证了getter和setter存取方法的线程安全.

因为getter/setter方法有加锁的缘故,故在别的线程来读写这个属性之前,会先执行完当前操作.

例如: 线程1调用了某一属性的setter方法并进行到了一半,线程2调用其getter方法,那么会执行完setter操作后,在执行getter操作,线程2会获取到线程1 setter后的完整的值.

当几个线程同时调用同一属性的setter、getter方法时,会get到一个完整的值,但get到的值不可控.

例如: 线程1 调用getter 线程2 调用setter 线程3 调用setter 这3个线程并行同时开始,线程1会get到一个值,但是这个值不可控,可能是线程2,线程3 set之前的原始值,可能是线程2 set的值,也可能是线程3 set的值

atomic是线程安全的吗?

不是, 很多文章谈到atomic和nonatomic的区别时,都说atomic是线程安全,其实这个说法是不准确的. atomic只是对属性的getter/setter方法进行了加锁操作,这种安全仅仅是set/get 的读写安全,并非真正意义上的线程安全,因为线程安全还有读写之外的其他操作(比如:如果当一个线程正在get或set时,又有另一个线程同时在进行release操作,可能会直接crash)

nonatomic
系统生成的getter/setter方法没有加锁 线程不安全,但更快 当多个线程同时访问同一个属性,会出现无法预料的结果

内部实现
下面我们新建一个nonatomic和一个atomic变量,并用代码演示其内部实现

//interface
@property(nonatomic,strong)UIImage *icon;//nonatomic
@property(strong)UIImage *icon1;//atomic

nonatomic对象setter和getter方法的实现

//mrc 环境
//implementation
@synthesize icon = _icon;

//set
-(void)setIcon:(UIImage *)icon
{
if(_icon != icon)
{
[_icon release];
_icon = [icon retain];
}
}
//get
-(UIImage *)icon
{
return _icon;
}

atomic对象setter和getter方法的实现

//mrc 环境
//implementation
@synthesize icon1 = _icon1;

//set
-(void)setIcon1:(UIImage *)icon1
{
//同步代码块
@synchronized (self) {
if(_icon1 != icon1)
{
[_icon1 release];
_icon1 = [icon1 retain];
}
}
1
2
3
4
5
6
}
//get
-(UIImage *)icon1
{
UIImage *image = nil;
//同步代码块
@synchronized (self) {
image = [[_icon1 retain] autorelease];
}
return image;

}

总结
atomic只是保证了getter和setter存取方法的线程安全,并不能保证整个对象是线程安全的,因此在多线程编程时,线程安全还需要开发者自己来处理.

关于选择:atomic系统生成的getter、setter会保证get、set操作的安全性,但相对nonatomic来说,atomic要更耗费资源,且速度要慢,故在iPhone等小型设备上,如果没有多线程之间的通讯,使用nonatomic是更好的选择

Mac如何关闭开机启动项

1、首先进入“系统偏好设置”的 “用户与群组”选项。

2、打开用户与群组后可以看到如图所示的“登陆项”标签。

3、可以看到有应用程序是默认开机启动的,点击左下角的“+-” 即可添加或删除开机启动程序。如果机器有设置密码,先点击左下角锁的图标进行解锁。

iOS阻止文件被iTunes和iCloud同步

iCloud 会通过WiFi每天自动备份用户iOS设备。除了应用Bundle本身、Caches目录和tmp目录,app目录下的所有东西都会被备份。由于备份通过WiFi进行,并且为每个用户存储在iCloud中,应用需*小化自己存储的数据数量。大文件会延长备份时间,并且消耗用户的可用iCloud空间。

为了确保备份尽可能快速高效,应用存储数据需要遵循以下指导方针:

1. 只有那些用户生成的文档或其它数据,或者应用不能重新创建的数据,才应该存储在/Documents目录下,并且会被自动备份到iCloud。

2. 可以重新下载或生成的数据,应该存储在/Library/Caches目录。例如数据库缓存文件、可下载文件(杂志、报纸、地图应用使用的数据)等都属于这一类。

3. 临时使用的数据应该存放在/tmp目录。尽管这些文件不会被iCloud备份,应用在使用完之后需要记得删除这些文件,这样才不会继续占用用户设备的空间。

4. 使用”不要备份”属性来指定那些需要保留在设备中的文件(即使是低存储空间情况下)。那些能够重新生成,但在低存储空间时仍需保留,对应用正常运行有影响,或者用户希望文件在离线时可用的文件,需要使用这个属性。无论哪个目录下的文件(包括Documents目录),都可以使用这个属性。这些文件不会被删除,也不会包含在用户的iCloud或iTunes备份中。由于这些文件一直占用着用户设备的存储空间,应用有责任定期监控和删除这些文件。

iOS 5.0.1开始支持”do not backup”文件属性(com.apple.MobileBackup),允许开发者明确地指定哪些文件应该被备份,哪些是本地缓存可以自动删除,哪些文件不需要备份但不能删除。此外,对目录设置这个属性,会阻止备份该目录和目录中的所有内容。

文件可以分为以下四种数据类型:

关键数据
这些是用户创建的数据,或其它不能重新生成的数据。应该存放在/Documents目录下,并且不应该标记为”do not backup”属性。关键数据在低存储空间时也会保留,而且会被iCloud或iTunes备份。

路径:/Documents

属性:不要设置”不备份”

管理:iOS系统即时遇到存储空间不足的情况下,也不会清除,同时会备份到iTunes或iCloud中

缓存数据
可以重新下载或生成的数据,而且没有这些数据也不会妨碍用户离线使用应用的功能。缓存数据应该保存在/Library/Caches目录下。缓存数据在设备低存储空间时可能会被删除,iTunes或 iCloud不会对其进行备份。

路径:/Library/Caches

属性:默认

管理:在存储空间不足的情况下,会清空, 并且不会被自动备份到iTunes和iCloud中

临时数据
应用需要写到本地存储,内部使用的临时数据,但不需要长期保留使用。临时数据应该保存在/tmp目录。系统可能会清空该目录下的数据,iTunes或iCloud也不会对其进行备份。应用在不需要使用这些数据时,应该尽快地删除临时数据,以避免浪费用户的存储空间

路径:/tmp

属性:默认

管理:随时可能被iOS系统清除,且不会自动备份到iTunes和iCloud,尽量在文件不再使用时应用自己清空,避免对用户设备空间的浪费

离线数据
可以下载或重新创建,但用户希望在离线时也能访问这些数据。离线数据应该存放在/Documents目录或/Library/Private Documents目录,并标记为”do not backup”属性。这两个位置的数据在低存储空间时都会保留,而”do not backup”属性会阻止iTunes或iCloud备份。应用不再需要离线数据文件时,应该尽快删除,以避免浪费用户的存储空间。

路径:/Documents  或 /Library/自定义的文件夹

属性:放于Documents下不需设置,放在自定义文件夹中需设置”不备份”

管理:与关键数据类似,即时在存储空间不足的情况下也不会清除,应用自己应该清除已经不再使用的文件,以免浪费用户设备空间

设置Do Not Backup扩展属性
#import “sys/xattr.h”

– (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)url {
const char* filePath = url.path.fileSystemRepresentation;
const char* attrName = “com.apple.MobileBackup”;
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;

 

iOS14适配兼容

Cell点击无效
在iOS14上可能出现点击cell上的视图无法响应的情况.
原因:

iOS14更改Cell视图布局.将contentView放在*上层,如果将视图加载在cell上,将会出现contentView遮罩,导致事件无法响应.是在此前关于 contentView 的声明注释中,官方已经明确建议开发者将 customView 放在 contentView 上,使 contentView 作为 UITableViewCell 默认的父视图。

解决办法:
1、可以将cell子视图加载在contentView上(提倡)
2、将contentView设置到*底层 self.sendSubviewToBack(self.contentView)

UIDatePicker 更新 UI 样式
iOS14 UIDatePicker新增加了一个UI样式 UIDatePickerStyleInline 作为默认样式,如果还需要旧版本的滚轮样式,需要设置为

UIDatePicker 的 preferredDatePickerStyle 属性为 UIDatePickerStyleWheels。

typedef NS_ENUM(NSInteger, UIDatePickerStyle) {
/// Automatically pick the best style available for the current platform & mode.
UIDatePickerStyleAutomatic,
/// Use the wheels (UIPickerView) style. Editing occurs inline.
UIDatePickerStyleWheels,
/// Use a compact style for the date picker. Editing occurs in an overlay.
UIDatePickerStyleCompact,
/// Use a style for the date picker that allows editing in place.
UIDatePickerStyleInline API_AVAILABLE(ios(14.0)) API_UNAVAILABLE(tvos, watchos),
} API_AVAILABLE(ios(13.4)) API_UNAVAILABLE(tvos, watchos);
相册权限
iOS14 新增了“Limited Photo Library Access” 模式,在授权弹窗中增加了 Select Photo 选项。用户可以在 App 请求调用相册时选择部分照片让 App 读取。从 App 的视⻆来看,你的相册里就只有这几张照片,App 无法得知其它照片的存在。
权限提示框会在每次冷启动后打开相册时重新弹出,可以在 info.plist 中设置 PHPhotoLibraryPreventAutomaticLimitedAccessAlert 选项为 YES ,关闭提示。

在 iOS14 中官方推荐使用 PHPicker 来替代原 API 进行图片选择。PHPicker 为独立进程,会在视图*顶层进行展示,应用内无法对其进行截图也无法直接访问到其内的数据。

UIImagePickerController -> PHPickerViewController, UIImagePickerViewController 功能受限,每次只能选择一张图片,将逐渐被废弃。

地理位置
CLLocationManager 新增了 精确定位 和 模糊定位 的概念,用户可以手动选择,模糊定位的误差约 500m 。可以根据实际功能判断是否可以接受用户选择模糊定位。

如果功能强依赖精确定位,可以在需要的时候调用 requestTemporaryFullAccuracyAuthorizationWithPurposeKey 方法。

– (void)requestTemporaryFullAccuracyAuthorizationWithPurposeKey:(NSString *)purposeKey completion:(void(^ _Nullable)(NSError * _Nullable))completion;

– (void)requestTemporaryFullAccuracyAuthorizationWithPurposeKey:(NSString *)purposeKey;
单独请求一次精确定位,用户可以选择拒*授权。所需参数 purposeKey 需要在 info.plist 中设置 NSLocationTemporaryUsageDescriptionDictionary 字典。

可以通过 accuracyAuthorization 属性获取当前的定位方式:

@property (nonatomic, readonly) CLAccuracyAuthorization accuracyAuthorization;
typedef NS_ENUM(NSInteger, CLAccuracyAuthorization) {
// 精准定位
CLAccuracyAuthorizationFullAccuracy,
// 模糊定位
CLAccuracyAuthorizationReducedAccuracy,
};
适配UIPageControl
iOS14以后删除了pageImage 与currentPageImage,不能再通过KVC修改控制器的图片。

解决方案:

借助iOS14新增字段preferredIndicatorImage,我们可以修改指示器小圆点的大小以及形状,借助currentPageIndicatorTintColor与pageIndicatorTintColor来确认不同状态下小圆点的颜色。

*终展示的颜色是由TintColor以及preferredIndicatorImage共同决定的,因此这里将preferredIndicatorImage设置为白色。

使用layer.mask做遮罩时无法显示
使用UIImageView的layer 做遮罩时,不显示任何东西。

在iOS14以下使用UIImageView的layer做遮罩的代码:

UIImageView *maskView = [[UIImageView alloc] initWithImage:maskImage];
maskView.frame = self.bounds;
self.layer.mask = maskView.layer;
但是在iOS14上无法正常展示。

解决方案:

UIImage *newImage = [self stretchImage:maskImage newSize:self.bounds.size leftCapWidth:maskImage.size.width/2 topCapHeight:maskImage.size.height-7];
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = self.bounds;
maskLayer.contents = (id)newImage.CGImage;
self.layer.mask = maskLayer;
– (UIImage *)stretchImage:(UIImage *)originImage newSize:(CGSize)newSize leftCapWidth:(CGFloat)leftCapWidth topCapHeight:(CGFloat)topCapHeight{
UIImage *newImage;
newImage = [originImage stretchableImageWithLeftCapWidth:leftCapWidth topCapHeight:topCapHeight];
UIGraphicsBeginImageContextWithOptions(newSize, false, 0);
[newImage drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];

newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;