技术文章

Dash4许可证获取方法 · May 23, 2017 · 技术文章

之前买过一次Dash 2,当时以为可以一直用下去,结果有一天,Dash从2升级到了3,然后我之前的序列号就不能用了!从此之后,我变成了Dash黑,一直用破解。

但是每次找破解比较麻烦,所以记录下来。

https://kapeli.com/licenses/Dash/2015/181/A9xyvwUTgNKIjFMPNX3Uh4byRMmZgk/license.dash-license

可以打开这个链接,然后升到Dash 4的许可证,然后就可以用啦~


Alfred 3 破解补丁 · May 23, 2017 · 技术文章

每次重启系统都要找一下Alfred 3的破解补丁,太过于麻烦。下载链接如下:

https://pan.baidu.com/s/1nuPHTGX  提取码为:pa71

另外在破解好后,因为破解工具会把alfred 3的证书给移除掉,所以需要给他补个证书,否则每次打开都会提示让你同意他读取通讯录。

sudo codesign --force --deep --sign - "/Applications/Alfred 3.app/Contents/Frameworks/Alfred Framework.framework/Versions/A/Alfred Framework"

Reveal查看任意app的高级技巧 · March 8, 2017 · 技术文章

Reveal是一个很强大的UI分析工具,与其他几个功能相近的工具(比如PonyDebugger)相比,其最大的特点就是非常直观,用来查看app的UI布局非常方便。其常规用法是将framework集成至Xcode工程中,可参见Reveal的官网http://revealapp.com/,但我们这次讲述的却是非常规用法。
在12/21的#阿里技术嘉年华#上,我给听众展示了使用Reveal查看任意app的效果,估计是当时所展示的工具中最亮眼的一个。既然如此,我就提前在这里把Reveal的这个技巧详细的列出来。
1、越狱设备,iPhone/iTouch/iPad都可以,iOS6以上(惊闻iOS7也已经越狱了);
2、安装Reveal,Trail或正式版都可以,然后越狱设备与安装Reveal的Mac在同一wifi内。
3、点击菜单Help / Show Reveal Library in Finder,获取libReveal.dylib

4、将libReveal.dylib上传到设备的/Library/MobileSubstrate/DynamicLibraries

5、同时编辑并上传一个libReveal.plist,格式如下:

注意,此时是可以指定多个BundleID的,也就是说,你可以同时监控任意多的app;再扩大一步说,如果你愿意,不上传这个libReveal.plist,你可以监控所有app,只要你不觉得机器很慢。。。
6、re-spring或重启iOS设备,打开你想看的app,再从Reveal界面左上角选择要连接的机器,进入不同的页面之后还可以点击右上角的刷新钮来刷新监测的页面信息。

以上是不写一行代码就能够查看任意app的方法,各位看别人app爽的时候,也可以摸摸脖子想想自己的app。
这种“高级技巧”从来没有被Reveal官方提起过,而是我们接触到Reveal之后逐步发现的。一开始的方法比较粗暴,是直接hook想看的app,把libReveal.dylib插进去;后来经过@卢明华 的进一步探索,才总结出这个更简单粗暴的方法。
虽然Reveal是最直观的一个工具,但是在iOS逆向这个领域,它占的比重连1/10都不到,真正的大块头都有点难啃,相信各位都是理解的。
最后,相信我们的书出来之后,会给朋友们更多深度撞击的感觉。

该文章转自http://c.blog.sina.com.cn/profile.php?blogid=cb8a22ea89000gtw


category使用 objc_setAssociatedObject/objc_getAssociatedObject 实现添加属性 · December 2, 2016 · 技术文章

属性 其实就是get/set 方法。我们可以使用 objcsetAssociatedObject/objcgetAssociatedObject 实现 动态向类中添加 方法

@interface NSObject (CategoryWithProperty)

@property (nonatomic, strong) NSObject *property;

@end

@implementation NSObject (CategoryWithProperty)

- (NSObject *)property {
    return objc_getAssociatedObject(self, @selector(property));
}

- (void)setProperty:(NSObject *)value {
    objc_setAssociatedObject(self, @selector(property), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

objc runtime 动态增加属性 · October 13, 2016 · 技术文章

objective-c中,有类别可以在不修改源码的基础上增加方法;近排在看别人的开源代码时,发现还可以动态增加属性。而且是在运行时,太牛B了。
使用运行时库,必须要先引入 objc/runtime.h
可以使用的函数如下:

OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

这个函数

OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);

兄弟们,看一个类别和动态添加属性的例子:

UILabel+Associate.h

#import <UIKit/UIKit.h>

@interface UILabel (Associate)

- (void) setFlashColor:(UIColor *) flashColor;

- (UIColor *) getFlashColor;

@end

UILabel+Associate.m

#import "UILabel+Associate.h"
#import <objc/runtime.h>

@implementation UILabel (Associate)

static char flashColorKey;

- (void) setFlashColor:(UIColor *) flashColor{
    objc_setAssociatedObject(self, &flashColorKey, flashColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIColor *) getFlashColor{
    return objc_getAssociatedObject(self, &flashColorKey);
}
@end

调用代码:

UILabel *lab = [[UILabel alloc] init];
[lab setFlashColor:[UIColor redColor]];
NSLog(@"%@", [lab getFlashColor]);

转自:http://www.cnblogs.com/luoguoqiang1985/p/3551966.html?utm_source=tuicool


id ,NSObject, id<NSObject>区别 · October 12, 2016 · 技术文章

我们经常会混淆以下三种申明(我是没有留意过):

id foo1;
NSObject *foo2;
id foo3;
第一种是最常用的,它简单地申明了指向对象的指针,没有给编译器任何类型信息,因此,编译器不会做类型检查。但也因为是这样,你可以发送任何信息给id类型的对象。这就是为什么+alloc返回id类型,但调用[[Foo alloc] init]不会产生编译错误。

因此,id类型是运行时的动态类型,编译器无法知道它的真实类型,即使你发送一个id类型没有的方法,也不会产生编译警告。

我们知道,id类型是一个Objective-C对象,但并不是都指向继承自NSOjbect的对象,即使这个类型和NSObject对象有很多共同的方法,像retain和release。要让编译器知道这个类继承自NSObject,一种解决办法就是像第2种那样,使用NSObject静态类型,当你发送NSObject没有的方法,像length或者count时,编译器就会给出警告。这也意味着,你可以安全地使用像retain,release,description这些方法。

因此,申明一个通用的NSObject对象指针和你在其它语言里做的类似,像java,但其它语言有一定的限制,没有像Objective-C这样灵活。并不是所有的Foundation/Cocoa对象都继承息NSObject,比如NSProxy就不从NSObject继承,所以你无法使用NSObject*指向这个对象,即使NSProxy对象有release和retain这样的通用方法。为了解决这个问题,这时候,你就需要一个指向拥有NSObject方法对象的指针,这就是第3种申明的使用情景。

id告诉编译器,你不关心对象是什么类型,但它必须遵守NSObject协议(protocol),编译器就能保证所有赋值给id类型的对象都遵守NSObject协议(protocol)。这样的指针可以指向任何NSObject对象,因为NSObject对象遵守NSObject协议(protocol),而且,它也可以用来保存NSProxy对象,因为它也遵守NSObject协议(protocol)。这是非常强大,方便且灵活,你不用关心对象是什么类型,而只关心它实现了哪些方法。

现在你知道你要用什么类型了不?

如果你不需要任何的类型检查,使用id,它经常作为返回类型,也经常用于申明代理(delegate)类型。因为代理类型通常在运行时,才会检查是否实现了那些方法。

如果真的需要编译器检查,那你就考虑使用第2种或者第3种。很少看到NSObject*能正常运行,但id无法正常运行的。使用协议(protocol)的优点是,它能指向NSProxy对象,而更常用的情况是,你只想知道某个对象遵守了哪个协议,而不用关心它是什么类型。


查看iOS设备支持的字体 · September 1, 2016 · 技术文章

可以通过遍历UIFont中的familyNames得到所有iOS设备的字体

for (NSString *family in [UIFont familyNames]) 
{ 
    NSLog(@"%@", family); 
    for(NSString *font in [UIFont fontNamesForFamilyName:family]) 
    { 
        NSLog(@"\t%@",font); 
    } 
}

用GDB命令PO(print-object)打印UIView的视图层级 · September 1, 2016 · 技术文章

UIView有一个私有方法:recursiveDescription
这个方法可以显示出当前视图的详细层级,可以在代码中直接调用,也可以在GDB中调用,在GDB中调用时需要借助另一个GDB命令:print-object:

po [self.view recursiveDescription]

iOS多线程GCD · August 1, 2016 · 技术文章

Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。
dispatch queue分成以下三种:
1)运行在主线程的Main queue,通过dispatchgetmain_queue获取。

__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)

DISPATCH_EXPORT struct dispatch_queue_s _dispatch_main_q;
#define dispatch_get_main_queue() \
DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q)
可以看出,dispatch_get_main_queue也是一种dispatch_queue_t。

2)并行队列global dispatch queue,通过dispatchgetglobal_queue获取,由系统创建三个不同优先级的dispatch queue。并行队列的执行顺序与其加入队列的顺序相同。
3)串行队列serial queues一般用于按顺序同步访问,可创建任意数量的串行队列,各个串行队列之间是并发的。
当想要任务按照某一个特定的顺序执行时,串行队列是很有用的。串行队列在同一个时间只执行一个任务。我们可以使用串行队列代替锁去保护共享的数据。和锁不同,一个串行队列可以保证任务在一个可预知的顺序下执行。
serial queues通过dispatchqueuecreate创建,可以使用函数dispatchretain和dispatchrelease去增加或者减少引用计数。
GCD的用法:

// 后台执行: 
dispatchasync(dispatchgetglobalqueue(0, 0), ^{ // something });

// 主线程执行:
dispatch_async(dispatch_get_main_queue(), ^{
    // something
});

// 一次性执行:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // code to be executed once
});

// 延迟2秒执行:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    // code to be executed on the main queue after delay
});

// 自定义dispatch_queue_t
dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL);
dispatch_async(urls_queue, ^{  
    // your code 
});
dispatch_release(urls_queue);

// 合并汇总结果
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
    // 并行执行的线程一
});
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
    // 并行执行的线程二
});
dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
    // 汇总结果
});

一个应用GCD的例子:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
    NSError * error;
    NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
    if (data != nil) {
        dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"call back, the data is: %@", data);
    });
    } else {
        NSLog(@"error when download:%@", error);
    }
});

GCD的另一个用处是可以让程序在后台较长久的运行。
在没有使用GCD时,当app被按home键退出后,app仅有最多5秒钟的时候做一些保存或清理资源的工作。但是在使用GCD后,app最多有10分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。
让程序在后台长久运行的示例代码如下:

// AppDelegate.h文件
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;

// AppDelegate.m文件
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self beingBackgroundUpdateTask];
    // 在这里加上你需要长久运行的代码
    [self endBackgroundUpdateTask];
}

- (void)beingBackgroundUpdateTask
{
    self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [self endBackgroundUpdateTask];
    }];
}

- (void)endBackgroundUpdateTask
{
    [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
    self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}

iOS日志框架CocoaLumberjack配置 · July 21, 2016 · 技术文章

首先,你想要在你的应用程序中配置这个日志框架,通常在applicationDidFinishLaunching方法中配置。
开始时,你需要下面两行代码:

[DDLog addLogger:[DDASLLogger sharedInstance]]; 
[DDLog addLogger:[DDTTYLogger sharedInstance]];

这将在你的日志框架中添加两个“logger”。也就是说你的日志语句将被发送到Console.app和Xcode控制 台(就像标准的NSLog)
这个框架的好处之一就是它的灵活性,如果你还想要你的日志语句写入到一个文件中,你可以添加和配置一个file logger:

fileLogger = [[DDFileLogger alloc] init]; 
fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling 
fileLogger.logFileManager.maximumNumberOfLogFiles = 7; 
[DDLog addLogger:fileLogger];

上面的代码告诉应用程序要在系统上保持一周的日志文件。
用DDLog替换NSLog语句 DDLog的头文件定义了你用来替换NSLog语句的宏,本质上看起来向下边这样:

// Convert from this: 
NSLog(@"Broken sprocket detected!"); 
NSLog(@"User selected file:%@ withSize:%u", filePath, fileSize); 

// To this: 
DDLogError(@"Broken sprocket detected!"); 
DDLogVerbose(@"User selected file:%@ withSize:%u", filePath, fileSize);

我们看到DDLog宏和NSLog的语法完全相同。
所以你所要做的就是决定每个NSlog语句属于哪种日志级别。DDLog默认有四种级别的日志,分别是: 1.@DDlogError
2.@DDlogWarn
3.@DDlogInfo
4.@DDlogVerbose
在配置CocoaLumberjack前可以通过Alcatraz安装XcodeColors,若控制台输出的文字仍然还是黑色,则可以通过

product->scheme->Edit Scheme->Run->Argument->Environment Variables
加上XcodeColors : YES,然后重启Xcode即可。