mshen


  • 首页

  • 归档

  • 标签

IAP总结

发表于 2018-09-16

IAP准备工作

  1. 申请沙盒账号 (可以同时给测试也创建一个)
    邮箱ID可以填一个不存在的邮箱,
  2. 填写银行、财务信息(这个我没有实际操作)
  3. 创建一个购买商品

流程

  1. 请求商品ID (商品ID放到服务器)
  2. 根据根据这个商品ID去获取商品信息(不同的地区显示不同的货币符号和价格)
  3. 请求订单ID,每次支付之前都要请求获取一个订单ID(服务器生成的)
  4. 根据之前步骤的信息去发起支付。
  5. 根据苹果的回调状态去处理支付状态。
  6. 如果支付成功,去服务器进行验证,并且以服务器的验证结果为准。

遇到的问题和异常:

  1. 跟服务器验证的请求,需要传一个receipt,但是后端一直返回21002,说receipt格式错误

    解决: 后端想办法解决的,编码的问题。

  2. 在支付中这个状态中断了支付,怎么处理?

    解决: 这个状态下就算重新去发起一个新的支付,回调也一直是购买中。等待IAP的服务器给你推送继续购买的弹框。我的经验是:晚上八点遇到这个情况,第二天早上八点就推送了。

  3. 支付的回调方法中,购买中这个状态做什么处理?

    解决: 刚开始我是写了个转菊花的等待状态,但是发现如果遇到第二种情况,他会一直走回调,阻碍主线程。等待状态可以写在调起支付之前。

  4. 需不需要保存订单信息和receipt?

    解决:其实不需要,每次调起支付之前先判断有没有未结束的交易。代码如下:

           //查询队列里是不是还有没有finish的交易,去服务器验证。
    if ([SKPaymentQueue defaultQueue].transactions.count > 0)                         {
    
        //NSLog(@"交易成功但是没finish的个数为:%zd",[SKPaymentQueue     defaultQueue].transactions.count);
        for (SKPaymentTransaction *transaction in [SKPaymentQueue defaultQueue].transactions)
        {
            if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
                //这个时候沙盒里是有未完成的交易凭证的
                [self completeTransaction:transaction];
            } else if (transaction.transactionState == SKPaymentTransactionStatePurchasing) {
                //这种情况,强行结束交易会崩溃
            }
        }
    }
    

资料

  1. In-App Purchase Programming Guide(《App 内购买项目编程指南》)

  2. APP Store Connect 帮助

  3. IAP需要了解的

  4. [iOS]贝聊 IAP 实战之满地是坑

Category的本质

发表于 2018-08-05

##Category的本质一
链接:https://www.jianshu.com/p/da463f413de7

总结:

  1. 通过runtime加载某个类的所有Category数据。
  2. 把所有Category的方法,属性,协议数据合并到一个大数组中,后面参与斌编译的Category数据,会在数组的前面。
  3. 将合并后的分类数据(方法,属性,协议),插入到类原来数据的前面。

分类中的对象方法和类方法最终会合并到类中,分类中的对象方法合并到类的类对象中,分类中的类方法合并到类的元类对象中。那么这个合并是什么时候发生的呢?是在编译器编译器就帮我们合并好了吗?实际上是在运行期,进行的合并。整个合并的过程是通过runtime进行实现的,最后合并到类对象的方法列表中或者元对象的方法列表中。

下面我们通过将Objective-c的代码转化为c++的源码窥探一下Category的底层结构。我们在命令行进入到存放Person+Test.m这个文件的文件夹中,然后在命令行输入clang -rewrite-objc Person+Test.m,这样Person+Test.m这个文件就被转化为了c++的源码Person+Test.cpp。

合并的时候并不是覆盖,只是在类对象的方法列表中,分类的方法数组被copy放到了列表中原有方法的前面。查找方法的时候总是先查找到分类中最后参与编译的方法。(在Compile Sources靠后的就是后编译的)

##Category的本质<二>load,initialize方法
链接:https://www.jianshu.com/p/b5492c40fe8f

面试题1:Category中有load方法吗?load方法是什么时候调用?

面试题2:load,initialize的区别是什么?它们在Category中的调用顺序以及出现继承时它们之间的调用过程是怎么样的?

那么这篇文章主要就是回答这两个问题。

有load方法。因为load方法的调用并不是objc_msgSend机制,它是直接找到类的load方法的地址,然后调用类的load方法,然后再找到分类的load方法的地址,再去调用它。

1.先调用类的load方法

调用子类的load方法之前会先调用父类的load方法

2.再调用分类的load方法

按照编译先后顺序,先编译,先调用

load方法什么时候调用?

load方法是在runtime加载类和分类的时候调用。

initialize在类第一次接收到消息时调用,也就是objc_msgSend()。

+initialize的调用过程:

1查看本类的initialize方法有没有实现过,如果已经实现过就返回,不再实现。

2.如果本类没有实现过initialize方法,那么就去递归查看该类的父类有没有实现过initialize方法,如果没有实现就去实现,最后实现本类的initialize方法。并且initialize方法是通过objc_msgSend()实现的。

+initialize和+load的一个很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点:

如果子类没有实现+initialize方法,会调用父类的+initialize(所以父类的+initialize方法可能会被调用多次)

如果分类实现了+initialize,会覆盖类本身的+initialize调用。

下面我们把Student类及其分类中的+initialize这个方法的实现去掉,然后增加一个Teacher类继承自Person类。然后我们给Student类和Teacher类都发送alloc消息:

[Student alloc];
[Teacher alloc];

这个时候也就是只有Person类及其分类实现了+initialize方法。那么打印结果会是怎样呢?

2018-07-25 21:47:59.899995+0800 interview - Category[20981:582224] Person (Test2) + initialize
2018-07-25 21:47:59.900112+0800 interview - Category[20981:582224] Person (Test2) + initialize
2018-07-25 21:47:59.900240+0800 interview - Category[20981:582224] Person (Test2) + initialize

这里Person类的+initialize方法竟然被调用了三次,这多少有些出乎意外吧。下面我们来分析一下。

BOOL studentInitialized = NO;
BOOL personinitialized = NO;
BOOL teacherInitialized = NO;

[Student alloc];
//判断Student类是否初始化了,这里Student类还没有被初始化,所以进入条件语句。
if(!studentInitialized){
//判断Student类的父类Person类是否初始化了
if(!personinitialized){
//这里Person类还没有初始化,就利用objc_msgSend调用initialize方法
objc_msgSend([Person class], @selector(initialize));
//变更Person类是否初始化的状态
personinitialized = YES;
}
//利用objc_msgSend调用Student的initialize方法
objc_msgSend([Student class], @selector(initialize));
//变更Student是否初始化的状态
studentInitialized = YES
}

[Teacher alloc];

//判断Teacher类是否已经初始化了,这里Teacher类还没有初始化,进入条件语句
if(!teacherInitialized){
//判断其父类Person类是否初始化了,这里父类已经初始化了,所以不会进入这个条件语句
if(!personinitialized){

objc_msgSend([Person class], @selector(initialize));
personinitialized = YES;
}
//利用objc_msgSend调用Teacher类的initialize方法
objc_msgSend([Teacher class], @selector(initialize));
//变更状态
teacherInitialized = YES;
}

上面列出来的是调用initialize的伪代码,下面再详细说明这个过程:

1.Student类收到alloc消息,开始着手准备调用initialize方法。首先判断自己有没有初始化过。

2.判断自己没有初始化过,所以就去找自己的父类Person类,看Person类有没有初始化过,发现Person类也没有初始化过,且Person类也没有父类,所以对Person类使用objc_msgSend([Person class], @selector(initialize))调用Person类的initialize方法。这是第一次调用Person类的initialize方法。

3.父类处理完后,再通过objc_msgSend([Student class], @selector(initialize));调用Student类的initialize方法,但是由于Student类没有实现initialize方法,所以通过其superclass指针找到父类Person类,然后调用了Person类的initialize实现。这是第二次调用Person类的initialize方法。

4.Teacher类收到alloc方法,开始准备调用initialize方法。首先判断自己有没有被初始化过。

5.判断自己没有被初始化过后,又开始判断其父类Person类有没有被初始化过,刚刚父类Person类已经被初始化过。

6.于是通过objc_msgSend([Teacher class], @selector(initialize))调用Teacher类的initialize方法。但是由于Teacher类没有实现initialize方法,所以只能通过superclass指针去查找父类有没有实现initialize方法,发现父类Person类实现了initialize方法,于是调用父类的initialize方法。这是第三次调用Person类的initialize方法。

iOS中正则表达式的用法

发表于 2018-06-26

1.判断字符串是否符合正则

//------  NSPredicate(谓词)匹配  -----
NSString *email = @"15173265865";
NSString *regex = @"1[358][0-9]{9}" ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
NSLog(@"%d",[predicate evaluateWithObject:email]);//1,如果正则{9}改成{1},打印结果为0

2.查找满足条件的子字符串

2.1 NSString的API:rangeOfString:option:查找字符串中满足正则的子字符串

NSString *searchText = @"15173265132";
NSString *regex1 = @"1[358][0-9]{1}" ;
NSRange range = [searchText rangeOfString:regex1 options:NSRegularExpressionSearch];
if (range.location != NSNotFound) {
    NSLog(@"%@", [searchText substringWithRange:range]); //151,会返回第一个匹配结果的位置
}

2.2 NSRegularExpressionSearch 替换匹配的子字符串

NSString *replaceString = [searchText stringByReplacingOccurrencesOfString:regex1 withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, searchText.length)];
NSLog(@"--- :%@",replaceString); //73265

2.3 enumerateMatchesInString block的形式,可以得到字符串然后做一些操作

NSString *searchText = @"15173261865/18551510506";
NSString *regexStr = @"1[358][0-9]{1}";
NSError *error;
NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:&error];
if (error) return;
NSInteger count = [regular numberOfMatchesInString:searchText options:NSMatchingReportCompletion range:NSMakeRange(0, searchText.length)];
NSLog(@"%d",count); //4

//也可以用block
[regular enumerateMatchesInString:searchText options:NSMatchingReportCompletion range:NSMakeRange(0, searchText.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
    NSRange matchRange = result.range;
    NSLog(@"range:%@",NSStringFromRange(matchRange));
    NSString *str = [searchText substringWithRange:matchRange];
    NSLog(@"%@",str);
}];
/*上面的打印结果:
2018-06-26 21:19:31.818 DataBaseDemo[18967:563419] range:{0, 3}
2018-06-26 21:19:31.818 DataBaseDemo[18967:563419] 151
2018-06-26 21:19:31.818 DataBaseDemo[18967:563419] range:{7, 3}
2018-06-26 21:19:31.818 DataBaseDemo[18967:563419] 186
2018-06-26 21:19:31.818 DataBaseDemo[18967:563419] range:{12, 3}
2018-06-26 21:19:31.818 DataBaseDemo[18967:563419] 185
2018-06-26 21:19:31.819 DataBaseDemo[18967:563419] range:{16, 3}
2018-06-26 21:19:31.819 DataBaseDemo[18967:563419] 151
2018-06-26 21:19:31.819 DataBaseDemo[18967:563419] range:{0, 0}
*/ 

2.4 matchesInString 直接得到数组

NSTextCheckingResult *match = [regular firstMatchInString:searchText options:0 range:NSMakeRange(0, [searchText length])];
  NSLog(@"first-result.range = %@",NSStringFromRange(match.range)); //first-result.range = {0, 3}
  NSArray<NSTextCheckingResult *> *resultsSign = [regular matchesInString:string options:0 range:NSMakeRange(0, string.length)];

3. NSDataDetector 这个类是NSRegularExpression的子类,对常用的正则匹配做了封装

 NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeAddress | NSTextCheckingTypePhoneNumber | NSTextCheckingTypeLink
                                                           error:nil];
//需要检测的字符串
NSString *testStr = @"有一个网址:wwww.JohnnyLiu.com有 一个电话:15310547654 还有一个地址:北京市大屯街道 你看看这个www.baidu.com/sabc/bnss怎 么样?";
[detector enumerateMatchesInString:testStr options:NSMatchingReportCompletion range:NSMakeRange(0, testStr.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
    NSLog(@"result.range = %@",NSStringFromRange(result.range));
    if (result.URL) {
        NSLog(@"url = %@",result.URL);
    }
    if (result.phoneNumber) {
        NSLog(@"phone = %@",result.phoneNumber);
    }
    if ([result resultType] == NSTextCheckingTypeAddress) {

        NSDictionary<NSString *, NSString *> * addressComponent = [result addressComponents];
        NSLog(@"城市:%@, 街道:%@", addressComponent[NSTextCheckingCityKey], addressComponent[NSTextCheckingStreetKey]);
    }
}];

/*
 打印结果: //不知道为什么,地址没有匹配出来地址
 2018-06-26 21:56:22.297 DataBaseDemo[20712:614322] url = http://wwww.JohnnyLiu.com
 2018-06-26 21:56:26.319 DataBaseDemo[20712:614322] result.range = {28, 14}
 2018-06-26 21:56:26.319 DataBaseDemo[20712:614322] phone = 电话:15310547654
 2018-06-26 21:56:26.323 DataBaseDemo[20712:614322] result.range = {63, 24}
 2018-06-26 21:56:26.323 DataBaseDemo[20712:614322] url = http://www.baidu.com/sabc/bnss%E6%80%8E
 2018-06-26 21:56:26.323 DataBaseDemo[20712:614322] result.range = {0, 0}
 */

人类简史

发表于 2018-06-16

###读完这本书,有什么感受?

       这本书特别宏观,从大约135亿年前宇宙大爆炸讲起,历数人类发展的历史,最后延伸到未来。
真的可以说是一本奇书。作者的历史知识非常丰富,某一个历史时期,地球上某个地方在发生着什么历史事件可能对作者来说都是信手拈来。最后看完的时候我又读了一下序,这里说该书的中文版参照的英文版是作者特地为中国读者“量身定做”的,其他国家的版本也是如此,很细心,👍

类人猿记录片中说我们与大猿的基因几乎完全相同,我们跟大猩猩有97.7%的相同DNA。

笔记摘抄

每当人类整体的能力大幅增加,看来似乎大获成功,个人的苦痛也总是随之增长。

物种演化上的成功并不代表个体的幸福。

正是这些征收来的多余粮食哦,养活了政治、战争、艺术和哲学,建起了宫殿、堡垒、纪念碑和庙宇。在现代晚期之前,总人口有九成以上都是农民,日出而作、胼手胝足。他们生产出来的多余食粮养活了一小撮的精英分子:国王、官员、战士、牧师、艺术家和思想家,但历史写的几乎全是这些人的故事。于是,历史只告诉了我们极少数人在做什么,而其他绝大多数的人的生活就是不停挑水耕田。

有三大原因,让人类不会发现组织自己生活的种种秩序其实是想象:
1.想象构建的秩序深深与真实的世界结合。
2.想象构建的秩序塑造了我们的欲望。
比如:旅游,之前的人喜欢建金字塔
3.想象建构的秩序存在于人和人之间思想的连接。

文化会不断改变。

平等和自由就是矛盾的

历史文化的大方向就是分久必合

自动打包

发表于 2018-03-26

一:接入流程:

1.修改工程名

将xxx替换成自己的工程名
project_name=xxx

scheme名 将xxx替换成自己的sheme名
scheme_name=xxx

2.配置文件

在下面的路径下(或者自己换个路径也可)换成自己项目的配置文件

#plist文件所在路径
exportOptionsPlistPath=${HOME}/"Desktop/ieltsBuild/exportTest.plist"

这个配置文件其实在我们手动打包的目录下是有的,我们copy到shell脚本里写的目录下就可以了。

3.通知测试

https://oapi.dingtalk.com/robot/send?a…需要改成为自己创建的机器人url.

“atMobiles”:[“185xxxxxxxx”] 里的手机号改成需要通知的人的手机号

二:执行脚本

脚本放到工程目录下,
cd 到工程目录下,执行sh autoBuild.sh.

三:脚本


#使用方法


#工程绝对路径
project_path=$(cd `dirname $0`; pwd)

#echo $project_path
#工程名 将XXX替换成自己的工程名
project_name=xxx

#scheme名 将XXX替换成自己的sheme名
scheme_name=xxx

#打包模式 Debug/Release
development_mode=Debug

#build文件夹路径
build_path=${HOME}/"Desktop/ieltsBuild/build"
#${project_path}/build

#plist文件所在路径
exportOptionsPlistPath=${HOME}/"Desktop/ieltsBuild/exportTest.plist"
#${project_path}/exportTest.plist
#导出.ipa文件所在路径
exportIpaPath=${HOME}/"Desktop/ieltsBuild/${development_mode}"
#${project_path}/ipaDir/${development_mode}



#注释掉,默认是开发模式
#echo "Place enter the number you want to export ? [ 1:app-store 2:ad-hoc] "

##
#read number
#while([[ $number != 1 ]] && [[ $number != 2 ]])
#do
#echo "Error! Should enter 1 or 2"
#echo "Place enter the number you want to export ? [ 1:app-store 2:ad-hoc] "
#read number
#done

#if [ $number == 1 ];then
#development_mode=Release
#exportOptionsPlistPath=${project_path}/exportAppstore.plist
#else
#development_mode=Debug
#exportOptionsPlistPath=${project_path}/exportTest.plist
#fi


echo '///-----------'
echo '/// 正在清理工程'
echo '///-----------'
xcodebuild \
clean -configuration ${development_mode} -quiet  || exit


echo '///--------'
echo '/// 清理完成'
echo '///--------'
echo ''

echo '///-----------'
echo '/// 正在编译工程:'${development_mode}
echo '///-----------'
xcodebuild \
archive -workspace ${project_path}/${project_name}.xcworkspace \
-scheme ${scheme_name} \
-configuration ${development_mode} \
-archivePath ${build_path}/${project_name}.xcarchive  -quiet  || exit

echo '///--------'
echo '/// 编译完成'
echo '///--------'
echo ''

echo '///----------'
echo '/// 开始ipa打包'
echo '///----------'

xcodebuild -exportArchive -archivePath ${build_path}/${project_name}.xcarchive \
-configuration ${development_mode} \
-exportPath ${exportIpaPath} \
-exportOptionsPlist ${exportOptionsPlistPath}
#-quiet || exit

if [ -e $exportIpaPath/$scheme_name.ipa ]; then
echo '///----------'
echo '/// ipa包已导出'
echo '///----------'
#open $exportIpaPath
else
echo '///-------------'
echo '/// ipa包导出失败 '
echo '///-------------'
fi
echo '///------------'
echo '/// 打包ipa完成  '
echo '///-----------='
echo ''

echo '///-------------'
echo '/// 开始发布ipa包 '
echo '///-------------'

#if [ $number == 1 ];then
#
##验证并上传到App Store
## 将-u 后面的XXX替换成自己的AppleID的账号,-p后面的XXX替换成自己的密码
#altoolPath="/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool"
#"$altoolPath" --validate-app -f ${exportIpaPath}/${scheme_name}.ipa -u XXX -p XXX -t ios --output-format xml
#"$altoolPath" --upload-app -f ${exportIpaPath}/${scheme_name}.ipa -u  XXX -p XXX -t ios --output-format xml
#else

echo '///-------------'
echo '/// 传到蒲公英 '
echo '///-------------'

# 将XXX替换成自己的Fir平台的token
#自动上传到蒲公英
curl -F "file=@$exportIpaPath/$scheme_name.ipa" \
-F "uKey=xxxx" \
-F "_api_key=xxxx" \
https://www.pgyer.com/apiv1/app/upload

echo '///-------------'
echo '/// 通过钉钉机器人通知测试 '
echo '///-------------'

//其实就是一个post请求
curl -X POST -H 'Content-type':'application/json' -d '{"msgtype":"text","text":{"content":"新包打好啦 哒哒哒 "},"at":{"atMobiles":["185xxxxx"],"isAtAll":false}}' https://oapi.dingtalk.com/robot/send?xxxxxx
echo '///-------------'
echo '/// 完成 '
echo '///-------------'


#fi  #Shell脚本没有{}括号,所以用fi表示if语句块的结束

exit 0



参考文档:

1.iOS- 一键自动打包发布到Fir和AppStore: https://www.jianshu.com/p/05dc9f925467
2.钉钉自定义机器人文档:
https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.karFPe&treeId=257&articleId=105735&docType=1
3.蒲公英快速上传文档:https://www.pgyer.com/doc/view/upload_one_command

python思考

发表于 2017-09-28

廖雪峰学习python的官网上有这样一段话:

#前言
“
和list比较,dict有以下几个特点:

查找和插入的速度极快,不会随着key的增加而变慢;
需要占用大量的内存,内存浪费多。
而list相反:

查找和插入的时间随着元素的增加而增加;
占用空间小,浪费内存很少。
所以,dict是用空间来换取时间的一种方法。

dict可以用在需要高速查找的很多地方,在Python代码中几乎无处不在,正确使用dict非常重要,需要牢记的第一条就是dict的key必须是不可变对象。
”
上面说字典要占用大量的内存,我就去查了一下python中字典的实现方式:

http://liuzhijun.iteye.com/blog/2266358

发现讲python字典实现的一篇好文章

python见闻志

总结一下,
dict{key:value}
key通过hash函数,得到一个数(十进制),跟存储数组的长度 - 1 进行&操作,得到slot索引。
如:hash(‘a’) = 398026104856367068
如果数组的大小是8
hash(‘a’) & 7 = 0;

hash.png

图片引用自:https://harveyqing.gitbooks.io/python-read-and-write/content/

如果索引重复,就是用开放寻址法来查找空闲的slot,所以会形成一个探测链,当探测链中某个元素被删除时,不能直接删除,否则探测链就断了,就不能根据探测链来查找删除元素后面的元素了。 删除时通多dummy状态来标志。有三种状态,unused,active,dummy

Q:像这种探测链确定位置的元素怎么查找到呢?

先根据hash值来查找,找到一个元素,比较key值,不一样,然后根据探测链往下查找,直到比较key值是一样的,就找到了。

如果使用了的slots和dummy slots的总量超过了数组大小的2/3则重新调整字典的大小,然后slot索引会重新计算一遍。

isEqual和hash

发表于 2017-09-28

记录一下今天下午对这两个方法的学习。

基础类中的实现

在基类中的实现中,相等性检查本质上就是对本体性的检查,两个NSObject如果指向了同一个内存地址,那它们就被认为是相同的。

- (BOOL)isEqual:(id)object {
      return self == object;
} 

Person的父类是NSObject,创建Person类

- (NSUInteger)hash {
        return [super hash];
}

Person *person = [[Person alloc] init];
NSLog(@"person = %ld", (NSUInteger)person);
NSLog(@"[person1 getSuperHash] = %ld", [person     getSuperHash]);

打印结果如下:

person = 140643147498880
[person1 getSuperHash] = 140643147498880

可以看出 NSObject 的hash返回的就是该对象的内存地址。
自定义类中重写这两个方法的示例可以参考链接:
iOS开发 之 不要告诉我你真的懂isEqual与hash!

相等性判断

在 Foundation 框架中,下面这些 NSObject 的子类都有自己的相等性检查实现,分别使用下面这些方法:

NSAttributedString -isEqualToAttributedString:

NSData -isEqualToData:

NSDate -isEqualToDate:

NSDictionary -isEqualToDictionary:

NSHashTable -isEqualToHashTable:

NSIndexSet -isEqualToIndexSet:

NSNumber -isEqualToNumber:

NSOrderedSet -isEqualToOrderedSet:

NSSet -isEqualToSet:

NSString -isEqualToString:

NSTimeZone -isEqualToTimeZone:

NSValue -isEqualToValue:

对上面这些类来说,当需要对它们的两个实例进行比较时,推荐使用这些高层方法而不是直接使用 isEqual:。

hash的概念

hash table(也叫散列表)的设计就是为了加快查找速度。
它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
详细介绍:
哈希表(散列表)原理详解
NSHipster建议重写hash的时候,使用
关键属性的散列值进行一个简单的 XOR操作,可以满足99%的需求了。
Equality

重构-改善既有代码的设计

发表于 2017-08-30

前言

文章是对书:<重构 改善既有代码的设计>的整理。

重构的好处

重构之后软件功能和之前一样,不同的是性能优化了,执行速度快了或者代码易于理解等。
1.重构改进软件设计
2.重构使使软件容易理解
3.重构帮助找到bug
4.重构提高编程速度

何时重构

就是下面要写的代码的坏味道,这只是一些经验,具体什么时候要重构,要看一个见识广博者的直觉了。

代码的坏味道

1.重复代码

解决: 重复代码可以提取出一个方法,放到要被调用的类中,或者提炼出一个独立类中。
example:
//判断字符串是否为空

(BOOL)stringIsNull:(NSString *)checkString
{
if ((NSNull *)aCheckString == [NSNull null] ||
aCheckString.length == 0 ||
[aCheckString isEqualToString:@""] ||
[aCheckString isEqualToString:@"<null>"])
return YES;
else
return NO;
}

2.过长的方法

拥有短函数的对象会获得比较好、比较长。“间接层”所能带来的全部利益-解释能力、共享能力、选择能力都是由小型函数支持的。
文中说,我们可以遵循这样一个原则:每当感觉需要注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现方法)命名
(我觉得可以考虑一下,并不绝对)
解决: 分解成若干短函数

3.过大的类

类内如果有太多代码,也是代码重复、混乱并最终走向死亡的源头。
解决:可以试着提炼方法,提炼工具类,把数据和UI分开。

4.过长的参数列

解决:太长的参数列会变得难以理解,可以封装成一个对象。

5.Divergent Change(发散式变化)

标题的意思是:某个类经常因为不同的原因在不同的方向上发生变化。
针对某一外界变化的所有相应修改,都只应该发生在单一类中,而这个类中的所有内容都应该反应此变化。
解决:可以找出某特定原因造成的所有变化,把他们提炼到另一个类中。

6.Shotgun Surgery(霰弹式修改)

标题的意思是:每遇到某种变化,你都必须在许多不同的类中做出许多小修改。
解决: 把所需要修改的代码放到同一个类
发散式变化是指“一个类受多种变化的影响”,霰弹式变化是指“一种变化引发多个类相应修改”。这两种情况下你都会希望整理代码,使外界变化与“需要修改的类”趋于一一对应。

7.依恋情结

如果我们看到某个函数为了计算某个值,从另一个对象那儿调用了几乎半打的取值函数。
解决: 把这个函数移至另一个地点。
如果一个函数用到了好几个类的功能。
解决:我们的原则是:判断哪个类拥有最多被此函数使用的数据,然后就把这个函数和那些数据摆在一起。或者将这个函数分解为数个小函数并分别置于不同地点。
最根本的原则:数据和引用这些数据的行为总是一起变化的,但也有例外。如果例外出现,我们就搬移那些行为,保持变化只在一地发生。

8.Data clumps(数据泥团)

如果两个类中有一些相同的字段,如果删掉其中一项,其他字段不再有意义。你应该为它们创建一个新对象。

9.基本类型偏执
大多数编程华景都有两种数据:结构类型允许你将数据组织成有意义的形式,基本类型则是构成类型的积木块。

意思是:我们不应该偏执的只使用一些基本类型,我们应该试着创建一些小对象— 像是结合数值和币种的money类、由一个起始值和一个结束值组成的range类。

10.Switch Statements(switch惊悚现身)

面向对象的一个最明显的特征就是:少用switch(或case)语句。
从本质上说,switch语句的问题在于重复。
解决 用多态来替换它。如果你只是在单一函数中有些选择事例,且不想改变它们,那多态就有点杀鸡用牛刀了。这种情况可以为参数的每个值,建立一个独立函数。

11.平行继承体系

每当你为某个类增加一个子类,必须也为另一个类相应增加一个子类。

12.冗赘类

每一个类都要去理解它,维护它。如果重构使它身形缩水,不再做那么多工作。或者这个类只是来应付未来的变化,而变化并没有发生。
解决: 将类内联化,或者直接删除掉。

13.夸夸其谈未来性

当有人说“噢,我们总有一天需要做这事” 并企图以各式各样的钩子和特殊情况来处理一些非必要的事情,这种坏味道就出现了。 这会造成代码更难理解和维护。
如:某个抽象类其实并没有太大作用,如果函数的某些参数未用上。

14.另人迷惑的暂时字段。

对象中某个实例变量仅为某种特定情况而设。这样的代码让人不易理解。因为你通常认为对象在所有时候都需要它的所有变量。在变量未被使用的情况下猜测当初其设置目的,会让你发疯的。

Hello World

发表于 2017-08-30

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

mhen

mhen

9 日志
3 标签
© 2018 mhen
由 Hexo 强力驱动
主题 - NexT.Muse