分类: IOS技术

IOS技术

iOS – 静态库、动态库从浅到深学习之路 (三)

XCFramework  (framework的增强版)
说明:

1. 苹果官方推荐,支持的,可以更加方便多个平台和架构的分发二进制库的格式。

2. 需要xcode11以上支持

3. 在2019年提出的framework的另一种先进格式。

多架构合并
架构打包命令:

// 打包成模拟器架构
xcodebuild archive -project ‘SYTimer.xcodeproj’ \
-scheme ‘SYTimer’ \
-configuration Release \
-destination ‘generic/platform=iOS Simulator’ \
-archivePath ‘../archives/SYTimer.framework-iphonesimulator.xcarchive’ \
SKIP_INSTALL=NO

// 打包成iOS真机架构
xcodebuild archive -project ‘SYTimer.xcodeproj’ \
-scheme ‘SYTimer’ \
-configuration Release \
-destination ‘generic/platform=iOS’ \
-archivePath ‘../archives/SYTimer.framework-iphoneos.xcarchive’ \
SKIP_INSTALL=NO
胖二进制: 多个架构将库文件压缩放在一起,并不是合并。

通过lipo 命令进行将动态库进行合并。

格式 : lipo -output 合并后输出的名称 -create  架构路径1 架构路径2

lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer
遇到问题;

相同的架构合并,会出现冲突

解决方法:

去除相同的架构,只留一个架构,相关命令如下:

lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer

//提取 x86_64架构
lipo -output SYTimer-x86_64 -extract x86_64 ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer

lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer SYTimer-x86_64
苹果为了解决这个问题,所以引进了XCFramework ,优点相比于 lipo

1.  不用处理头文件

2. 会自动处理重复的架构

3. 会自动产生调试符号

4. 会根据运行的架构自动选择对应的架构 。

 

制作一个XCFramework:
xcodebuild -create-xcframework \
-framework ‘../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-framework ‘../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-output ‘SYTimer.xcframework’ 需要改xcframework放的路径
xcodebuild -create-xcframework \
-framework ‘../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-debug-symbols ‘架构1 – bcsymbolmap – 的*对路径’ \
-debug-symbols ‘架构2 – bcsymbolmap – 的*对路径’ \
-debug-symbols ‘架构1 – dsym – 的相对路径’ \
-framework ‘架构2 -framework – 的相对路径’ \
-debug-symbols ‘架构2 – dysm – 的相对路径’ \
-output ‘SYTimer.xcframework’
weak import
1. 弱引用的作用 ,就是当动态库被引入但没有使用的话,会自动置为nil ,不会让程序崩溃,编译通过,一个符号找不到,不会强制找到为止。 在xcconfig文件中格式为:

OTHER_LDFLAGS = $(inherited) -Xlinker -weak_framework -Xlinker “第三方库或动态库名称”
相同静态库的重复引入,符号冲突的解决方法:

通过链接器的 force_load 引入其中一个静态库, 另外一个静态库的符号通过 load_hidden 去隐藏。

命令如下:

OTHER_LDFLAGS = $(inherited) -l”*个静态库名” -l”第二个静态库名” -Xlinker -force_load -Xlinker “*个静态库路径.a” -Xlinker -load_hidden -Xliner “第二个静态库路径.a”
动态库 、静态库的实战
动态库与动态库的链接实战:

思路:  自己的framework,通过pod导入一个第三方,然后自己的项目,导入自己的framework并使用 ,反向使用等。

实现步骤:

1 .创建一个自己的framework (创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库。 (pods 只会生成链接器的参数,并不会把库文件放到framework下,pods是通过脚本是复制到指定目录,方式二:再使用的工程中,通过pods的配置文件,再指定加载的第三方库。)

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的动态库  -> 第三方动态库 (AFNetworking) , 大致的动态库与动态库的模型就出来了。

后期上传demo.

主要出现的问题:

1. 运行app,找不到第三方库文件(因为这个第三方库,是动态库中所引用的第三方库),报错信息:

Undefined symbols for architecture x86_64:
“_OBJC_CLASS_$_ZGRAppObject”, referenced from:
objc-class-ref in ZGRAFNetworkingManager.o
解决方法是:在pod 文件中为这个target也加入pod 第三方库的方法,Podfile 代码如下:

platform :ios, ‘14.1’

#自己做的framework ,动态库
target :’ZGRNetworkManager’ do
use_frameworks!
pod ‘AFNetworking’
end

#比喻App
target :’ZGRNetworkManagerTests’ do
use_frameworks!
pod ‘AFNetworking’
end
2. 找不到对应的符号_OBJC_CLASS_$_ZGRAppObject ,  修改pod的配置文件,创建的target 和 动态库对应的配置文件都加上这句话。

OTHER_LDFLAGS = $(inherited) -framework “AFNetworking” -Xlinker -U -Xlinker _OBJC_CLASS_$_ZGRAppObject
动态库与静态库的链接实战:

思路:  自己的framework,通过pod导入一个第三方静态库 AFNetworking.a ,在framework 中添加一个target,模拟app来使用 。

实现步骤:

1 .创建一个自己的framework (创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库。 (pods 只会生成链接器的参数,并不会把库文件放到framework下,pods是通过脚本是复制到指定目录,方式二:再使用的工程中,通过pods的配置文件,再指定加载的第三方库。)

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的动态库  -> 第三方静态库 (AFNetworking.a) , 大致的动态库与静态库的模型就出来了。

 

注:

动态库链接静态库,会链接所有的静态库符号, 所以当app去编译动态库的时候,并不会报错。

静态库的导出符号,也会出现在动态库符号里面,所以可以在app中直接使用,导入头文件即可。   (搜索不到,需要设置 header search paths路径 ${SRCROOT}/Pods/Headers/Public)

后期上传demo.

关于静态库的符号隐藏,通过在pod的配置文件加入如下代码:

OTHER_LDFLAGS = $(inherited) -Xlinker -hidden-l “AFNetworking”
 

静态库与静态库的链接实战:

思路:  自己的framework静态库组件,通过pod导入一个第三方静态库 AFNetworking.a ,在framework 中添加一个target,模拟app来使用 。

实现步骤:

1 .创建一个自己的framework (静态库) (创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库静态库。

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的静态库组件  -> 第三方静态库 (AFNetworking.a) 。

 

操作记录:

1. 在静态库组件首先要设置 library search path ,  例如 : “${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking”

2. 设置Other Linker Flags , -lAFNetworking

 

原理就是: app去链接静态库组件, 但静态库组件所链接的静态库并没有路径链接到app,所以需要在组件中手动设置下所链接的静态库的路径与头文件。

 

静态库与动态库的链接实战:

公式:  app = app + 静态库 (所有的符号都是可见的)

思路:  自己的framework静态库,通过pod导入一个第三方动态库 AFNetworking ,在framework 中添加一个target,模拟app来使用 。

实现步骤:

1 .创建一个自己的framework ,静态库(创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库AFNetworking。

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的静态库  -> 第三方动态库 (AFNetworking) , 大致的静态库与动态库的模型就出来了。

 

Cocoapods 拷贝动态库脚本的用法。

首先,把脚本拷贝到工程根目录下,脚本文件,可以在任意cocoapods的工程中去找,  然后在项目中,在build phase 添加一个脚本,输入:

“${SRCROOT}/Pods-LGNetworkManagerTests-frameworks.sh”
这样在运行项目的时候,脚本会自动将动态库拷贝到product目录中。

 

总结:

1. XCFramework  – sdk开发,组件开发

解决了:

1.头文件的处理

2. 调试符号的保存

3. 相同架构的处理

4.2019年才出。

 

2.  实战

1. weak_import

使用场景:  动态库 运行时 -》 不能确认到指定位置,所以用到了弱引用,当找不到动态库的时候,项目不会报错的作用

2. 静态库冲突 -》 app -> all_load\-Objc

3. app – 动态库 – 动态库链接  ,app不知道所以需要以下操作

方式一: 通过cocoapod脚本复制

方式二: 通过cocoapod 加载第三方库,并不会加载两次。 不用担心

 

动态库 – 静态库

存在问题:

静态库不想暴露到处符号,需要用到 -hidden -l静态库

 

app – 静态库 – 静态库

1. 名称  知道。

2 不知道所在位置: 所以只需要告诉位置

 

app – 静态库 – 动态库

1. 编译报错:  不知道动态库的路径

2. 运行报错: 不知道动态库的rpath

iOS CPU占有率达到了100%甚至更多,然后导致App闪退情况总结及解决过程

今天在真机调试的过程中,发现了一个严重的问题,发现CPU的使用率竟然达到了100%,以至于会导致运行内存占用过高,被系统的看门狗机制给杀掉。

下面就讲一讲怎么去定位这个问题:

1.打开Xcode,把项目跑动起来,然后选择这个选项卡

2.现在就可以看到这个画面


3. 现在我们可以看到这个页面,发现我的CPU达到了 105%,这肯定是有问题,那现在怎么办呢,我们可以看到右边的图,点击Profile in Instruments. —》 然后点击Transfer.

4. 现在就进入到Instruments中,我们看看究竟发生了什么,到底是什么情况,导致出现了这种问题。

1. 首先,我通过观察CPU占用率,各个页面进行排查,看是进行了何种操作后,才出现的这种CPU占用率居高不小。

2. 我很庆幸,我很快就定位到了原因。所以我可以知道是进入某一个页面,触发了某种操作后,然后,就会出现这种情况

3. 现在就可以通过Instruments来进行定位,来看看是执行什么代码,导致了这种非常耗时的操作,让CPU一直如此忙碌。

4.选中Xcode先把程序(command + R)运行起来

5.再选中Xcode,按快捷键(command + control + i)运行起来,此时Leaks已经跑起来了

6.由于Leaks是动态监测,所以我们需要手动操作APP,一边操作,一边观察Leaks的变化,当出现红色叉时,就监测到了内存泄露,点击右上角的第二个,进行暂停检测(也可继续检测,当多个时暂停,一次处理了多个).

扩展: 查内存泄露具体方法 点击打开链接l

5. 电脑卡爆了,哎。 回去了在截图,反正*后是跟踪到了 Runloop下。 有一个行为一直在占据着主线程,并且不释放,所以导致CPU一直在大量消耗,内存也慢慢渐长,一般能造成这种情况的就只有循环,并且一直没有释放,我利用Instruments中的leaks,然后进行了各种各样的内存泄露的检测及修复, 也正是这样,我发现了问题的所在。 原来是我写的有一个方法有问题。 我写的代码如下:

我们很清晰的看到如果条件为真,这就是一个死循环,我的PM那时候,这儿就想做一个图片一直闪烁的效果,这儿可以采用三种方案,一种是用这种循环引用来执行一套方法, 一种是通过NSTimer来定时去调用一个方法。我开始选择了前者,那时候也知道后果,也许这个死循环会一直存在下去,直到这个VC被dealloc,*后一种是通过 core animation来实现。 这种事*推荐的,具体写法,我会在后面开博客进行讲解

-(void)animationAction:(bool)isNeedbreak{
if(!isNeedbreak) {
[self performSelector:@selector(animationAction:) withObject: [NSNumber numberWithBool:YES] afterDelay:2];
}
}
2. 由于有上面这个担心所以,我在popviewcontroller, 控制器出栈的时候,我调用了如下方法,那个时候太粗心了,大概比方,是我想延迟2s执行一个方法,这个过程中,我想终止方法,那就只有通过调用下面两种随意一种,我却很天真的以为,这样就可以完美的终止死循环的调用。

//这个是取消所有的延迟执行函数。
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(animationAction:) object:[NSNumber numberWithBool:YES]];
[NSObject cancelPreviousPerformRequestsWithTarget:self];

3. 发现问题依然存在,所以只能用我的第二种解决办法, 用NSTimer来代替他。代码如下

NSTimer *animationTwoTime = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(productBrandIconAnimationWithIsBreak:) userInfo:[NSNumber numberWithBool:NO] repeats:YES];

4. 然后在vc出栈的时候,然后把NSTimer进行 invalidate下。

总结:

1.以后一定要慎用用for循环来进行实现动画的连续执行.
2. 这种动画效果尽量用 core animation来进行解决。

 

iOS – Shell 脚本学习入门

解释器与编译器
1. 编译器过程:  源代码 – 预处理器 – 编译器 – 目标代码 – 链接器 – 可执行程序

2. 解释器过程:  源代码 – 解释器  (python ,shell , js)

 

如何学习脚本: 三步骤
1. 学语法

2. 看脚本

3. 抄

 

基本语法:省略 。 自己去 w3c学习 。

常用语法记录:

#!/bin/bash

IS_ZSH=””
# bash-3.2 和 zsh

: << !
Shebang(Hashbang):一个由井号和叹号构成的字符序列#!出现在文本文件的*行的前两个字符。
操作系统的程序加载器会分析Shebang后的内容,将这些内容作为解释器指令。
并调用该指令,并将载有Shebang的文件路径作为该解释器的参数。

#!/usr/bin/python

#!/usr/bin/env pyhon

env:不同对操作系统,脚本解释器可能被安装于系统的不同的目录,设置到系统的PATH中。
env可以在系统的PATH目录中查找。
上述命令,使用在用户路径中找到的*个Python版本。但是可以通过指定版本号:
#!/usr/bin/env pythonX.x

env也可以指定搜索目录:
#!/usr/bin/env -S -P/usr/local/bin:/usr/bin:${PATH} python
会在/usr/local/bin、/usr/bin、系统PATH搜索python。
!

# echo “单行注释”

: << !
多行注释方式一:
echo “多行注释”
!

: << COMMENT
多行注释方式二:
echo “多行注释”
COMMENT

: ‘
多行注释方式三:
echo “多行注释”

if false; then
多行注释方式四:
echo “多行注释”
fi

((0)) && {
多行注释方式五:
echo “多行注释”
}

# 有空格时,将串包裹起来
VARIABLE=”Some string”
LUE=VARIABLE
# FOO=””
LONG_STRING=”I am Cat\\”
LONG_LONG_STRING=”I am Cat!CAT!Cat!Cat”

: ‘COMMENT
单引号与双引号,括号一定要成对
冒号(:)作为内建命令:占位符、参数扩展和重定向
子进程:在当前的shell下,去打开另一个新shell
env:查看环境变量与常见环境变量说明

PS1:提示符设置 [\u@\h \w \A #\#]\$
1. \h 主机名缩写
2. \u 用户账号名称
3. \w 完整工作路径
4. \A 24小时时间
5. \# 第几个命令
6. \$ 提示符,如果是root,提示符为#,否则是$
HOME:代表用户主文件夹。
SHELL:当前使用的是那个SHELL
PATH:执行文件查找路径
export: 自定义变量转环境变量
locale:显示语系变量
read [-pt] 变量 读取键盘变量
-p:提示符
-t:等待秒数

$(( 20 + 5 * 6)):返回双括号内算数运算的结果。
expr命令是一款表达式计算工具,使用它完成表达式的求值操作。
eval会对后面的cmdLine进行两遍扫描,如果*遍扫描后,cmdLine是个普通命令,则执行此命令;
如果cmdLine中含有变量的间接引用,则保证间接引用的语义。
type:显示命令属性,不加参数时,会显示该命令是内置命令还是其他。
-t:显示命令属性缩写
file:代表外部命令。
alias:代表该命令为别名。
builtin:内置命令。
-p:为外部命令时,会显示命令所在的文件
-a:将PATH中设置的所有的与命令名相关的列出来
echo [-ne][字符串]
-n 不要在*后自动换行
-e 若字符串中出现以下字符,则特别加以处理
\a 发出警告;
\b 删除前一个字符;
\c 不产生进一步输出 (\c 后面的字符不会输出);
\f 换行但光标仍旧停留在原来的位置;
\n 换行且光标移至行首;
\r 光标移至行首,但不换行;
\t 插入tab;
\v 与\f相同;
\\ 插入\字符;
\nnn 插入 nnn(八进制)所代表的ASCII字符;
用echo命令打印带有色彩的文字:
文字色:
echo -e “\e[1;31mThis is red text\e[0m”
\e[1;31m 将颜色设置为红色
\e[0m 将颜色重新置回
颜色码:重置=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,洋红=35,青色=36,白色=37
背景色:
echo -e “\e[1;42mGreed Background\e[0m”
颜色码:重置=0,黑色=40,红色=41,绿色=42,黄色=43,蓝色=44,洋红=45,青色=46,白色=47
文字闪动:
echo -e “\033[37;31;5mMySQL Server Stop…\033[39;49;0m”
红色数字处还有其他数字参数:0 关闭所有属性、1 设置高亮度(加粗)、4 下划线、5 闪烁、7 反显、8 消隐
COMMENT’

#
# if [ true ]; then
# :
# fi

#${VAR:=DEFAULT}
: ${VAR:=DEFAULT}
echo $VAR

: ‘COMMENT
1. Shell变量默认为字符串。shell不关心这个串是什么含义。
2. Shell默认的数值运算是整数类型。所以若要进行数学运算,必须使用一些命令例如declare、expr、双括号等。
3. Shell变量可分为两类:
i. 局部变量:只在创建它们的shell中可用。在函数内定义,函数执行后就被删除。
ii. 环境变量:可以在创建它们的shell及其派生出来的任意子进程中使用。在整个脚本执行期间,只要没有被删除就一直存在。
3. 定义规则:变量名必须以字母或下划线字符开头。其余的字符可以是字母、数字(0~9)或下划线字符。任何其他的字符都标志着变量名的终止。
小写敏感。
4. 给变量赋值时,等号周围不能有任何空白符。
5. 通常大写字符为系统默认变量。个人习惯。
6. set:查看所有变量(含环境变量与自定义变量),以及设置shell变量的新变量值。
-a:标示已修改的变量,以供输出至环境变量。
-b:使被中止的后台程序立刻回报执行状态。
-e:若指令传回值不等于0,则立即退出shell。
-f:取消使用通配符。
-h:自动记录函数的所在位置。
-H Shell:可利用”!”加<指令编号>的方式来执行history中记录的指令。
-k:指令所给的参数都会被视为此指令的环境变量。
-l:记录for循环的变量名称。
-m:使用监视模式。
-n:只读取指令,而不实际执行。
-p:启动优先顺序模式。
-P:启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。
-t:执行完随后的指令,即退出shell。
-u:当执行时使用到未定义过的变量,则显示错误信息。
-v:显示shell所读取的输入值。
-x:执行指令后,会先显示该指令及所下的参数。
7. declare/typeset [-aixrp] 变量
-a 将变量定义成数组
-i 将变量定义成整数
-x 将变量定义成环境变量
-r 将变量定义成readonly
-p:显示变量定义的方式和值
+:取消变量属性,但是 +a 和 +r 无效,无法删除数组和只读属性,可以使用unset删除数组,但是 unset 不能删除只读变量。
8. local关键字,用来在作用域内创建变量。出来作用域被销毁。
9. export为shell变量或函数设置导出属性,成为环境变量。无法对未定义的函数添加导出属性。同时,重要的一点是,export的效力仅及于该次登陆操作。
注销或者重新开一个窗口,export命令给出的环境变量都不存在了。
-f:代表[变量名称]为函数名称。。
-n:删除变量的导出属性。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
-p:显示全部拥有导出属性的变量。
-pf:显示全部拥有导出属性的函数。
-nf:删除函数的导出属性。
–:在它之后的选项无效。
10. 通配符
*:匹配任意字符串,包括空字符串,不包含对“/”字符的匹配。
?:匹配任意单个字符,不能匹配“/”字符。
[abc]:匹配“a”或者“b”或者“c”字符。
[^abc]:不匹配“a”或者“b”或者“c”字符。
[a-z]:匹配26个英文小写字符中任意一个。
用set命令可以查看所有的变量,unset var命令可以清除变量var,var相当于没有定义过。
readonly var可以把var变为只读变量,定义之后不能对var进行任何更改。

COMMENT’

if [[ -n $IS_ZSH ]]; then
# 根据变量属性强制转换值的英文大小写。
declare -u uc_var=’abc’
declare -l lc_var=’ABC’
# 显示’ABC abc’;
echo “${uc_var} ${lc_var}”
fi

# 执行后显示7,注意空格。
# expr 3 + 4
# result=`expr 2 + 3`
# echo $result

# # 没有指定整型属性,输出为字符串’a+b’。
# declare a=3 b=4 c
# c=a+b
# # a+b
# echo ${c}
# declare -p a

# # 不过可以使用以下方式赋值。
# c=$((a+b))
# # 7
# echo ${c}

# 设置了整型属性就可以直接加了。
# declare -i a=3 b=4 c
# c=a+b
# # 7
# echo ${c}
# declare -p a

# declare -p VARIABLE

# # 定义函数内的全局变量
# function test() {
# declare testA=3
# VARIABLE=”Value”
# local testB=3
# # 让我们查看它们的属性。
# declare -p testA VARIABLE testB
# }
# # 执行函数。
# test
# #Value
# echo $testA $VARIABLE $testB

# export a b=3
# # 当然也可以先定义后增加导出属性
# export VARIABLE
# 删除变量的导出属性
# export -n a b
# function func_1(){ echo ‘123’; }
# # 为已定义函数增加导出属性
# export -f func_1
# # 删除函数的导出属性
# export -fn func_1

# set 11 22 33 44
# # 44
# echo $4
# # $4
# echo “\$$#”
# # 44 *遍得到变量个数4,第二遍去第四个
# eval echo “\$$#”

# declare mylove=’Cat’ #定义新变量
# # env | grep mylove mylove=Cat
# set -a mylove #设置为环境变量

# echo “./*.sh”
# echo “./*”.sh
# MMM=`echo “./*.sh”`
# echo $MMM
# echo `echo “./*.sh”`
# echo ./*.sh
# set — “./”*.sh
# echo “$1″

: ‘COMMENT
数组:
var[1]=”var1”
var[2]=1
COMMENT’

# source Shell.sh
export VARIABLE

unset VARIABLE
echo $VARIABLE

: ‘COMMENT
运行方式:
1. sh:
使用$ sh script.sh执行脚本时,当前shell是父进程,生成一个子shell进程,在子shell中执行脚本。
脚本执行完毕,退出子shell,回到当前shell。$ ./script.sh与$ sh script.sh等效。
2. source:
使用$ source script.sh方式,在当前上下文中执行脚本,不会生成新的进程。脚本执行完毕,回到当前shell。
$ . script.sh与$ source script.sh等效。
3. exec方式:
使用exec command方式,会用command进程替换当前shell进程,并且保持PID不变。
执行完毕,直接退出,不回到之前的shell环境。
COMMENT’

: ‘COMMENT
参数扩展:通过符号$获得参数中存储的值。
1. 间接参数扩展${!parameter},,zsh不支持
i. ${parameter-string}:当parameter未设置则替换成string,不更改parameter值。否则,不做处理。
ii. ${parameter=string}:当parameter未设置则替换成string,更改parameter值。否则,不做处理。
iii. ${parameter?string}:parameter没有设置,则把string输出到标准错误中。否则,不做处理。
iiii. ${parameter+string}:当parameter为空的时替换成string。否则,不做处理。
2. 冒号后面跟 等号,加号,减号,问号(⚠不能有空格):
i. ${parameter:-string}:当parameter未设置或者为空则替换成string,不更改parameter值。
ii. ${parameter:=string}:当parameter未设置或者为空则替换成string,更改parameter值。
iii. ${parameter:?string}:若变量parameter不为空,则使用变量parameter的值。
若为空,则把string输出到标准错误中,并从脚本中退出。
iiii. ${parameter:+string}:当parameter不为空的时替换成string。若为空时则不替换或者说是替换空值。
3. 子串扩展:${parameter:offset}和${parameter:offset:length}。
从offset位置开始截取长度为length的子串,如果没有提供length,则是从offset开始到结尾。
i. offset可以是负值,且必须与冒号有间隔或者用()包裹。开始位置是从字符串末尾开始算起,然后取长度为length的子串。
例如,-1代表是从*后一个字符开始。
ii. parameter是@,也就是所有的位置参数时,offset必须从1开始。
4. 替换:${parameter/pattern/string}、${parameter//pattern/string}、${parameter/pattern}和${parameter//pattern}。
大小写敏感。string为空时,则相当于将匹配的子串删除。 parameter之后如果是/,则只匹配遇到的*个子串;
parameter之后如果是//,则匹配所有的子串。
5. 删除:${parameter#pattern}、${parameter##pattern}、${parameter%pattern}和${parameter%%pattern}。
i. # 是去掉左边,% 是去掉右边。单一符号是*小匹配﹔两个符号是*大匹配。
6. 参数长度:${#parameter}
COMMENT’

: ‘COMMENT
标准输入(stdin):代码为0,使用<或<<;
标准输出(stdout):代码为1,使用>或>>;
标准错误输出(stderr):代码为2,使用2>或2>>;
1> 以覆盖的方式将正确的数据输出到指定到文件或设备;
1>> 以累加到方法将正确到数据输出到指定到文件或者设备上;
2> 以覆盖的方式将错误的数据输出到指定到文件或设备;
2>> 以累加的方式将错误的数据输出到指定到文件或设备;
2>/dev/null 将错误到数据丢弃,只显示正确到数据
2>&1 或者 &>将正确到数据和错误到数据写入同一个文件
cmd;cmd 不考虑命令相关性,连续执行。
当前一个命令执行成功会回传一个 $?=0的值。
cmd1 && cmd2 如果*个命令的$?为0,则执行第二个命令。
cmd1 || cmd2 如果*个命令的$?为0,则不执行第二个命令。否则执行第二个命令。
|:管道仅能处理前面一个命令传来的正确信息,将正确信息作为stdin传给下一个命令
– :stdin和stdout利用减号“-“来代替
COMMENT’

: ‘COMMENT
sh [-nvx] scripts.h
-n:不执行,仅检查语法。
-v:在执行脚本之前,先将脚本内容输出。
-x:将使用到的脚本内容,输出。
COMMENT’

: ‘COMMENT
当条件成立,就进行循环:
while [ condation ] #判断条件
do #循环开始
程序
done #循环结束
当条件成立,就终止循环:
until [ condation ] #判断条件
do #循环开始
程序
done #循环结束
按照指定次数循环:
for var in con1 con2 con3 …
do
程序
done
for (( 初始值; 限制值; 执行步长 ))
do
程序
done
COMMENT’

: ‘COMMENT
多分支语句判断
除*后一个分支外(这个分支可以是普通分支,也可以是*)分支),其它的每个分支都必须以;;结尾,;;代表一个分支的结束,不写的话会有语法错误。
*后一个分支可以写;;,也可以不写,因为无论如何,执行到 esac 都会结束整个 case in 语句。
case $变量 in
“*个变量内容”)
程序
;; #结束
*) # 用来托底,没有匹配到数据
;;
esac
COMMENT’

: ‘COMMENT
一个条件判断:
if [ condation ]; then
成立
else
不成立
fi
多条件判断:
if [ condation ]; then
成立
elif [ condation ]; then
成立
else
不成立
fi
COMMENT’

: ‘COMMENT
shift:参数号码偏移。会移动变量,可以接数字,代表移动前面几个参数的意思
COMMENT’

: ‘COMMENT
[]:判断符号,两个等号和一个等号,效果类似。
1. 中括号里面的每个组件都需要空格分隔。
2. 中括号的变量,使用双引号
3. 中括号的常量,使用单引号或双引号
COMMENT’

: ‘COMMENT
test命令测试:
1. test n1 -eq n2:
-eq:相等
-ne:不等
-gt:大于
-lt:小于
-ge:大于等于
-le:小于等于
2. 字符串判断
-z string:判断string是否为0,为空,则为true。
-n string:判断string是否非0,为空,则为false。
string1 = string2:字符串是否相等,相等为true。
string1 != string2:字符串是否不等,相等为false。
3. 多重条件判断
-a:两个条件同时成立,为true。
-o:两个条件任何一个成立,为true。
!:反向。
4. 文件类型判断
-e:文件名是否存在。
-f:该文件名是否存在且是否为文件。
-d:该名称是否存在且为目录。
-L:该名称是否存在且是否为链接文件。
5. 文件权限检测
-r:是否存在是否有可读权限。
-w:是否存在是否有可写权限。
-x:是否存在是否有可执行权限。
-s:是否存在且为非空白文件。
6. 两文件比较
-nt 文件1是否比文件2新。
-ot 文件1是否比文件2旧。
-ef 文件1和文件2是否为同一个文件。
COMMENT’

# # Some string
# echo “${!VALUE}”

# #
# echo “${FOO-“Cat-“}”
# # Cat-
# echo “${FOO=”Cat-“}”
# #
# echo “${FOO+”Cat-“}”
# # Cat-
# echo “${FOO?”Cat—-“}”

# # Cat-
# echo “${FOO:-“Cat-“}”
# # Cat=
# echo “${FOO:=”Cat=”}”
# # Cat=
# echo “${FOO:?”Cat?”}”
# #Cat+
# echo “${FOO:+”Cat+”}”

# #Cat\
# echo “${LONG_STRING:5}”
# #Cat
# echo “${LONG_STRING:5:3}”
# #Cat
# echo “${LONG_STRING: -4:3}”
# #Cat
# echo “${LONG_STRING: -4:3}”
# #Cat
# echo “${LONG_STRING:(-4):3}”

# # I am Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING/cat}”
# # I am LGCat!CAT!LGCat!LGCat
# echo “${LONG_LONG_STRING//Cat/LGCat}”

# # am Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#* }”
# # am Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#? }”

# # at!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*[Cc]}”

# if [[ -n $IS_ZSH ]]; then
# # Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*(AT|m)}”
# echo “—–${LONG_LONG_STRING#*(AT|m)}”
# echo “—–${LONG_LONG_STRING#[A-z]**(AT|m)}”
# # !Cat!Cat
# echo “—–${LONG_LONG_STRING#*(AT|mm)}”
# fi

# # m Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*[a-t]}”
# # m Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*[a-t]}”
# # am Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING#*[^A-Z]}”

# echo `echo “./*.sh”`
# echo `echo “./”*.sh`

# # Cat!CAT!Cat!Cat
# echo “${LONG_LONG_STRING##* }”
# # at
# echo “${LONG_LONG_STRING##*[Cc]}”
# #
# echo “${LONG_LONG_STRING##*[a-t]}”

# # I am
# echo “${LONG_LONG_STRING% *}”
# # I
# echo “${LONG_LONG_STRING%% *}”

# # 20
# echo “${#LONG_LONG_STRING}”

# # i am cat!cat!cat!cat
# echo “$(echo “${LONG_LONG_STRING}” | tr “[:upper:]” “[:lower:]”)”
# # I AM CAT!CAT!CAT!CAT
# echo “$(echo “${LONG_LONG_STRING}” | tr “[:lower:]” “[:upper:]”)”

# if [[ -n $IS_ZSH ]]; then
# # I AM CAT!CAT!CAT!CAT
# echo “${LONG_LONG_STRING:u}”
# # i am cat!cat!cat!cat
# echo “${LONG_LONG_STRING:l}”
# fi

# : ${TEM:=”./”*.sh}
: ‘COMMENT
函数的声明形式:
function 函数名 {
函数体
}
function 函数名() {
函数体
}
函数名() {
函数体
}
1. 有funtion,可以不写(),没有function,必须写()。
2. 函数名和”{“之间必须有空格。
3. 不得声明形式参数。
4. 必须在调用函数地方之前,声明函数
5. 无法重载
6. 后来的声明会覆盖之前的声明
7. 没有返回值的函数,默认返回函数内*后一条指令的返回值。有返回值的函数,只能返回整数。
8. 需要获得函数值,只能通过$?获得。通过=获得是空值。

我们可以将shell中函数,看作是定义一个新的命令,因此各个输入参数直接用空格分隔。
一次,命令里面获得参数方法可以通过:$0…$n得到。$0代表函数本身。
$#:传入的参数的个数。
$*:所有的位置参数(作为单个字符串)。
$@:所有的位置参数(每个都作为独立的字符串)。
$?:当前shell进程中,上一个命令的返回值,如果上一个命令成功执行则$?的值为0,否则为其他非零值。
$$:当前shell进程的pid。
$!:后台运行的*后一个进程的pid。
$-:显示shell使用的当前选项。
$_:之前命令的*后一个参数。
COMMENT’

# 无输出
# logic
# function DoWork {
# local LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# DoWork() {
# local LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }

# 无输出
# logic
# function DoWork {
# local LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# echo $LG_CAT
# echo `DoWork`
# 无输出
# logic
# function DoWork {
# LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# echo $LG_CAT
# echo `DoWork`
# logic
# 无输出
# function DoWork {
# LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# echo `DoWork`
# echo $LG_CAT
# logic
# logic
# LG_Cat
# function DoWork {
# LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# DoWork
# echo `DoWork`
# echo $LG_CAT
# logic
# 2
# logic
# 0
# function DoWork {
# LG_CAT=”LG_Cat”
# echo “logic”
# return 2
# }
# DoWork
# echo “$?”
# echo `DoWork`
# echo “$?”
# function DoWork {
# echo “特殊变量:\n
# \$#:$#\\n
# \$0:$0\\n
# \$1:$1\\n
# \$2:$2\\n
# \$*:$*\\n
# \$@:$@\\n
# \$$:$$\\n
# \$-:$-\\n
# \$_:$_
# ”
# return 2
# }
# DoWork “Cat” “LGCat”
array=($MMM 1 2)

echo $array

declare -p MMM

declare -p array
if [[ -n $IS_ZSH ]]; then
declare -A fruits=([‘apple’]=’red’ [‘banana’]=’yellow’)
# 显示所有关联数组。
declare -A
# red yellow
echo ${fruits[@]}
echo ${fruits[*]}
echo ${(@k)fruits}
fi

脚本实现:
while [[ $# -gt 0 ]]; do
case $1 in
-d|–directory)
shift
echo “$1—-”
shift
;;
-k|–keyword)
shift
echo “$1 —-”
shift
;;
-s|-source)
echo “$1 —-source”
shift
;;
-f|–framework)
shift
;;
-l|–lib)
shift
;;
-h|–help)
Show_Help
exit 0
;;
*)
echo “Unknown option: $1”
exit 1
esac
done
通过上面的脚本来学习脚本:

while do  case : 循环

[[ $# -gt 0 ]] :

$# ,获取执行脚本的时候传递的参数个数

-gt : 大于

$1: 循环的参数值

exit: 0 正常退出

其他exit: 为自定义意义。

 

自定义一个脚本说明文档

格式如下:

Show_Help() {
# 结束符
cat <<EOF

find_api.sh –directory <dir> 在指定目录指定文件内搜索指定关键字。

-d|–directory <dir> – 指定查找目录,默认当前所在目录
-k|–keyword <word> – 查找关键字
-s|–source – 指定查找源码文件
-f|–framework – 指定查找framework文件
-l|–lib – 指定查找libs文件
–help – prints help screen

EOF

}
注意EOF不要有空格。

cat <<EOF

中间写上脚本的使用文档

EOF

查找、细分查询功能
find . -name “*.framework”
find . -name “*.h” -exec echo {} \;
grep -E “some|weak” -A 1 -B 1 -i
nm -pa <mach-o>
find : 查找文件  -name ‘字串’  查找文件名匹配所给字串的所有文件,字串内可用通配符 *、?、[ ]。

grep: 搜索具体文件中的关键字   -E   –extended-regexp             # 将范本样式为延伸的普通表示法来使用,意味着使用能使用扩展正则表达式

 

数组与For循环
# <<< 表示将右侧的字符串传递到左侧命令的标准输入。
# < 将文件的内容传递到命令的标准输入
# << 将带有结束标志的文档内容传递到命令的标准输入

#test
#-e:文件名是否存在。
#-f:该文件名是否存在且是否为文件。
#-d:该名称是否存在且为目录。
#-L:该名称是否存在且是否为链接文件。

#read
#-a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
#-r 屏蔽\,如果没有该选项,则\作为一个转义字符,有的话 \就是个正常的字符了。

# read通过输入重定向,把file的*行所有的内容赋值给变量line,循环体内的命令一般包含对变量line的处理;然后循环处理file的第二行、第三行。。。一直到file的*后一行。
#for是每次读取文件中一个以空格为分割符的字符串。
#for是每次读取字符串中一个以空格为分割符的字符串。
#weak|cat|Hank
Find_Api() {
local key_word=””
if [[ -n “${KEYWORD}” ]]; then
read -a names <<< “${KEYWORD}”
for name in ${names[@]}
do
if [[ ! -n “${key_word}” ]]; then
key_word=”$name”
else
key_word=”$key_word|$name”
fi
done
else
echo “请输入查找的关键字!”
exit 1
fi
echo “$key_word —-”
}
 

iOS开发之第三方登录微信– 史上*全*新第三方登录微信方式实现

*新版本的微信登录实现步骤实现:

1.在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的移动应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。 地址: 点击打开链接

2. 下载*新的SDK   地址: 点击打开链接

SDK内容如下:

结构解析:

从上到下依次说明:

1. 静态库,直接拖入工程。

2. ready.text自己看

3. 授权SDK。

4. 登录方法所在类。

5.  一些常用的对象类。

iOS微信登录注意事项:

1、目前移动应用上微信登录只提供原生的登录方式,需要用户安装微信客户端才能配合使用。
2、对于Android应用,建议总是显示微信登录按钮,当用户手机没有安装微信客户端时,请引导用户下载安装微信客户端。
3、对于iOS应用,考虑到iOS应用商店审核指南中的相关规定,建议开发者接入微信登录时,先检测用户手机是否已安装微信客户端(使用sdk中isWXAppInstalled函数 ),对未安装的用户隐藏微信登录按钮,只提供其他登录方式(比如手机号注册登录、游客登录等)。

iOS微信登录大致流程:

1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
2. 通过code参数加上AppID和AppSecret等,通过API换取access_token;
3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。

示意图:

接下来就进入正题:

1.配置工程
1. 新建一个工程。
2. 把下载下来的sdk中的.h文件与静态库全部拖入工程。
3.  加入依赖库

4.  URL – Types  (加入 appid)
target  –  Info – URL Types

5. 白名单
当程序出现此错误
-canOpenURL: failed for URL: “weixin://app/wx5efead4057f98bc0/” – error: “This app is not allowed to query for scheme weixin”
就说明没有针对iOS9 增加白名单。 在info.plist文件中加入 LSApplicationQueriesSchemes

App Transport Security 这个是让程序还是用http进行请求。
LSApplicationQueriesSchemes 这个是增加微信的白名单。

6.  现在编译应该是没有问题了。

2. 终于到令人兴奋的代码部分了。 直接上代码。
//
// AppDelegate.m
// weixinLoginDemo
//
// Created by 张国荣 on 16/6/20.
// Copyright © 2016年 BateOrganization. All rights reserved.
//

#import “AppDelegate.h”
#import “WXApi.h”

//微信开发者ID
#define URL_APPID @”app id”

@end

@implementation AppDelegate

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

//向微信注册应用。
[WXApi registerApp:URL_APPID withDescription:@”wechat”];
return YES;
}

-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options{

/*! @brief 处理微信通过URL启动App时传递的数据
*
* 需要在 application:openURL:sourceApplication:annotation:或者application:handleOpenURL中调用。
* @param url 微信启动第三方应用时传递过来的URL
* @param delegate WXApiDelegate对象,用来接收微信触发的消息。
* @return 成功返回YES,失败返回NO。
*/

return [WXApi handleOpenURL:url delegate:self];
}

/*! 微信回调,不管是登录还是分享成功与否,都是走这个方法 @brief 发送一个sendReq后,收到微信的回应
*
* 收到一个来自微信的处理结果。调用一次sendReq后会收到onResp。
* 可能收到的处理结果有SendMessageToWXResp、SendAuthResp等。
* @param resp具体的回应内容,是自动释放的
*/
-(void) onResp:(BaseResp*)resp{
NSLog(@”resp %d”,resp.errCode);

/*
enum WXErrCode {
WXSuccess = 0, 成功
WXErrCodeCommon = -1, 普通错误类型
WXErrCodeUserCancel = -2, 用户点击取消并返回
WXErrCodeSentFail = -3, 发送失败
WXErrCodeAuthDeny = -4, 授权失败
WXErrCodeUnsupport = -5, 微信不支持
};
*/
if ([resp isKindOfClass:[SendAuthResp class]]) { //授权登录的类。
if (resp.errCode == 0) { //成功。
//这里处理回调的方法 。 通过代理吧对应的登录消息传送过去。
if ([_wxDelegate respondsToSelector:@selector(loginSuccessByCode:)]) {
SendAuthResp *resp2 = (SendAuthResp *)resp;
[_wxDelegate loginSuccessByCode:resp2.code];
}
}else{ //失败
NSLog(@”error %@”,resp.errStr);
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@”登录失败” message:[NSString stringWithFormat:@”reason : %@”,resp.errStr] delegate:self cancelButtonTitle:@”取消” otherButtonTitles:@”确定”, nil];
[alert show];
}
}
}

@end
下面是登录的类。

//
// ViewController.m
// weixinLoginDemo
//
// Created by 张国荣 on 16/6/20.
// Copyright © 2016年 BateOrganization. All rights reserved.
//

#import “ViewController.h”
#import “WXApi.h”
#import “AppDelegate.h”
//微信开发者ID
#define URL_APPID @”appid”
#define URL_SECRET @”app secret”
#import “AFNetworking.h”
@interface ViewController ()<WXDelegate>
{
AppDelegate *appdelegate;
}
@end

@implementation ViewController

– (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark 微信登录
– (IBAction)weixinLoginAction:(id)sender {

if ([WXApi isWXAppInstalled]) {
SendAuthReq *req = [[SendAuthReq alloc]init];
req.scope = @”snsapi_userinfo”;
req.openID = URL_APPID;
req.state = @”1245″;
appdelegate = [UIApplication sharedApplication].delegate;
appdelegate.wxDelegate = self;

[WXApi sendReq:req];
}else{
//把微信登录的按钮隐藏掉。
}
}
#pragma mark 微信登录回调。
-(void)loginSuccessByCode:(NSString *)code{
NSLog(@”code %@”,code);
__weak typeof(*&self) weakSelf = self;

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];//请求
manager.responseSerializer = [AFHTTPResponseSerializer serializer];//响应
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@”text/html”,@”application/json”, @”text/json”,@”text/plain”, nil];
//通过 appid secret 认证code . 来发送获取 access_token的请求
[manager GET:[NSString stringWithFormat:@”https://api.weixin.qq.com/sns/oauth2/access_token?appid=%@&secret=%@&code=%@&grant_type=authorization_code”,URL_APPID,URL_SECRET,code] parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {

} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { //获得access_token,然后根据access_token获取用户信息请求。

NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
NSLog(@”dic %@”,dic);

/*
access_token 接口调用凭证
expires_in access_token接口调用凭证超时时间,单位(秒)
refresh_token 用户刷新access_token
openid 授权用户唯一标识
scope 用户授权的作用域,使用逗号(,)分隔
unionid 当且仅当该移动应用已获得该用户的userinfo授权时,才会出现该字段
*/
NSString* accessToken=[dic valueForKey:@”access_token”];
NSString* openID=[dic valueForKey:@”openid”];
[weakSelf requestUserInfoByToken:accessToken andOpenid:openID];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@”error %@”,error.localizedFailureReason);
}];

}

-(void)requestUserInfoByToken:(NSString *)token andOpenid:(NSString *)openID{

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:[NSString stringWithFormat:@”https://api.weixin.qq.com/sns/userinfo?access_token=%@&openid=%@”,token,openID] parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {

} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSDictionary *dic = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
NSLog(@”dic ==== %@”,dic);

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@”error %ld”,(long)error.code);
}];
}

– (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end

大功告成。

 

Mac系统上使用SnailSVN的配置步骤

1·在Apple Store下载SvnSnail: Lite版本(需输入apple id)
2·打开SnailSVN
3·点击General,打开系统设置(Open System Preference),勾上SnailSVNLite: Finder Extensions
4·点击SVN Settings,选择paths
5·创建.ssh路径,先打开终端Terminal,然后输入 mkdir .ssh,创建完成后就可以paths中选择你刚刚创建的.ssh,再依次选择bin以及Applications路径。/Users/xxx/.ssh, /urs/local/bin, /Applications
6·新建一个文件夹名为 svn-workspace 作为工作路径
7·点击SnailSVN Lite左上角的File->Svn Checkout,输入项目的svn地址,检出项目的路径到 svn-workspace,点击确定,弹出验证框,输入svn帐号密码即可。
8·结束

吐槽 iOS 过于“智能”的一个 feature

众所周知 iOS 的普通闹钟铃是没有声音“渐强”功能的,临时设置的闹钟响起来能把人吓的心脏骤停。起床闹铃还不错,无奈只能设置一组。

以上是前提

所以就准备另辟蹊径了,找一段舒缓的音频,用 Audition 拉了淡入,这样铃声本身就是渐强的,导入 iPhone 测试。
好嘛,iOS 会“智能”地把铃声增益进行调整,音量又重新拉平了。

lychs1998

Reply    1

lychs1998   30 天前   ❤️ 8

库克:不然 AppleWatch 的闹钟怎么吹?
shenlanAZ

Reply    2

shenlanAZ   30 天前

用个 Android 当闹钟,法定假日放着不管都不会响,还能播报天气等等….

哪个爽用哪个。

reayyu

Reply    3

reayyu   30 天前 via iPhone

我用 garage band 手动渐入没被拉平啊…
jiaslbang

Reply    4

jiaslbang   30 天前

厨子:我要我觉得
hfl1995

Reply    5

hfl1995   30 天前   ❤️ 2

为什么不用就寝功能呢?那个铃声很舒缓,而且作息管理更智能
promisenev

Reply    6

promisenev   30 天前

iOS 有个细节,早上闹钟想了以后,拿起手机,闹钟声音会自动降低..
lc125581200

Reply    7

lc125581200   30 天前   ❤️ 1

闹钟无法提醒事项上显示了,真的难受
elfive

Reply    8

elfive   30 天前 via iPhone   ❤️ 1

同意 5 楼,就寝功能应该是符合你的需求的。
不过 iOS 这些自作主张的地方确实不太符合一些人的直观使用感受。
可能是他想着闹钟就是要引起你的注意,提醒你别忘事一类的,如果声音渐变,你可能会有点注意不到,他可能认为闹钟不单纯只用来提醒起床的吧。
a302800411

Reply    9

a302800411   30 天前

买一个 100 块钱的智能音箱,在闹钟 /提醒 这两件事上,用起来还是很方便的
hhyvs111

Reply    10

hhyvs111   30 天前   ❤️ 1

楼上的,仔细读题,楼主都说了起床闹钟只能弄一个时间点
zhhanging

Reply    11

zhhanging   30 天前   ❤️ 1

还有一个 feature,如果没被闹钟叫醒的话,过一段时间闹钟就不会再响了,非常人性化。既然你不想起来,我干脆不叫你了。
arphone

Reply    12

arphone   30 天前   ❤️ 1

*近用 12mini 发现一个新的 feature, 闹钟会准时提醒,但是有时候并没有声音。
Sharenruma

Reply    13

Sharenruma   30 天前 via iPhone

@hfl1995
@elfive
一直在用啊,但单位中午有两个半小时的休息时间,没办法再设置一组。还有有时候出差要临时设个闹钟~
venster

Reply    14

venster   30 天前 via Android

@zhhanging 还有一个如果还没到闹钟时间就开始用手机,快响的时候会问你是不是已经醒了,需不需要关掉闹钟。
Sharenruma

Reply    15

Sharenruma   30 天前 via iPhone

@reayyu 求教 ios 的 garage band 怎么淡入,只找到了淡出
zjuster

Reply    16

zjuster   30 天前

你在 health 里配置起床闹钟的话,建议用系统的起床铃声,那些铃声是选取过的,变相实现了你的需求。
Sharenruma

Reply    17

Sharenruma   30 天前 via iPhone

@arphone 据我测试这个并不是你拿起手机生效的。而是有 face ID 的手机,里面有个功能叫 attention aware features
这个功能在电话响起时也会起作用的
reayyu

Reply    18

reayyu   30 天前 via iPhone

@Sharenruma 用录音机模块导入音频 点屏幕*左侧那个麦克风标志 菜单选择”自动化” 然后在波形图上就有一条代表音量的曲线 可以添加节点上下拖动控制声音
Mithril

Reply    19

Mithril   30 天前

@arphone 那是个 bug 。。。
你如果设了一个和起床闹钟同时的普通闹钟,这俩有可能都不会响。。。
Rekkles

Reply    20

Rekkles   30 天前

被这个闹钟已经弄的神经衰弱,不小心就会被头疼一天。
ZanderHsueh

Reply    21

ZanderHsueh   30 天前

@zhhanging 如果不是不想起,是睡太死起不来。。。
zhhanging

Reply    22

zhhanging   30 天前

@ZanderHsueh 是的,我已经迟到好几次了
maninfog

Reply    23

maninfog   30 天前

滴滴滴滴滴滴~~~~滴滴滴滴滴滴滴~~~
ohiu

Reply    24

ohiu   30 天前 via iPhone

反正我是受不了 iOS 的任何一个闹钟铃声,宁愿每天点一捆烈性定时炸药
longline

Reply    25

longline   29 天前   ❤️ 4

@maninfog 滴滴滴滴滴滴~~~~滴滴滴滴滴滴滴~~~办公室午休有用这铃声的,心率直接干到 120..
liuw666

Reply    26

liuw666   29 天前 via iPhone

ios 不能过滤节假日 ,也挺烦人的
pusheax

Reply    27

pusheax   29 天前

适合我这种,不开*大声闹钟把自己吓一跳就起不来的人,
ByteCat

Reply    28

ByteCat   29 天前

一直用 就寝,铃声也很好听
jtshs256

Reply    29

jtshs256   29 天前 via iPhone

所以我用两台手机…emmm…
ik

Reply    30

ik   29 天前 via iPhone

就寝的铃声就是渐强吧
dangyuluo

Reply    31

dangyuluo   29 天前

用 Shortcut 的自动化吧
applehater

Reply    32

applehater   29 天前 via Android

cook 每天四点起床是自然醒的吗
turlin

Reply    33

turlin   29 天前

@liuw666 你找下 有捷径可以用 估计楼主也是用这个 所以就没有用起床的那个闹铃 声音是直接就拉满的
qlqsh

Reply    34

qlqsh   29 天前

sleep Cycle,值得拥有,相当给力。

不过付费的太贵了,免费的也够用,而且没广告,就是只能一组,当起床用还行。

Sharenruma

Reply    35

Sharenruma   29 天前 via iPhone

@reayyu 学了一招,感谢!
淡入似乎有些作用,但我把导入的铃声整体分贝数拉低了,放出来*后的声音还是很大,似乎 iOS 还是给我自动增益了。
铃声和闹铃不能分开设置音量真是太不方便了。

APP 顶部推送问题

iPhone 怎么解决国内各种 app 自建的顶部推送问题。。一打开 app,先来一记各种优惠券推送,声音巨大 还关不掉

入职该家公司,当该项目产品经理
or
直接卸载
tanranran Reply 2
tanranran 2 天前
入职该家公司,当该项目产品经理
or
直接卸载
or
放弃用手机
PerFectTime Reply 3
PerFectTime 2 天前
微信小程序?
lscho Reply 4
lscho 2 天前 ❤️ 1
我都不知道为啥有人天天喷微信,微信在推送和广告上已经是国内做的*克制的大型 app 了。
5966 Reply 5
5966 2 天前 via iPhone
某猫吧?
Leonard Reply 6
Leonard 2 天前 ❤️ 2
@lscho #4 因为其他*大多数 app 你看不惯不用就行了,眼不见心不烦。微信你看不惯也得用,用着烦就喷呗。
Justkkk Reply 7
Justkkk 2 天前
@5966 哪只是某猫。。基本国内和钱有关点的 app 都这样了。可能各家还为自建推送 沾沾自喜吧。也不想想为毛大家都不给 app 推送权限
cairnechen Reply 8
cairnechen 2 天前
spam 治理有一条原则是斩断利益链条,从根本上解决 spam,所以想想这些 App 为啥这么做就明白了,基本上没法解决,而且就算解决了这个,也一定会从其他方面找补回来
mygreens Reply 9
mygreens 2 天前
滴滴吧 一打开就响
cjpjxjx Reply 10
cjpjxjx 2 天前
+1,而且弹出的推送刚好遮挡搜索栏,有很大概率在你要点击搜索栏的时候点到推送,我觉得这明显是故意的
cincout Reply 11
cincout 2 天前
这个不是真推送,模仿的推送样式,app 里面实现的,没法屏蔽,尽量不用这个软件吧,大家都烦这种
jwing Reply 12
jwing 1 天前
@lscho 喷微信是*为国内使用*多的社交类 APP,不思进取,很多功能没有,是和 QQ 比,和其他 APP 比 微信算不错了
dagouziwangwang Reply 13
dagouziwangwang 1 天前
@mygreens didi 是真的烦 我经常叫车排队之后把手机放旁边 就打开了通知 结果他天天给我推今天天气不好 哪哪哪堵车了 哪哪哪好玩 我要真有这需求我用得着一个打车软件告诉我吗 气得我把通知权限直接给关了
littiefish Reply 14
littiefish 1 天前 via iPhone
@lscho 还真是矮子群里拔将军。
Modred Reply 15
Modred 1 天前
使用替代软件
or
使用青少年模式(如果有)
or
使用第三方入口(比如滴滴不用 app 用小程序也能打)
ohiu Reply 16
ohiu 1 天前
感化他们
5966 Reply 17
5966 1 天前 via iPhone
@Justkkk 猫这个关闭不了,狗东的还可以关闭,没必要都不用猫了,你一打开本来要在搜索栏搜索,结果弹出推送挡住,实在是不人性化!
finnlewis Reply 18
finnlewis 1 天前 via iPhone
我已经受不了 dd 了已经关掉了
S179276SP Reply 19
S179276SP 1 天前 via Android
@lscho 主要是一个即使通讯软件做的不像是即使通讯软件,比例到现在也没适配安卓 7 的通知栏快捷回复,地球上哪个即使软件没适配?
dcty Reply 20
dcty 1 天前
可以找看看是否有对应的小程序或者轻应用。
qq73666 Reply 21
qq73666 1 天前
木有办法
Roccc Reply 22
Roccc 1 天前
pdd 是真的多 上去就各种中*信息 已卸载
zhuangku556 Reply 23
zhuangku556 1 天前
@Roccc 只能说 pdd 的用户非常吃这一套了
jiayong2793 Reply 24
jiayong2793 1 天前
修改法律,把引导直接定义为诈骗,但要有修改法律的权力

iOS 10.3 改进后的App Review机制

今天没事查看了下iOS 10.3 的变更功能。发现Apple修改了Review机制,提供App内直接Review弹窗。

SKStoreReviewController.requestReview()

如果没有网络则无任何反应。
据说有调用次数限制,不过API中没有提到,我测试也没有触发这个现象。

看了API说明的话,有人可能注意到了,这句话:

available to the App Store by appending the query params “action=write-review” to a product URL.

我测试了下,如果在itms-apps url中添加action=write-review则可以打开AppStore中App评论详情,同时自动打开评论编辑窗口。

func reviewApp(for appId: String) {
if let url = URL(string: “itms-apps://itunes.apple.com/app/id\(appId)?action=write-review”) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}

Mac下的SVN客户端工具Cornerstone使用教程

使用命令当然是一个非常好的选择,但是对我们人类来说还是喜欢图形化界面的操作的。因此本文将介绍我喜欢的一款SVN客户端工具的使用。

 

想要安装的Cornerstone的朋友,我有个坏消息是,如果你想使用它的正版软件,你是需要花费几十美金的,当然这里我带给您的永远都是有价值的好的信息,那就是在我们天朝使用软件还花钱真的是*品,我就奉献一下我在网上找到的一个比较好的破解版:http://pan.baidu.com/s/1o6F31zG

 

这个破解版本的Cornerstone软件安装方法我就不做介绍了,因为太容易。本文重点介绍一下它的使用方法。

 

当你打开软件时候会看到如下图所示界面:

现在是空空如也,因此我们应该做点什么,让它发挥作用。界面还是非常的直观和有引导性的,我直接按灰色区域的“Add repository”

完成点击之后会弹出配置界面,一般我们会使用第四个选项卡”SVN Server”.如图

图中我对SVN服务器做了访问的svn服务器配置,配置介绍如下:

tunnel:访问通道,默认不用修改

Server:我的svn服务器在本地,所以Server填写了localhost

Port:设置端口号,我在服务器上没有配置访问端口号,所以port留空

Repository Path:这个是服务器仓库的目录位置,我这里填写了company,这是因为我在SVN服务的根路径下添加了company仓库。

Nickname:显示名。这个可以随便填写,建议为仓库和用户名的组合。

 

如果您的配置正确应该会添加成功的,如果错误,请检查服务器是否开启和你配置是否正确等。 下图是我们添加仓库成功后的效果图:

 

从图中可以看到我昨天写的《SVN服务器配置实战》中的目录结构了。

 

现在我们来试试CorenerStone是如何代替我们的命令行的.下图介绍各个功能模块的作用:

四、使用简介

 

1.上传项目到repository

 

可以直接拖动到repository的子文件夹中,或是选择软件上方的Import按钮上传,会弹出选项填写所在位置及名称,然后选择Import即可

 

2.下载项目

 

下载分为两种:Export和Check Out,区别在于,Export后的项目不会与repository中的源文件相关联,是一个独立的版本,而Check Out下来的文件会创建一个working copy,参见步骤三的*幅图,此文件与库中源文件相关联,当有新版本(他人修改)或是本地修改(自己修改)时,working copy会显示修改数量,白色数量为他人修改数量,灰色数量为本人修改数量

 

所以如果你是项目中的开发人员,可以选择check out,如果只是下载查看,不希望自己的修改影响到整个项目,*好是选择Export

 

3.版本管理

 

每一次提交会创建一个新版本,在repository中会保存所有历史版本,如下图(可通过修改人及提交信息进行检索版本),所以用svn开发可以很好的控制项目出现不可解决及未知bug时代码的修复问题:

 

svn方便了多人开发同一项目的代码合并问题,但是也有一些事项需要注意:

 

①先更新后提交

 

在看到有新版本(即同伴已经提交代码时),先更新代码,直至working copy不再显示白色圈,然后运行代码确定可运行且功能无误之后再commit自己的代码,否则,会造成项目中出现多处冲突或bug,且很难排查原因

 

②完成独立功能后再提交,且务必填写提交信息

 

每完成一个独立的功能,或解决一个bug之后再提交代码,不要连续多次重复提交,造成版本过多过杂,且提交时务必填写提交信息,交代本次完成了什么功能,方便上图中可以进行message的搜索来查看历史版本

 

③冲突文件

 

原则上同一组开发人员*好不要在同一文件中进行操作,但有时候必须去其他文件中进行操作,或者是误操作,如果同时多人在同一文件的同一位置修改代码,后提交的人会出现版本冲突文件,一般会有三个同样名称不同后缀的文件

 

.mine文件:本人所做修改

 

两个.r0XX文件:XX为数字,数字较小的为更改前的文件,较大的为更改后的文件,在文件中会有<<<< mine .r0XX  >>>>>等字样包含起来的代码,即冲突的地方,此时请和组内同事讨论或自己删除某部分修改文件后进行调试,修复文件

 

针对ios项目:出现某个工程或文件打不开的情况,如果为.project文件无法打开,则选择显示包内容->用文稿打开project.pbxproj文件->搜索.mine,将.mine部分前后<<<< >>>>包含起来的代码删除,工程就可以打开了,如果build时出现某个xib文件打不开的错误,则选中,用文稿打开,跟上文同样操作即可解决无法build的问题

 

④新添加文件

 

提交时新增加的文件显示为问号状态的,请选中右击后 选择Add to Working Copy之后再commit

iOS10.3 app内好评详解 SKStoreReviewController

App Store评分方式
目前方式(无版本限制)

只能通过APP内部打开网页形式,直接跳转到App Store 编辑评论。在评分页面,可以评分和评价,评论更有价值。缺点是跳转到App Store,用户的操作场景的转换,会造成部分用户使用的困扰,可能需要花费较长的等待时间,甚至加载失败等,造成评价数量少。
iOS6 +

在APP内部加载App Store 展示APP信息,但不能直接跳转到评论编辑页面。再加载处App Store展示页面后,需要手动点击 评论→ 撰写评论,多两步操作,部分用户可能存在操作障碍(找不到)。
iOS10.3 +
APP内评分机制是iOS 10.3 中新添功能。用户可以直接在 App 内进行评分,开发者可以对用户在 App Store 的评论进行回复。
APP内评分调用API [SKStoreReviewController requestReview]; (目前唯一),应用会自动弹窗请求用户评分,弹窗不可定制,对处理过程和处理结果无法监控。只能使用该 API 请求评分,不能请求评价和反馈。
一个应用内每年*多使用 3 次弹窗,滥用弹窗,会引起不少用户的反感,甚至因此给应用差评。
iOS10.3版本以前的评分方式依然可以使用。
评分接入方式
目前接入方式(无版本限制)

1、调用方法

– (void)showAppStoreReView

{
NSString *APPID = [PlistReader valueForKeyInConfig:@”APP_ID”];

NSString *appStoreReviewStr = [NSString stringWithFormat: @”itms-apps://itunes.apple.com/app/id%@?action=write-review”,APPID];

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:appStoreReviewStr]];

}

iOS6 +  接入方式
1、引入头文件

#import <StoreKit/StoreKit.h>

2、遵循代理

SKStoreProductViewControllerDelegate

3、调用方法

– (void)showAppStoreReView

{
SKStoreProductViewController *storeProductViewContorller = [[SKStoreProductViewController alloc] init];

storeProductViewContorller.delegate = self;

//加载App Store视图展示

[storeProductViewContorller loadProductWithParameters:

@{SKStoreProductParameterITunesItemIdentifier : [PlistReader valueForKeyInConfig:@”APP_ID”]} completionBlock:^(BOOL result, NSError *error) {
if(error) {
} else {
//模态弹出appstore

[self presentViewController:storeProductViewContorller animated:YES completion:^{
}];

}

}];

}

4、实现代理

– (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[self dismissViewControllerAnimated:YES completion:^{
}];

}

iOS10.3 + 接入方式

1、引入头文件

#import <StoreKit/StoreKit.h>

2、调用方法

– (void)showAppStoreReView

{
//仅支持iOS10.3+(需要做校验) 且每个APP内每年*多弹出3次评分alart

if([SKStoreReviewController respondsToSelector:@selector(requestReview)]) {
//防止键盘遮挡

[[UIApplication sharedApplication].keyWindow endEditing:YES];

[SKStoreReviewController requestReview];

} else {
//不论iOS 版本均可使用APP内部打开网页形式,跳转到App Store 直接编辑评论

NSString *APPID = [PlistReader valueForKeyInConfig:@”APP_ID”];

NSString *nsStringToOpen = [NSString stringWithFormat: @”itms-apps://itunes.apple.com/app/id%@?action=write-review”,APPID];

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:nsStringToOpen]];

}

}

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速