iOS Tweak进阶

/ 1

theos资料

%hook%end:hook 一个类的开始与结束

%log:打印方法调用详情。可以通过 Xcode - Window - Devices and Simulators 查看日志

HBDebugLog:跟 NSLog 类似

%new:添加一个新的方法,相当于 class_addMethod,如果直接调用 %new 新增的方法,需要在 @interface 中声明一下。

%c(ClassName):生成一个 Class 对象,相当于 objc_getClass

%orig:函数原来的代码逻辑

%ctor:加载动态库时调用

%dtor:退出程序时调用

logify.pl:可以将一个头文件快速转换成已经包含打印信息的 .xm 文件

logify.pl使用

先将头文件转换为 .xm 文件:

logify.pl xxx.h > xx.xm

然后需要进行一些处理才能编译通过:

删掉关键字 __weakinout

删掉方法 -(void).cxx_destruct {%log; %orig;}

HBLogDebug(@"x%x",(unsigned int)r); 改为 HBLogDebug(@"x%@",(unsigned int)r);

id<协议名> 改为 id,或者声明缺少的协议 @protocol 协议名;

类名 * 改为 void *,或者声明缺少的类 @class 类名;

项目资源

Tweak 工程的图片、plist配置文件等资源需要存放在特定目录下,才能正常安装到手机上。

在工程根目录创建 layout 目录,相当于手机的根目录。一般系统插件(在系统的设置里有入口,比如 Reveal Loader)的资源文件会放在 layout/Library/PreferenceLoader/Preferences 目录下,为了不和其他插件的资源冲突,最好是再创建一个只存放自己资源的目录。

比如我们创建的目录为:layout/Library/PreferenceLoader/Preferences/JFXXXXXX

则在代码中引用资源的绝对路径为:/Library/PreferenceLoader/Preferences/JFXXXXXX

如果是 MonkeyDev 创建的 Logos Tweak 工程,则 Package 目录相当于手机的根目录:

多文件开发

在项目根目录创建 src 目录用于存放我们所有源代码,然后再在 src 下创建我们需要的目录结构,并且需要在 Makefile 文件中指定参与编译的源文件,指定时支持通配符,多个以空格分隔。

当我们在 .x 文件中导入我们创建的 .h 头文件时,请以当前 .x 文件当前目录为基准。

比如根目录下的 src 目录里的所有 .x 文件参与编译:

xxx_FILES = $(wildcard src/*.x)

一些小细节

1.和 iOS 正向开发一样,使用 NSUserDefaults 来保存一些简单配置,这样最终保存的 plist 文件也是在对应 APP 的沙盒目录下。

2.使用 self 调用方法,需要前向声明。self 在此处会被认为是 id 类型,而 numberOfSectionsInTableView: 方法属于 WCTableViewManager 类的方法,如果不对 WCTableViewManager 进行前向声明,是无法调用 numberOfSectionsInTableView: 的。

Tweak.x:12:18: error: receiver type 'WCTableViewManager' for instance message is a forward declaration
        if (section == [self numberOfSectionsInTableView:tableView] - 1) {
                        ^~~~
Tweak.x:24:8: note: forward declaration of class here
@class WCTableViewManager;

前向声明代码:

@interface WCTableViewManager
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
@end

3.未导入头文件会提示未知类型,需要导入对应的头文件:

Tweak.x:25:20: error: type specifier missing, defaults to 'int' [-Werror,-Wimplicit-int]
static NSInteger (*_logos_orig$_ungrouped$WCTableViewManager$numberOfSectionsInTableView$)(_LOGOS_SELF_TYPE_NORMAL WCTableViewManager* _LOGOS_SELF_CONST, SEL, UITableView *); static NSInteger _logos_method$_un...
                   ^
Tweak.x:25:160: error: unknown type name 'UITableView'
static NSInteger (*_logos_orig$_ungrouped$WCTableViewManager$numberOfSectionsInTableView$)(_LOGOS_SELF_TYPE_NORMAL WCTableViewManager* _LOGOS_SELF_CONST, SEL, UITableView *); static NSInteger _logos_method$_un...

4.导入了 <UIKit/UIKit.h> 编译失败:

==> Notice: Build may be slow as Theos isn’t using all available CPU cores on this computer. Consider upgrading GNU Make: https://github.com/theos/theos/wiki/Parallel-Building
> Making all for tweak WechatTweak…
==> Preprocessing Tweak.x…
==> Compiling Tweak.x (armv7)…
==> Linking tweak WechatTweak (armv7)…
ld: warning: building for iOS, but linking in .tbd file (/opt/theos/vendor/lib/CydiaSubstrate.framework/CydiaSubstrate.tbd) built for iOS Simulator
Undefined symbols for architecture armv7:
  "_OBJC_CLASS_$_UIAlertController", referenced from:
      objc-class-ref in Tweak.x.ae33fabd.o
  "_OBJC_CLASS_$_UIApplication", referenced from:
      objc-class-ref in Tweak.x.ae33fabd.o
  "_OBJC_CLASS_$_UITableViewCell", referenced from:
      objc-class-ref in Tweak.x.ae33fabd.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

需要在 Makefile 文件中,指定要导入的 Framework

TARGET := iphone:clang:latest:10.0
INSTALL_TARGET_PROCESSES = SpringBoard

include $(THEOS)/makefiles/common.mk

TWEAK_NAME = WechatTweak

WechatTweak_FILES = Tweak.x
WechatTweak_CFLAGS = -fobjc-arc
WechatTweak_FRAMEWORKS = UIKit

include $(THEOS_MAKE_PATH)/tweak.mk