IMYAOPTableView 소스 학습 노트
Head
최근에 새로운 회사에 입사하여 정보 업무의 모듈을 접하게 되었습니다. 코드를 보니 정보 업무의 광고 삽입은 IMYAOPTableView에서 이루어졌습니다. 호기심 때문에 다음 코드를 탐색하고 한쪽 절차를 밟았습니다. 비록 구조에 아직 이해하지 못한 것이 많지만 [:(], 이쪽은 절차를 기록합니다.
Content
IMYAOPTableView
은 전체적으로 업무 흐름과 광고 흐름을 분리하고 원 데이터 원본과 에이전트, 새로운 데이터 원본과 새로운 에이전트를 기록한 다음에 대응하는 흐름 구조의 세 줄 코드를 나누어 준다. 대응하는 위치는 YYFeedListExample
의 tableView:didSelectRowAtIndexPath:
이다.UITableView *feedsTableView = [ctrl valueForKey:@"feedsView"];
self.aopDemo = [IMYAOPTableDemo new];
self.aopDemo.aopUtils = feedsTableView.aop_utils;
여기서
kvc
을 사용하여 업무 흐름을 추출합니다. 정수는 aop_utils
을 설정하는 속성에 있습니다. 오른쪽에 있는 aop_utils
을 클릭하여 진입합니다.- (IMYAOPTableViewUtils *)aop_utils {
IMYAOPTableViewUtils *aopUtils = objc_getAssociatedObject(self, kIMYAOPTableUtilsKey);
if (!aopUtils) {
@synchronized(self) {
aopUtils = objc_getAssociatedObject(self, kIMYAOPTableUtilsKey);
if (!aopUtils) {
///
[_IMYAOPTableView aop_setupConfigs];
// aop utils, aopUtils tableView
aopUtils = [IMYAOPTableViewUtils aopUtilsWithTableView:self];
//
objc_setAssociatedObject(self, kIMYAOPTableUtilsKey, aopUtils, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// tableView
[aopUtils injectTableView];
}
}
}
return aopUtils;
}
여기에 단례 대상을 만들고
runtime
관련을 사용합니다. [_IMYAOPTableView aop_setupConfigs];
이 왜 쓰는지 모르겠습니다. 어떤 선량한 분이 알아보시면 저에게 알려주세요...aopUtils
에 들어가는 injectTableView
방법:- (void)injectTableView {
UITableView *tableView = self.tableView;
//
// Twitter :T1HomeTimelineItemsViewController
_origDataSource = tableView.dataSource;
_origDelegate = tableView.delegate;
[self injectFeedsView:tableView];
}
여기에 원 데이터 소스, 원 에이전트를
aopUtils
의 _origDataSource
과 _origDelegate
에 저장하고 T1HomeTimelineItemsViewController
의 대상을 injectFeedsView
의 방법으로 들어갑니다.- (void)injectFeedsView:(UIView *)feedsView {
struct objc_super objcSuper = {.super_class = [self msgSendSuperClass], .receiver = feedsView};
// : objcSuper (receiver)
// objc_msgSendSuper feedsView
// feedsView.delegate = self;
// feedsView.dataSource = self;
((void (*)(void *, SEL, id))(void *)objc_msgSendSuper)(&objcSuper, @selector(setDelegate:), self);
((void (*)(void *, SEL, id))(void *)objc_msgSendSuper)(&objcSuper, @selector(setDataSource:), self);
self.origViewClass = [feedsView class];
Class aopClass = [self makeSubclassWithClass:self.origViewClass];
if (![self.origViewClass isSubclassOfClass:aopClass]) {
[self bindingFeedsView:feedsView aopClass:aopClass];
}
}
여기에 구조체 objcSuper를 구성하였으며,objc 를 사용하였다.msgSendSuper 메시지 보내기
((void (*)(void *, SEL, id))(void *)objc_msgSendSuper)(&objcSuper, @selector(setDelegate:), self);
((void (*)(void *, SEL, id))(void *)objc_msgSendSuper)(&objcSuper, @selector(setDataSource:), self);
등가:
feedsView.delegate = self;
feedsView.dataSource = self;
다음으로
makeSubclassWithClass
으로 이동하십시오.- (Class)makeSubclassWithClass:(Class)origClass {
NSString *className = NSStringFromClass(origClass);
NSString *aopClassName = [kA`setupAopClass`PFeedsViewPrefix, stringByAppendingString:className];
Class aopClass = NSClassFromString(aopClassName);
if (aopClass) {
return aopClass;
}
aopClass = objc_allocateClassPair(origClass, aopClassName.UTF8String, 0);
[self setupAopClass:aopClass];
objc_registerClassPair(aopClass);
return aopClass;
}
여기에 동태적으로 하위 클래스
kIMYAOP_ClassName
을 만들고 이 하위 클래스를 실현하는 방법을 주입하는 클래스는 _IMYAOPTableView
이다. 상위 클래스를 덮어쓰는 실현, 예를 들어 setupAopClass
에 들어가면 보기[self addOverriteMethod:@selector(reloadData) aopClass:aopClass];
- (void)addOverriteMethod:(SEL)seletor aopClass:(Class)aopClass {
NSString *seletorString = NSStringFromSelector(seletor);
NSString *aopSeletorString = [NSString stringWithFormat:@"aop_%@", seletorString];
SEL aopMethod = NSSelectorFromString(aopSeletorString);
[self addOverriteMethod:seletor toMethod:aopMethod aopClass:aopClass];
}
- (void)addOverriteMethod:(SEL)seletor toMethod:(SEL)toSeletor aopClass:(Class)aopClass {
Class implClass = [self implAopViewClass];
Method method = class_getInstanceMethod(implClass, toSeletor);
if (method == NULL) {
method = class_getInstanceMethod(implClass, seletor);
}
const char *types = method_getTypeEncoding(method);
IMP imp = method_getImplementation(method);
class_addMethod(aopClass, seletor, imp, types);
}
aop_seletor
을 동적으로 생성하여 하위 클래스 kIMYAOP_ClassName
의 메소드 목록에 추가합니다.class_addMethod(aopClass, seletor, imp, types);
그래서
aopUtils.tableView.reloadData
을 다시 호출할 때 _IMYAOPTableView
의 aop_reloadData
방법으로 실현하고 bindingFeedsView:aopClass:
을 내려다보는데...이게 뭔지 모르겠어, 알아볼 수 있으면 빨리 알려줘...여기에 원시 데이터 원본, 에이전트, 동적 생성 하위 클래스, 하위 클래스 덮어쓰기 방법 등을 설정하고 광고 클래스의 설정을 보겠습니다.
왼쪽
aopUtils
클릭 self.aopDemo.aopUtils = feedsTableView.aop_utils;
injectTableView
에 진입- (void)injectTableView {
[self.aopUtils.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"AD"];
/// , TableView Delegate,DataSource 。
self.aopUtils.delegate = self;
self.aopUtils.dataSource = self;
dispatch_async(dispatch_get_main_queue(), ^{
[self insertRows];
});
}
여기, aopUtils의 에이전트를 광고 클래스로 설정하여 마지막 분배에 사용합니다. 아래를 보면
insertRows
:- (void)insertRows {
NSMutableArray *insertBodys = [NSMutableArray array];
/// 5
for (int i = 0; i < 5; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:arc4random() % 10 inSection:0];
[insertBodys addObject:[IMYAOPTableViewInsertBody insertBodyWithIndexPath:indexPath]];
}
///
[self.aopUtils insertWithSections:nil];
[self.aopUtils insertWithIndexPaths:nil];
/// , row row
[self.aopUtils insertWithIndexPaths:insertBodys];
/// tableView reloadData,
[self.aopUtils.tableView reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", self.aopUtils.allModels);
});
}
insertWithIndexPaths
진입 방법:- (void)insertWithIndexPaths:(NSArray *)indexPaths {
NSArray *array = [indexPaths sortedArrayUsingComparator:^NSComparisonResult(IMYAOPBaseInsertBody *_Nonnull obj1, IMYAOPBaseInsertBody *_Nonnull obj2) {
return [obj1.indexPath compare:obj2.indexPath];
}];
NSMutableDictionary *insertMap = [NSMutableDictionary dictionary];
[array enumerateObjectsUsingBlock:^(IMYAOPBaseInsertBody *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
NSInteger section = obj.indexPath.section;
NSInteger row = obj.indexPath.row;
NSMutableArray *rowArray = insertMap[@(section)];
if (!rowArray) {
rowArray = [NSMutableArray array];
[insertMap setObject:rowArray forKey:@(section)];
}
while (YES) {
BOOL hasEqual = NO;
for (NSIndexPath *inserted in rowArray) {
if (inserted.row == row) {
row++;
hasEqual = YES;
break;
}
}
if (hasEqual == NO) {
break;
}
}
NSIndexPath *insertPath = [NSIndexPath indexPathForRow:row inSection:section];
[rowArray addObject:insertPath];
obj.resultIndexPath = insertPath;
}];
self.sectionMap = insertMap;
}
게으른 저를 용서해 주세요. 제가 직접 결과를 봤는데 광고의
indexPath
을 sectionMap
에 기록한 거예요. 네, 맞아요. 아마...마지막으로 호출 과정입니다.[self.aopUtils.tableView reloadData];
_IMYAOPTableView
에 이르는 aop_reloadData
방법이 실현됩니다.- (void)aop_reloadData {
AopDefineVars;
aop_utils.isUICalling += 1;
AopCallSuper(@selector(reloadData));
aop_utils.isUICalling -= 1;
}
여기에 부류(YYTableView)의
reloadData
방법을 사용하고 YYTableView는 [super reloadData]
을 사용했기 때문에 최종적으로 [UITableView]
의 reloadData
을 사용했다. 즉, aop_utils
의 데이터 원본 방법에 가서 IMYAOPTableViewUtils+UITableViewDataSource
의 numberOfRowsInSection
방법을 살펴보면 핵심 방법은NSIndexPath *feedsIndexPath = [self feedsIndexPathByUser:[NSIndexPath indexPathForRow:rowCount inSection:section]];
- (NSIndexPath *)feedsIndexPathByUser:(NSIndexPath *)userIndexPath {
if (userIndexPath == nil) {
return nil;
}
NSInteger section = userIndexPath.section;
NSInteger row = userIndexPath.row;
/// table section
section = [self feedsSectionByUser:section];
NSMutableArray *array = self.sectionMap[@(section)];
for (NSIndexPath *obj in array) {
if (obj.row <= row) {
row += 1;
} else {
break;
}
}
NSIndexPath *feedsIndexPath = [NSIndexPath indexPathForRow:row inSection:section];
return feedsIndexPath;
}
여기서 최종 업무 흐름 + 광고 흐름의cell 수량을 계산하고
tableView:cellForRowAtIndexPath:
방법을 내려다본다.- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
kAOPUICallingSaved;
kAOPUserIndexPathCode;
UITableViewCell *cell = nil;
if ([dataSource respondsToSelector:@selector(tableView:cellForRowAtIndexPath:)]) {
cell = [dataSource tableView:tableView cellForRowAtIndexPath:indexPath];
}
if (![cell isKindOfClass:[UITableViewCell class]]) {
cell = [UITableViewCell new];
if (dataSource) {
NSAssert(NO, @"Cell is Nil");
}
}
kAOPUICallingResotre;
return cell;
}
핵심은
kAOPUserIndexPathCode
: 이 indexPath가 광고 흐름인지 업무 흐름인지 구분하여 userIndexPathByFeeds
을 보고 최종적으로 dataSource
을 꺼내 나누어 준다.#define kAOPUserIndexPathCode \
NSIndexPath *userIndexPath = [self userIndexPathByFeeds:indexPath]; \
id dataSource = nil; \
if (userIndexPath) { \
dataSource = (id)self.origDataSource; \
indexPath = userIndexPath; \
} else { \
dataSource = self.dataSource; \
isInjectAction = YES; \
} \
if (isInjectAction) { \
self.isUICalling += 1; \
}
- (NSIndexPath *)userIndexPathByFeeds:(NSIndexPath *)feedsIndexPath {
if (!feedsIndexPath) {
return nil;
}
NSInteger section = feedsIndexPath.section;
NSInteger row = feedsIndexPath.row;
NSMutableArray *array = self.sectionMap[@(section)];
NSInteger cutCount = 0;
for (NSIndexPath *obj in array) {
if (obj.row == row) {
cutCount = -1;
break;
}
if (obj.row < row) {
cutCount++;
} else {
break;
}
}
if (cutCount < 0) {
return nil;
}
/// , index
section = [self userSectionByFeeds:section];
NSIndexPath *userIndexPath = [NSIndexPath indexPathForRow:row - cutCount inSection:section];
return userIndexPath;
}
END
아직도 못 알아본 데가 많아요. 공부 많이 해야 돼요.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.