如何删除 Xcode 的描述文件
删除路径:
/Users/电脑名/Library/MobileDevice/Provisioning Profiles
// Xcode 16
~/Library/Developer/Xcode/UserData/Provisioning\ Profiles
或者在该路径下找到对应的删除之.
删除路径:
/Users/电脑名/Library/MobileDevice/Provisioning Profiles
// Xcode 16
~/Library/Developer/Xcode/UserData/Provisioning\ Profiles
或者在该路径下找到对应的删除之.
如何查看打出来的 .ipa
包是否包含某个 UDID
?
security cms -D -i embedded.mobileprovision
ProvisionedDevices
对应的数组当然,你把 .ipa 包上传到蒲公英后,也是可以看到的^_^
PS : 如果 .ipa
包里不包含 某个 UDID
,则需要通过开发者账号在 Device 里添加 一个“UDID”,或者通过 Xcode 注册一个设备,然后删除 Xcode 的 Provisioning Profiles
目录下的对应文件,重启 Xcode并进行打包。
命令:
rm -rf ~/Library/MobileDevice/Provisioning\ Profiles/*
在《破解Revealapp的试用时间限制》这篇blog里,笔者在提供补丁之前,曾经提供了一种通过修改plist中的安装时间来延长试用的方法。这个方法对很多留言的同学来说不起作用,当初笔者也没有深究,就继而提供了补丁来直接修改可执行文件。不过这些同学的修改plist不起作用的这个问题却一直留在笔者心中。直到最近,看到一篇文章(参考资料1),才恍然大悟。
原来在OSX的10.9版本之前,plist的读写都是APP自己来进行的,但升级到10.9的时候,有一个专门的精灵进程来负责plist文件的读写,那就是cfprefsd。
如上图所示,所有的plist读写都是通过进程cfprefsd来进行的,也就是说cfprefsd会对plist数据信息缓存。这就是为什么即使你修改了plist,APP读到的仍然是以前的数据。
要解决这个问题可以用如下步骤:
参考资料:
Revealapp作为分析iOS app UI结构的利器,还是非常称手的,89刀的价格也是物有所值。本文分析其试用版时间限制,只是用于学习,如果一直用,还是买个licence支持一下吧。
试用版有30天的时间限制,既然是30天时间限制,肯定每次启动是要读当前时间的啰。所以最简单的hack方法就是修改系统时间。如果这种方法可以接受,就不用往下看了。
如果你的工作严重依赖于Calendar,那么修改系统时间的方法就是不可以接受的。下面的追踪过程包含了对双精度浮点数在内存中的表示、ObjC对象模型等问题的讨论,如果不感兴趣可直接跳到文末查看最终的解决方案。
用dtruss看了下启动时调用的syscall,是没有网络通讯的,说明app的安装时间不可能是从网络读下来的,那么这个时间肯定是写在本地的文件系统。
用opensnoop看了下启动时Reveal读过的所有文件,没有值得注意的地方。最后的发现证实这个思路忽略了一个问题,一个app读的文件并不一定是它自己打开的,可以是进程间通信。
这些简单的尝试失败后,就只能老老实实的分析代码了。
试用版的Reveal有提醒试用剩余时间的信息在窗口的右上角”Free trial ends in xx days”(我觉得这不是一个好的设计,这句话似乎时刻挑衅着使用者:“来呀,你来hack我呀”)。“trial”是我感兴趣的关键字,除了在数据段肯定能找到这个关键字以外,说不定在ObjC的运行时类型系统中还能有意外的收获。果真,Reveal没有对类型信息进行模糊处理,在class-dump生成的头文件中发现了:
-[IBARegistrationPreferencesViewController messageForTrialDaysRemaining:(long long)arg1]
从函数名来看它应该就是生成试用剩余时间字符串的。
上GDB,单步跟踪,
0x000000010008bd34 push rbp
0x000000010008bd3f move rbx, rdx ; rdx 就是还剩下的试用天数,也就是函数的参数arg1
以此为突破口,发现下面的小段代码。
0x00000001000872cd call 0x100086ec2
0x00000001000872d2 mov rcx, rax ; rax中是上面函数返回的已过去的天数
0x00000001000872d7 mov eax, 0x1e ; 0x1e=30 30天的限制
0x00000001000872dc sub rax, rcx ; 30减去已经过去的天数的,减出来就是还剩下的天数
再往下走,需要分析的数据不再像是“天数”这样的整数,而是像软件安装日期NSDate这样的对象,特征不明显。所以就有必要清楚NSDate这个对象中日期的表示方法。
NSDate对象应该有两个域,第一个“isA”是所有ObjC对象都有的类型指针,指向NSDate类型对象。第二个是个双精度浮点数,表示从2001年1月1日到现在的时间间隔,单位是秒。
pointer: isA
double: _timeIntervalSinceReferenceDate
其实isA指针就是NSDate对象的特征,所有的NSDate对象都是以相同的8个字节开始。第二个域是一个浮点数,分两步把它转换为一个日期。
第一步,十六进制浮点数转换为十进制
双精度浮点数由8个字节构成,1个bit表示符号,11个bit表示指数,剩下的52位用来表示底数。
使用python可以方便的把8字节的十六进制浮点数转换为十进制数:
struct.unpack('<d','c3b72c7a9ebfb841'.decode('hex'))[0]
在gdb中,可以直接使用命令
p (double)(NSDate指针地址+8)
第二步,秒数转换为日期
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:415285808.20822901];
NSLog(@"\n%@", date);
使用上面的方法,可以在跟踪汇编代码的时候检查内存中的NSDate对象,以及它所表示的日期。(这需要点耐心)
最终,安装Reveal的时间第一次出现在内存中的位置被找到,这个位置所在的函数显然负责把存在文件某处的一个magic number转换为软件安装日期。
但是意料之外的是,这个想像中的magic number并不magic,它仅仅是存在user default的plist文件中的一项,而且就是安装日期的双精度浮点数的十六进制表示。
所以,要想永久试用Reveal,只需要打开
~/Library/Preferences/com.ittybittyapps.Reveal.plist
把IBAApplicationPersistenceData这一项删除就是了。
有同学留言说上面的方法不起作用(问题的原因请参考另一篇blog谁动了我的plist),于是叽歪刘写了个补丁。
补丁是用10.9的SDK编译的,在Reveal1.0.3(2287)上测试通过。
下载解压后,用右键的“打开”菜单运行程序。亲,叽歪刘只能帮你到这里了。
文章转载自:破解 Revealapp 的试用时间限制
对代码添加如下样式:
article code {
white-space: pre-wrap;
word-break: break-all;
}
今天测试给过来一张图片(后缀是.png)说无法在 APP 的 WebView 里面无法显示,而且在 Safari 里也是无法打开的,但在谷歌浏览器上是可以正常显示。起初是知道 WebP 格式的图片苹果是不支持显示的,但这个图片的后缀是.png 的,难道还有 png 的图片是苹果不支持的么?
根据个人经验,是没有听说苹果不支持 png 格式的图片的,这时想到以前自己更改 JPG 图片后缀的事情,是不是这张图片也是经过别人手动改后缀的呢,带着这个疑问,我决定手动判断这张图片的原始格式。
记得以前在看 SDWebImage 源码时,源码中是有关于判断根据 image 来判断图片的实际格式的,于是从 SDWebImage 中的源码中抠出来判断图片实际格式的代码。
图片的前8位是存储图片格式的,可以通过先读取图片的数据,拿到图片的前8位来判断图片的类型
/**
*
* 根据图片数据获取图片的原始类型
*
* @param data 图片的二进制数据
* @return 图片的实际格式
*/
- (NSString *)typeForImageData:(NSData *)data {
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return @"image/jpeg";
case 0x89:
return @"image/png";
case 0x47:
return @"image/gif";
case 0x49:
case 0x4D:
return @"image/tiff";
case 0x52: {
//R as RIFF for WEBP
if (data.length < 12) {
return nil;
}
NSString *identifierTypeStr = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([identifierTypeStr hasPrefix:@"RIFF"] && [identifierTypeStr hasSuffix:@"WEBP"]) {
return @"image/webp";
}
return nil;
}
default:
break;
}
return nil;
}
That's All.
原文链接:iOS 获取图片的原始格式
今天在重新整理希尔排序
的时候,使用了 100000 个种子数据进行测试,但发现在排序好后,用 NSLog 无法打印完全排序结果,开始以为自己的排序算法写法有误,但查看内存数据信息,显示排序结果正常。这时怀疑 NSLog 在 XCode 8.0 下可能有 Bug。 于是使用 c 语言函数 printf
进行打印。
Bingo
!代码如下:
NSMutableArray<NSNumber *> *results = [dataList mutableCopy];
printf("%s", [results.description UTF8String]);
附(NSString 与 char 相互转换):
//NSString转换char
NSString * str1= @"Test";
const char * c1 =[str1 UTF8String];
//char转换NSString
const char * c2 ="test";
NSString *str2 = [NSString stringWithUTF8String:c2];
Ubuntu 16.0.4 安装 Swift 后提示: error while loading shared libraries: libpython2.7.so.1.0:
swift/usr/bin/lldb: error while loading shared libraries: libpython2.7.so.1.0: cannot open shared object file: No such file or directory
这个问题会在 Ubuntu 14.04 和 Ubuntu 16.04 上出现,是 swift 的一个依赖问题,只需要安装 libpython2.7-dev
就可以解决问题。代码如下
sudo apt-get install libpython2.7-dev
Link : Incomplete install instructions for Ubuntu
原文链接: Ubuntu 16.0.4 安装 Swift 后提示 error while loading shared libraries: libpython2.7.so.1.0
在 iOS 开发中,我们使用系统的枚举定义的时候,经常可以看到位枚举
:
typedef NS_OPTIONS(NSUInteger, UIControlState) {
UIControlStateNormal = 0,
UIControlStateHighlighted = 1 << 0, // used when UIControl isHighlighted is set
UIControlStateDisabled = 1 << 1,
UIControlStateSelected = 1 << 2, // flag usable by app (see below)
UIControlStateFocused NS_ENUM_AVAILABLE_IOS(9_0) = 1 << 3, // Applicable only when the screen supports focus
UIControlStateApplication = 0x00FF0000, // additional flags available for application use
UIControlStateReserved = 0xFF000000 // flags reserved for internal framework use
};
需要掌握位枚举,我们需要先了解位运算
和 位移
:
位运算有两种:位或(|)
和 位与(&)
位或(|)
:两个位
进行或(|)
运算。运算规则:两个运算的位
只要有一个为1
则运算结果为1
,否则为0
如:
0000 0001 | 0000 0010 结果为:0000 0011
0000 0000 | 0000 0000 结果为:0000 0000
位与(&)
:两个位
进行与(&)
运算。运算规则:两个运算的位
都为1
则运算结果为1
,否则为0
如:
0000 0001 & 0000 0001 结果为:0000 0001
0000 0001 & 0000 0010 结果为:0000 0000
位移包含两种:左移(<<)
和 右移(>>)
<<
:将一个数的二进制位
向左移动 n 位,高位丢弃,低位补 0
。如将数字1(0000 0001)左移两位得到结果为:4(0000 0100)。表述为:1 << 2。
左移就是将一个数乘以 2 的 n 次方。
>>
:将一个数的二进制位
向右移动 n 位,低位丢弃,高位补 0
。如将数字4(0000 0100)右移两位得到结果为:1(0000 0001)。表述为:4 >> 2。
右移就是将一个数除以 2 的 n 次方。
我们有如下定义:
typedef NS_ENUM(NSUInteger, HJDirection) {
// 0000 0001
HJDirectionLeft = 1 << 0,
// 0000 0010
HJDirectionRight = 1 << 1,
// 0000 0100
HJDirectionTop = 1 << 2,
// 0000 1000
HJDirectionBottom = 1 << 3
};
PS:定义一个位枚举时,我们通常以一个数字作为基准,如数字1
,然后对该数字进行左移(右移),这样我们才能在后面使用中判断是否包含某个枚举值。
使用:
//获取所有方向(位或运算)
//0000 1111
HJDirection directionAll = HJDirectionLeft | HJDirectionRight | HJDirectionTop | HJDirectionBottom;
//获取是否包含某个方向(位与运算)
if ((directionAll & HJDirectionLeft) == HJDirectionLeft) {
//0000 0001
NSLog(@"满足条件:左方向");
}
if ((directionAll & HJDirectionRight) == HJDirectionRight) {
//0000 0010
NSLog(@"满足条件:右方向");
}
if ((directionAll & HJDirectionTop) == HJDirectionTop) {
//0000 0100
NSLog(@"满足条件:上方向");
}
if ((directionAll & HJDirectionBottom) == HJDirectionBottom) {
//0000 1000
NSLog(@"满足条件:下方向");
}
我们回到开始的 UIControlState
枚举定义
我们在定义 UIButton
状态时,一般会用到 UIControlStateNormal
、UIControlStateHighlighted
、UIControlStateSelected
,但我们怎么去定义一个选中状态下的高亮呢?如果我们单纯的使用 UIControlStateHighlighted
, 我们得到的只是默认状态下的高亮显示。这时我们就需要用到位枚举的神奇之处了。
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:@"点击我" forState:UIControlStateNormal];
//定义普通状态文字颜色
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
//定义选中状态文字颜色
[button setTitleColor:[UIColor blueColor] forState:UIControlStateSelected];
//定义普通状态高亮文字颜色
[button setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
//定义选中状态高亮文字颜色
[button setTitleColor:[UIColor orangeColor] forState:UIControlStateSelected | UIControlStateHighlighted];
Done !
Link:iOS 位枚举
系统环境:
Ubuntu 16.04.3 LTS
IP : 192.168.1.110
apt-get update
apt-get install bind9
打开打开/etc/bind/named.conf.options
,修改后如下:
acl goodclients {
192.168.1.0/24;
localhost;
localnets;
};
options {
directory "/var/cache/bind";
// If there is a firewall between you and nameservers you want
// to talk to, you may need to fix the firewall to allow multiple
// ports to talk. See http://www.kb.cert.org/vuls/id/800113
// If your ISP provided one or more IP addresses for stable
// nameservers, you probably want to use them as forwarders.
// Uncomment the following block, and insert the addresses replacing
// the all-0's placeholder.
// forwarders {
// 0.0.0.0;
// };
//========================================================================
// If BIND logs error messages about the root key being expired,
// you will need to update your keys. See https://www.isc.org/bind-keys
//========================================================================
dnssec-validation auto;
auth-nxdomain no; # conform to RFC1035
listen-on-v6 { any; };
//added
listen-on { 192.168.1.110;};
recursion yes ;
allow-query { goodclients;};
allow-query-cache { any; }; # 很重要,不然无法解析外网
allow-transfer { none; }; # disable zone transfers by default
forwarders {
223.5.5.5; # alidns
223.6.6.6; # alidns
202.96.199.133; #上海电信DNS
202.96.0.133; #上海电信DNS
114.114.114.114; # 114 现在只配置alidns,不能解析国内域名
114.114.115.115; # 114 现在只配置alidns,不能解析国内域名
8.8.8.8; # Google Google可以解析国内和国外域名
8.8.4.4; # Google Google可以解析国内和国外域名
};
forward only ;
};
named.conf.local
文件默认是空的。本文在配置文件中分别定义一条正向解析
和一条反向解析
。配置文件修改后类似如下:
//domain->ip
zone "local.com" in {
type master;
file "/var/cache/bind/db.local.com";
};
//domain->ip(for anthor domain)
//zone "local1.com" in {
// type master;
// file "/var/cache/bind/db1.local.com";
//};
//ip->domain
zone "1.168.192.in-addr.arpa" in {
type master;
file "/var/cache/bind/db.1.168.192";
};
配置文件定义后类似如下,分别是正向和反向两个解析记录。按照自己需求修改相应的区域和区域解析记录,IP等信息。
正向记录
sudo vim /var/cache/bind/db.local.com
$TTL 604800
@ IN SOA local.com. root.local.com. (
2 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604000) ; Negative Cache TTL
;
; name servers
@ IN NS ns.local.com.
@ IN A 192.168.1.110
;ns records
ns IN A 192.168.1.110
;host records
www IN A 192.168.1.110
api IN A 192.168.1.100
反向记录
sudo vim /var/cache/bind/db.1.168.192
$TTL 604800
@ IN SOA local.com. root.local.com. (
2 ; Serial Number
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
86400 ); ; Minimum
@ IN NS local.com.
66 IN PTR www.local.com.
66 IN PTR api.local.com.
hejun@ubuntu:/var/cache/bind$named-checkconf
hejun@ubuntu:/var/cache/bind$named-checkzone local.com /var/cache/bind/db.local.com
zone local.com/IN: loaded serial 2
OK
hejun@ubuntu:/var/cache/bind$named-checkzone db.1.168.192 /var/cache/bind/db.1.168.192
zone db.1.168.192/IN: loaded serial 2
OK
分别检查了语法和区域配置文件,没有报错。重启bind服务。
sudo service bind9 restart
到这里DNS服务器的配置就完成了,可以使用dig命令
测试。
在路由器里设置首要DNS
为 192.168.1.110
,这样我们就可以在同一个内网
下访问:www.local.com
就会指向到 192.168.1.110
,访问:api.local.com
就会指向到 192.168.1.100