Skip to content

Commit

Permalink
QMUIRuntime 增加 qmui_getProjectClassList() 函数,优化 App 启动时寻找配置表的性能
Browse files Browse the repository at this point in the history
  • Loading branch information
MoLice committed Dec 11, 2019
1 parent f9f872e commit c53f0a9
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 20 deletions.
36 changes: 16 additions & 20 deletions QMUIKit/QMUICore/QMUIConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -83,29 +83,25 @@ - (void)applyInitialTemplate {
// Automatically look for templates and apply them
// @see https://github.com/Tencent/QMUI_iOS/issues/264
Protocol *protocol = @protocol(QMUIConfigurationTemplateProtocol);
int numberOfClasses = objc_getClassList(NULL, 0);
if (numberOfClasses > 0) {
Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numberOfClasses);
numberOfClasses = objc_getClassList(classes, numberOfClasses);
for (int i = 0; i < numberOfClasses; i++) {
Class class = classes[i];
// 这里用 containsString 是考虑到 Swift 里 className 由“项目前缀+class 名”组成,如果用 hasPrefix 就无法判断了
// Use `containsString` instead of `hasPrefix` because class names in Swift have project prefix prepended
if ([NSStringFromClass(class) containsString:@"QMUIConfigurationTemplate"] && [class conformsToProtocol:protocol]) {
if ([class instancesRespondToSelector:@selector(shouldApplyTemplateAutomatically)]) {
id<QMUIConfigurationTemplateProtocol> template = [[class alloc] init];
if ([template shouldApplyTemplateAutomatically]) {
QMUI_hasAppliedInitialTemplate = YES;
[template applyConfigurationTemplate];
_active = YES;// 标志配置表已生效
// 只应用第一个 shouldApplyTemplateAutomatically 的主题
// Only apply the first template returned
break;
}
classref_t *classes = nil;
int numberOfClasses = qmui_getProjectClassList(&classes);
for (NSInteger i = 0; i < numberOfClasses; i++) {
Class class = (__bridge Class)classes[i];
// 这里用 containsString 是考虑到 Swift 里 className 由“项目前缀+class 名”组成,如果用 hasPrefix 就无法判断了
// Use `containsString` instead of `hasPrefix` because class names in Swift have project prefix prepended
if ([NSStringFromClass(class) containsString:@"QMUIConfigurationTemplate"] && [class conformsToProtocol:protocol]) {
if ([class instancesRespondToSelector:@selector(shouldApplyTemplateAutomatically)]) {
id<QMUIConfigurationTemplateProtocol> template = [[class alloc] init];
if ([template shouldApplyTemplateAutomatically]) {
QMUI_hasAppliedInitialTemplate = YES;
[template applyConfigurationTemplate];
_active = YES;// 标志配置表已生效
// 只应用第一个 shouldApplyTemplateAutomatically 的主题
// Only apply the first template returned
break;
}
}
}
free(classes);
}

if (IS_DEBUG && self.sendAnalyticsToQMUITeam) {
Expand Down
20 changes: 20 additions & 0 deletions QMUIKit/QMUICore/QMUIRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,23 @@ _QMUIGetIvarValueGenerator(Selector, SEL)
CG_INLINE id getObjectIvarValue(id object, Ivar ivar) {
return object_getIvar(object, ivar);
}


#pragma mark - Mach-O

typedef struct classref *classref_t;

/**
获取业务项目的所有 class
@param classes 传入 classref_t 变量的指针,会填充结果到里面,然后可以用下标访问。如果只是为了得到总数,可传入 NULL。
@return class 的总数
例如:
@code
classref_t *classes = nil;
int count = qmui_getProjectClassList(&classes);
Class class = (__bridge Class)classes[0];
@endcode
*/
FOUNDATION_EXPORT int qmui_getProjectClassList(classref_t **classes);
46 changes: 46 additions & 0 deletions QMUIKit/QMUICore/QMUIRuntime.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

#import "QMUIRuntime.h"
#import "QMUICommonDefines.h"
#include <mach-o/getsect.h>
#include <mach-o/dyld.h>

@implementation QMUIPropertyDescriptor

Expand Down Expand Up @@ -146,3 +148,47 @@ + (NSString *)typeWithEncodeString:(NSString *)encodeString {
}

@end

#ifndef __LP64__
typedef struct mach_header headerType;
#else
typedef struct mach_header_64 headerType;
#endif

static const headerType *getProjectImageHeader() {
const uint32_t imageCount = _dyld_image_count();
const char *target_image_name = ((NSString *)[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleExecutableKey]).UTF8String;
const headerType *target_image_header = 0;
for (uint32_t i = 0; i < imageCount; i++) {
const char *image_name = _dyld_get_image_name(i);
if (strstr(image_name, target_image_name) != NULL) {
target_image_header = (headerType *)_dyld_get_image_header(i);
break;
}
}
return target_image_header;
}

// from https://github.com/opensource-apple/objc4/blob/master/runtime/objc-file.mm
static classref_t *getDataSection(const headerType *machHeader, const char *sectname, size_t *outCount) {
unsigned long byteCount = 0;
classref_t *data = (classref_t *)getsectiondata(machHeader, "__DATA", sectname, &byteCount);
if (!data) {
data = (classref_t *)getsectiondata(machHeader, "__DATA_CONST", sectname, &byteCount);
}
if (!data) {
data = (classref_t *)getsectiondata(machHeader, "__DATA_DIRTY", sectname, &byteCount);
}
if (outCount) *outCount = byteCount / sizeof(classref_t);
return data;
}

int qmui_getProjectClassList(classref_t **classes) {
size_t count = 0;
if (!classes) {
getDataSection(getProjectImageHeader(), "__objc_classlist", &count);
return (int)count;
}
*classes = getDataSection(getProjectImageHeader(), "__objc_classlist", &count);
return (int)count;
}

0 comments on commit c53f0a9

Please sign in to comment.