Hopper 是一款不错的 mac OS 与 Linux 反汇编工具,同时还提供一定的反编译能力,可以利用它来调试我们的程序。此外,Hopper 还支持控制流视图模式,Python 脚本,LLDB & GDB,并且提供了 Hopper SDK 可供扩展,在 Hopper SDK 的基础上你甚至可以扩展自己的文件格式和 CPU 支持。
值得一提的是 Hopper 的作者是一名独立开发者,他的日常工作环境也是在 mac OS 上,所以在 mac OS 上的 Hopper 是完全使用 Cocoa Framework 实现的,而 Linux 版本的 Hopper 则选择使用 Qt 5 来实现。
个人认为 Hopper 在 mac OS 上面的运行表现非常好,很多细节(比如类型颜色区分等)都做的不错,功能简洁的同时快捷键也很好记(Hopper 提供的功能已经覆盖了绝大多数使用场景)。
我们知道 C 语言中如果想要灵活的创建一个动态大小的数组需要自己手动开辟、管理、释放相关的内存,示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
voidfoo() { int max; double *ptd; puts("What is the maximum number of type double entries?"); scanf("%d", &max); ptd = malloc(max * sizeof(double)); if (ptd == NULL) { // memory allocation failed ... } // some logic ... free(ptd); }
那么 C struct 中 ARC Objective-C 的动态内存管理是否应该这么写呢?
1 2 3 4 5 6
// Structs with ARC Fields Need Care for Dynamic Memory Management typedefstruct { NSString *name; NSNumber *price; } MenuItem; void testMenuItems() {
// Allocate an array of 10 menu items
MenuItem *items = malloc(10 * sizeof(MenuItem));
orderMenuItems(items, 10);
free(items);
}
Note: 在 Swift 中闭包默认都是非逃逸闭包(non-escaping closures),即闭包不应该在函数返回之后执行。
Objective-C 中与 Swift 闭包对应的就是 Block 了,但是 Objective-C 中的 Block 并没有诸如 Swift 中逃逸与否的限制,那么我们这样将 Swift 的非逃逸闭包转为 Objective-C 中无限制的 Block 岂不是会有问题?
别担心,转换过来的闭包(非逃逸)会有 Warnning 提示,而且我们说过一般这种情况下 Apple 的工程师都会在 LLVM 为 Objective-C 加一个宏来迎合 Swift…
1 2
// Warning for Missing Noescape Annotations for Method Overrides#import “Executor-Swift.h”@interfaceDispatchExecutor : NSObject<Executor>
- (void)performOperation:(NS_NOESCAPEvoid (^)(void))handler;
@end@implementationDispatchExecutor
- (void)performOperation:(NS_NOESCAPEvoid (^)(void))handler {
} // Programmer must ensure that handler is not called after performOperation returns@end
个人观点
如果 Swift 5 真的可以做到 ABI 稳定,那么 Swift 与 Objective-C 混编的 App 包大小也应该回归正常,相信很多公司的项目都会慢慢从 Objective-C 转向 Swift。在 Swift 中闭包(Closures)作为一等公民的存在奠定了 Swift 作为函数式语言的根基,本次 LLVM 提供了将 Swift 中的 Closures 与 Objective-C 中的 Block 互通转换的支持无疑是很有必要的。
使用 #pragma pack 打包 Struct 成员
Emmmmm… 老实说这一节的内容更底层,所以可能会比较晦涩,希望自己可以表述清楚吧。在 C 语言中 struct 有 内存布局(memory layout) 的概念,C 语言允许编译器为每个基本类型指定一些对齐方式,通常情况下是以类型的大小为标准对齐,但是它是特定于实现的。
C 语言允许每个远程现代编译器实现 #pragma pack,它允许程序猿对填充进行控制来依从 ABI。
From C99 §6.7.2.1:
12 Each non-bit-field member of a structure or union object is aligned in an implementation- defined manner appropriate to its type.
13 Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.
原因在于要做职责分离,这样也能避免很多重复代码。在 iOS 和 Mac OS X 两个平台上,事件和用户交互有很多地方的不同,基于多点触控的用户界面和基于鼠标键盘的交互有着本质的区别,这就是为什么 iOS 有 UIKit 和 UIView,而 Mac OS X 有 AppKit 和 NSView 的原因。他们功能上很相似,但是在实现上有着显著的区别。
// Add the selector as being modified. currentClass = klass; AspectTracker *parentTracker = nil; do { AspectTracker *tracker = swizzledClassesDict[currentClass]; if (!tracker) { tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass parent:parentTracker]; swizzledClassesDict[(id<NSCopying>)currentClass] = tracker; } [tracker.selectorNames addObject:selectorName]; // All superclasses get marked as having a subclass that is modified. parentTracker = tracker; }while ((currentClass = class_getSuperclass(currentClass)));
Note: 聪明的你应该已经注意到了全局变量 swizzledClassesDict 中的 value 对应着 AspectTracker 指针。
// 偏执。我们已经在 hook 注册的时候检查过了,(不过这里我们还要检查)。 if (numberOfArguments > originalInvocation.methodSignature.numberOfArguments) { AspectLogError(@"Block has too many arguments. Not calling %@", info); returnNO; }