iOS接入开屏广告教程 : 以腾讯优量汇为案例

·  阅读 2744
iOS接入开屏广告教程 : 以腾讯优量汇为案例

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

引言

本文以对接开屏广告为例子

developers.adnet.qq.com/doc/ios/uni…

广告类型接入方式简介适用场景版本备注
开屏广告SDK原生渲染开屏广告以App启动作为曝光时机,提供5s的可感知广告展示。用户可以点击广告跳转到目标页面;或者点击右上角的“跳过”按钮,跳转到app内容首页。 开屏V+广告是一个5s-30s的视频广告,在5s开屏呈现的过程中,用户点击右上角的“进入首页”或5s曝光结束后,视频均将收缩到APP内右下角的小视窗继续播放。APP启动时包含 开屏 与 开屏V+ 两种

IOS对接文档:

developers.adnet.qq.com/doc/ios/gui…

ios对接SDK包:对接demo 工程

OC 版本:github.com/zhangkn/GDT…,更多资源请关注公众号:iOS技能

[d3g.qq.com/gdt/sdk/ios…d3g.qq.com/gdt/sdk/ios…

)

在这里插入图片描述

优量汇 iOS14 适配指南

developers.adnet.qq.com/doc/ios/uni… 前提使用优量汇iOS SDK 4.12.5及以上版本

本文重点是拉取最新SDK库、iOS14的idfa适配以及iOS13的modal样式适配

I 、 SDK部署

developers.adnet.qq.com/doc/ios/gui…

1.1 术语介绍

APPID:媒体 ID,是您在腾讯优量汇开发者平台创建媒体时获得的ID,这个ID是我们在广告网络中识别您应用的唯一ID。

PlacementId:广告位 ID,是您在腾讯优量汇开发者平台为您的应用所创建的某种类型(Banner、开屏、插屏、平台模板、激励视频)的广告位置的ID。

dto(Data Transfer Objects): 接口返回的原始数据

权限申请

部分广告样式的接入需要权限,您可以联系腾讯优量汇运营进行了解和权限申请。在腾讯优量汇开发者平台新建广告位时您只能看到您有相应权限的广告位类型。目前有专门的优量汇代理商负责运营和商务。

说明:针对单媒体的用户,允许获取idfa和定位权限的,投放定向广告;不允许获取权限的用户,投放通投广告,媒体可以选择是否把idfa和定位数据提供给优量汇,并承担相应广告填充和eCPM单价下降损失的结果。

未在优量汇注册,请注册加入优量汇或者申请成为运营者

运营者adnet.qq.com/register/be…

注册:adnet.qq.com/register

1.2 拉取最新SDK库

pod 'GDTMobSDK'
#-> Installing GDTMobSDK 4.12.90 (was 4.11.11)

复制代码

使用pod update GDTMobSDK --verbose 拉取最新库,否则无法更新成功SDK到项目 ➜ retail git:(develop) ✗ pod update GDTMobSDK --verbose

1.3 接入注意事项

目前开屏广告只针对iPhone设备在垂直方向上展示。

  1. 开屏全屏广告需使得显示区域其高度与设备高度一致,即为开屏全屏广告。开发者可通过以下接口配合使用提供媒体logo,用以随开屏广告展示。

logo 推荐使用透明背景色,可为空

 - (void)loadFullScreenAd;
 - (void)showFullScreenAdInWindow:(UIWindow *)window withLogoImage:(UIImage *)logoImage skipView:(UIView *)skipView;

复制代码
  1. 开屏半屏广告的显示区域其高度一定要大于设备高度的75%(建议值大于80%),最小高度要大于400dp,开屏广告默认只在竖屏展示,横屏一般不满足尺寸要求。

  2. 优量汇开屏广告支持预加载开屏广告,调用方法如下:

    GDTSplashAd *preloadSplashAd = [[GDTSplashAd alloc] initWithPlacementId:YOUR_PLACEMENT_ID];
    [preloadSplashAd preloadSplashOrderWithPlacementId:YOUR_PLACEMENT_ID];

复制代码
  1. 初始化SDK,加载广告的代码推荐放在didFinishLaunchingWithOptions的第一行
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    [self setupGDTSDKConfig];
    ....
    return YES;
}
复制代码
  1. 由于SDK的静态库文件libGDTMobSDK.a>110M, 提交到git 仓库时可能由于文件大小的限制导致提交失败,所以推荐你使用.gitignore 将Pods目录忽略。其他同事拉取代码之后,可采用pod install --verbose --no-repo-update只安装新添加的库,已更新的库忽略。或者更新指定的库,其它库忽略 pod update 库名 --verbose --no-repo-update
# Pods/

复制代码

在这里插入图片描述

1.4 权限适配

针对单媒体的用户,允许获取idfa和定位权限的,投放定向广告;不允许获取权限的用户,投放通投广告,媒体可以选择是否把idfa和定位数据提供给优量汇,并承担相应广告填充和eCPM单价下降损失的结果。

idfa的适配请看本文的第三章节

GPS信息获取开关

在已获得GPS权限的前提下,媒体可以选择是否在广告中获取用户的GPS信息,以便获取定向广告。方法如下:

#import "GDTSDKConfig.h"

[GDTSDKConfig enableGPS:YES]; // 获取用户的GPS信息,默认值为NO

复制代码

II 、 接入代码示例

2.1 在AppDelegate头文件中导入头文件并声明实例

#import "GDTSplashAd.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate,GDTSplashAdDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) GDTSplashAd *splash;
@property (retain, nonatomic) UIView *bottomView;

@end

复制代码

2.2 初始化并加载广告数据

在AppDelegate的实现文件中初始化并加载广告数据,开屏广告目前支持全屏开屏和半屏开屏广告两种形式,其中半屏开屏广告支持开发者自定义设置开屏底部的界面,用以展示应用Logo等。

  1. 注册媒体ID
    BOOL result = [GDTSDKConfig registerAppId:@"xxx"];//
    
    
    if (result) {
        [self setupGDTSplashAd];

        NSLog(@"注册成功");
    }

复制代码
  1. 初始化开屏广告位ID
    GDTSplashAd *splash = [[GDTSplashAd alloc] initWithPlacementId:@"6806"];

复制代码
  1. 先拉取(LoadAd),再手动调用显示(splash show)
  // splash LoadAd 逻辑
  GDTSplashAd *splash = [[GDTSplashAd alloc] initWithPlacementId:YOUR_PLACEMENT_ID];
  splash.delegate = self; //设置代理
  //根据iPhone设备不同设置不同背景图
  if ([[UIScreen mainScreen] bounds].size.height >= 568.0f) {
   splash.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"LaunchImage-568h"]];
  } else {
   splash.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"LaunchImage"]];
  }
  splash.fetchDelay = 3; //开发者可以设置开屏拉取时间,超时则放弃展示
  [splashAd loadFullScreenAd];



复制代码

拉取成功之后手动调用来显示广告

#pragma mark - GDTSplashAdDelegate

- (void)splashAdDidLoad:(GDTSplashAd *)splashAd {
//    if (splashAd.splashZoomOutView) {
//        [self.view addSubview:splashAd.splashZoomOutView];
//        splashAd.splashZoomOutView.rootViewController = self;
//        // 支持拖拽
//        [splashAd.splashZoomOutView supportDrag];
//    }
    NSLog(@"%s", __func__);
    
    NSString *text = [NSString stringWithFormat:@"%@ 广告拉取成功", splashAd.adNetworkName];
    
    
    NSLog(@"ecpm:%ld ecpmLevel:%@ text:%@", splashAd.eCPM, splashAd.eCPMLevel,text);
    
    // splash show逻辑
    //设置开屏自定义 logo,展示半屏开屏广告
    
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    [self.splash showFullScreenAdInWindow:window withLogoImage:[UIImage imageNamed:@"img_login_logo"] skipView:nil];

}

- (void)splashAdSuccessPresentScreen:(GDTSplashAd *)splashAd
{
    NSLog(@"%s",__FUNCTION__);
//    self.tipsLabel.text = ;
    NSLog(@"广告展示成功");
    

}

复制代码

设置自定义跳过按钮

UIView *customSkipView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 50)]; // 设置跳过按钮的frame信息
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
[self.splashAd showAdInWindow:window withBottomView:self.bottomView skipView:customSkipView];

复制代码

通过backgroundImage 根据iPhone设备不同设置不同背景图

需要iPhone 8 Plus@2x.pngiPhoneX@2x.pngiPhone4@1x_2.png,以及启动页的SplashLogo.png

    UIImage *splashImage = [UIImage imageNamed:@"SplashNormal"];
    if (isIPhoneXSeries()) {
        splashImage = [UIImage imageNamed:@"SplashX"];
    } else if ([UIScreen mainScreen].bounds.size.height == 480) {
        splashImage = [UIImage imageNamed:@"SplashSmall"];
    }
    self.splashAd.needZoomOut = self.supportZoomoutViewSwitch.isOn;
    self.splashAd.backgroundImage = splashImage;
    self.splashAd.backgroundImage.accessibilityIdentifier = @"splash_ad";

复制代码

2.3 开屏广告Demo

demo 中的广告案例

    self.demoArray = [@[
                        @[@"自渲染2.0", @"UnifiedNativeAdViewController"],
                        @[@"开屏广告", @"SplashViewController"],
                        @[@"原生模板广告", @"NativeExpressAdViewController"],
                        @[@"原生视频模板广告", @"NativeExpressVideoAdViewController"],
                        @[@"激励视频广告", @"RewardVideoViewController"],
                        @[@"HybridAd", @"HybridAdViewController"],
                        @[@"Banner2.0", @"UnifiedBannerViewController"],
                        @[@"插屏2.0", @"UnifiedInterstitialViewController"],
                        @[@"插屏2.0全屏", @"UnifiedInterstitialFullScreenVideoViewController"],
                        @[@"获取IDFA", @(1)],
                        @[@"试玩广告调试", @"PlayableAdTestViewController"],
                        ] mutableCopy];

复制代码

#import "SplashViewController.h"

注册媒体ID API

/**
 SDK 注册接口,请在 app 初始化时调用。
 @param appId - 媒体ID
 
 @return 注册是否成功。
*/
+ (BOOL)registerAppId:(NSString *)appId;

复制代码

/**
 *  开屏广告的背景图片
 *  可以设置背景图片作为开屏加载时的默认背景
 */
@property (nonatomic, strong) UIImage *backgroundImage;
/**
 *  开屏广告的背景色
 *  可以设置开屏图片来作为开屏加载时的默认图片
 */
@property (nonatomic, copy) UIColor *backgroundColor;
/**
 *  发起拉取全屏广告请求,只拉取不展示
 *  详解:广告素材及广告图片拉取成功后会回调splashAdDidLoad方法,当拉取失败时会回调splashAdFailToPresent方法
 */
- (void)loadFullScreenAd;

/**
 *  展示全屏广告,调用此方法前需调用isAdValid方法判断广告素材是否有效
 *  详解:广告展示成功时会回调splashAdSuccessPresentScreen方法,展示失败时会回调splashAdFailToPresent方法
 */
- (void)showFullScreenAdInWindow:(UIWindow *)window withLogoImage:(UIImage *)logoImage skipView:(UIView *)skipView;

 /**
 *  发起拉取广告请求,只拉取不展示
 *  详解:广告素材及广告图片拉取成功后会回调splashAdDidLoad方法,当拉取失败时会回调splashAdFailToPresent方法
 */
- (void)loadAd;

/**
 *  展示广告,调用此方法前需调用isAdValid方法判断广告素材是否有效
 *  详解:广告展示成功时会回调splashAdSuccessPresentScreen方法,展示失败时会回调splashAdFailToPresent方法
 */
- (void)showAdInWindow:(UIWindow *)window withBottomView:(UIView *)bottomView skipView:(UIView *)skipView;

/**
 * 返回广告是否可展示
 * 对于并行请求,在调用showAdInWindow前时需判断下
 * @return 当广告已经加载完成且未曝光时,为YES,否则为NO
 */
- (BOOL)isAdValid;

复制代码

III、适配idfa

3.1 适配开屏广告

为iOS14.5升级所需要的修改,目的是请求用户授权,访问与应用相关的数据以跟踪用户或设备。详情请访问developer.apple.com/documentati…

在Info.plist中添加NSUserTrackingUsageDescription,描述获取IDFA等广告标识符的用途

	<key>NSUserTrackingUsageDescription</key>
	<string>该ID将用于向您推送个性化广告</string>

复制代码

弹窗小字文案建议:

  1. 获取标记权限向您提供更优质、安全的个性化服务及内容,未经同意我们不会用于其他目的;开启后,您也可以前往系统“设置-隐私 ”中随时关闭。

  2. 获取IDFA等广告标识符权限向您提供更优质、安全的个性化服务及内容;开启后,您也可以前往系统“设置-隐私 ”中随时关闭。

展示授权弹窗需要调用requestTrackingAuthorizationWithCompletionHandler:方法。

建议流量主等待方法回调完成后处理广告相关逻辑,这样如果用户授权使用IDFA等广告标识符信息,优量汇iOS SDK可以使用IDFA等广告标识符进行广告请求。代码如下:

#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <AdSupport/AdSupport.h>
...
- (void)requestIDFA {
  [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
    //  授权完成回调
     [self loadGDTAd];// 先加载
  }];
}

复制代码

完整适配代码如下

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    BOOL result = [GDTSDKConfig registerAppId:@""];//
    
    
    if (result) {
//        [self setupGDTSplashAd];
        [self requestIDFA];
        

        NSLog(@"注册成功");
    }

    return YES;
}
- (void)setupGDTSplashAd{
    
    // splash LoadAd 逻辑
    GDTSplashAd *splash = [[GDTSplashAd alloc] initWithPlacementId:@""];
    
    splash.delegate = self; //设置代理

    
//    if ([[UIScreen mainScreen] bounds].size.height >= 568.0f) {
//     splash.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"LaunchImage-568h"]];
//    } else {
//     splash.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"LaunchImage"]];
//    }
    splash.fetchDelay = 5; //开发者可以设置开屏拉取时间,超时则放弃展示
    self.splash =splash;
    
    //根据iPhone设备不同设置不同背景图
    
    UIImage *splashImage = [UIImage imageNamed:@"SplashNormal"];
    if (isIPhoneXSeries()) {
        splashImage = [UIImage imageNamed:@"SplashX"];
    } else if ([UIScreen mainScreen].bounds.size.height == 480) {
        splashImage = [UIImage imageNamed:@"SplashSmall"];
    }
    // * 是否需要开屏视频V+功能

//    self.splash.needZoomOut = self.supportZoomoutViewSwitch.isOn;
    self.splash.backgroundImage = splashImage;
    self.splash.backgroundImage.accessibilityIdentifier = @"splash_ad";

    //
    
    [self.splash loadFullScreenAd];


    

}


#pragma mark - GDTSplashAdDelegate

- (void)splashAdDidLoad:(GDTSplashAd *)splashAd {
//    if (splashAd.splashZoomOutView) {
//        [self.view addSubview:splashAd.splashZoomOutView];
//        splashAd.splashZoomOutView.rootViewController = self;
//        // 支持拖拽
//        [splashAd.splashZoomOutView supportDrag];
//    }
    NSLog(@"%s", __func__);
    
    NSString *text = [NSString stringWithFormat:@"%@ 广告拉取成功", splashAd.adNetworkName];
    
    
    NSLog(@"ecpm:%ld ecpmLevel:%@ text:%@", splashAd.eCPM, splashAd.eCPMLevel,text);
    
    // splash show逻辑
    //设置开屏自定义 logo,展示半屏开屏广告
    
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    [self.splash showFullScreenAdInWindow:window withLogoImage:[UIImage imageNamed:@"img_login_logo"] skipView:nil];

}

- (void)splashAdSuccessPresentScreen:(GDTSplashAd *)splashAd
{
    NSLog(@"%s",__FUNCTION__);
//    self.tipsLabel.text = ;
    NSLog(@"广告展示成功");
    

}



- (void)requestIDFA {
    
    
    if([self isNeedrequestTrackingAuthorization]){
        
        if (@available(iOS 14, *)) {
            [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
              //  授权完成回调
               [self setupGDTSplashAd ];// 先加载loadGDTAd
            }];
              

        } else {
            [self setupGDTSplashAd ];// 先加载loadGDTAd


        }


    }else{
        
        [self setupGDTSplashAd ];// 先加载loadGDTAd

    }
    
    
    
    
}
- (BOOL)isNeedrequestTrackingAuthorization{

    
if (@available(iOS 14, *)) {
        ATTrackingManagerAuthorizationStatus status = ATTrackingManager.trackingAuthorizationStatus;
        switch (status) {
            case ATTrackingManagerAuthorizationStatusDenied:
                NSLog(@"用户拒绝");
                return YES;
                

                break;
            case ATTrackingManagerAuthorizationStatusAuthorized:
                NSLog(@"用户允许");
                break;
            case ATTrackingManagerAuthorizationStatusNotDetermined:
                NSLog(@"用户为做选择或未弹窗");
                return YES;

                break;
            default:
                break;
        }
    } else {
        // Fallback on earlier versions
       if ([ASIdentifierManager.sharedManager isAdvertisingTrackingEnabled]) {
       }else {
          NSLog(@"用户开启了限制广告追踪");
       }
    }
    
    
    return NO;
    
    
}



复制代码

对于用户拒绝授权 UserTracking 的情况,可以考虑接入苹果的 SKAdNetwork 框架进行广告分析。

developer.apple.com/documentati…

    <key>SKAdNetworkItems</key>
    <array>
        <dict>
            <key>SKAdNetworkIdentifier</key>
            <string>f7s53z58qe.skadnetwork</string>
        </dict>
        <dict>
             <key>SKAdNetworkIdentifier</key>
             <string>example200.skadnetwork</string>
        </dict>
    </array>

复制代码

SKAdNetworkIdentifier : f7s53z58qe.skadnetwork 在这里插入图片描述

在这里插入图片描述

3.2 适配极光推送

iOS14.5.1适配【To use the AppTrackingTransparency framework】使用AppTrackingTransparency以请求用户授权获取IDFA信息

———————————————— 版权声明:本文为CSDN博主「#公众号:iOS逆向」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:blog.csdn.net/z929118967/…

注册极光

    [JPUSHService setupWithOption:launchOptions appKey:@"" channel:@"App Store" apsForProduction:YES advertisingIdentifier:[self testIDFA]];

复制代码

获取idfaString

#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <AdSupport/AdSupport.h>

- (NSString*)testIDFA {
    NSString  __block *idfaString = @"";
    
    
    
    if (@available(iOS 14, *)) {
        [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
            if (status == ATTrackingManagerAuthorizationStatusAuthorized) {
                idfaString  = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
                
            }
        }];
    } else {
        // 使用原方式访问 IDFA
        if ([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]) {
             idfaString = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
        }


    }
    NSLog(@"idfaString: %@", idfaString);

    return idfaString;
    
}


复制代码

3.3 上架权限配置

新增开屏广告功能,appstoreconnect后台的app隐私声明,采集数据需要包含idfa,需要添加idfa用于广告,否则会被拒绝。

具体配置:

app主页的隐私收集这次新增了标识符->广告标识符用于第三方广告,未与用户身份关联,会将设备ID用于追踪目的。 在这里插入图片描述

IV 常见问题

4.1 SDK 从4.12.90升级4.13.26之后的适配

-> Installing GDTMobSDK 4.13.26 (was 4.12.90)

2021-11-01 09:55:54.785169+0800 +[GDTSDKConfig enableGPS:]: unrecognized selector sent to class 0x104cf83b8

复制代码

解决方法:直接注释

//    [GDTSDKConfig enableGPS:YES]; // 获取用户的GPS信息,默认值为NO

复制代码

4.2 iOS13适配present半屏的问题

由于本文的广告类型是开屏全屏方式的,所以使用分类将广告控制器GDTSplashImageViewController的modalPresentationStyle设置为UIModalPresentationFullScreen 点击广告之后,如果不是打开第三方app,则会modal到GDTLandingPageWebViewController进行网页的展示,所以最好将GDT开头的控制器都是以UIModalPresentationFullScreen的方式展示。

    if([NSStringFromClass(viewControllerToPresent.class) hasPrefix:@"GDT"])

    {
        
        
        
        
        return UIModalPresentationFullScreen;
        
        
    }

复制代码

使用分类控制modal的样式

- (void)K_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
    if (@available(iOS 13.0, *)) {
        if (viewControllerToPresent.K_automaticallySetModalPresentationStyle) {
            
            
            viewControllerToPresent.modalPresentationStyle = [QCTSession getModalPresentationStyleWith:viewControllerToPresent];
            
            
            
                        

            
        }
        [self K_presentViewController:viewControllerToPresent animated:flag completion:completion];
    } else {
        // Fallback on earlier versions
        [self K_presentViewController:viewControllerToPresent animated:flag completion:completion];
    }
}


复制代码

调试发现广告页对应的控制器是 GDTSplashImageViewController

(lldb) po [QCT_Common getCurrentVC]
<GDTSplashImageViewController: 0x159ea5f70>
superclass:				GDTSplashViewController
title:					(null)
view:					<UIView: 0x159d30d80; frame = (0 0; 414 736); autoresize = W+H; layer = <CAGradientLayer: 0x280f56640>>

复制代码

更多适配细节,请看这篇文章:

blog.csdn.net/z929118967/…

- (NSMutableArray *)FullScreenClasss{
    
    if(_FullScreenClasss == nil){
        
        _FullScreenClasss = [NSMutableArray array];
        
        
        [_FullScreenClasss addObject:@"PGDatePickManager"];
        
        [_FullScreenClasss addObject:@"GDTSplashImageViewController"];

    }
    return _FullScreenClasss;
    
}

复制代码

getCurrentVC的实现

// 获取当前VC
+ (UIViewController *)getCurrentVC
{
    UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
    
    UIViewController *currentVC = [self getCurrentVCFrom:rootViewController];
    
    return currentVC;
}


+ (UIViewController *)getCurrentVCFrom:(UIViewController *)rootVC
{
    UIViewController *currentVC;
    
    if ([rootVC presentedViewController]) {
        // 视图是被presented出来的
        rootVC = [rootVC presentedViewController];
    }
    
    if ([rootVC isKindOfClass:[UITabBarController class]]) {
        // 根视图为UITabBarController
        currentVC = [self getCurrentVCFrom:[(UITabBarController *)rootVC selectedViewController]];
        
    } else if ([rootVC isKindOfClass:[UINavigationController class]]){
        // 根视图为UINavigationController
        currentVC = [self getCurrentVCFrom:[(UINavigationController *)rootVC visibleViewController]];
    } else {
        // 根视图为非导航类
        currentVC = rootVC;
    }
    return currentVC;
}


复制代码

see also

更多内容请关注#小程序:iOS逆向,只为你呈现有价值的信息,专注于移动端技术研究领域。

作者:公众号iOS逆向

分类:
iOS
标签:
分类:
iOS
标签: