iOS SDK常见问题

anwei发表于:2019年12月05日 15:29:22更新于:2019年12月05日 15:34:32

iOS SDK常见问题

GrowingIO SDK无埋点采集的原理是什么?


实现原理:Method Swizzling ( method swizzling 作用为替换或者修改系统方法),可理解为以下 5 个步骤。

  1. 在系统方法M1中插入GIO代码GIO_B1

  2. 系统方法 M1 被执行,此时也会执行 GIO_B1

  3. 在 GIO_B1 内遍历 View Tree / DOM Tree 以及 UIViewController 的层次关系以获取必要的数据

  4. 生成待发送事件并缓存

  5. 发送事件到服务器,完成采集

GrowingIO SDK 支持哪些iOS系统?


目前支持iOS 7-iOS 13。

如果动态添加UIView、删除UIView或者修改UIView在父视图中的位置,会有什么影响?


SDK 依赖 subviews 里面的元素次序。如果有动态的需求,建议在 viewDidLoad 里加载所有可能的 UIView 节点,添加或删除可以通过设置 hidden 属性来实现,把不需要显示的设置 hidden 属性为 YES,把需要显示的设置 hidden 属性为 NO。在 UIView 已经显示之后,不要调用 -bringSubviewToFront:方法, -sendSubviewToBack:方法或 -insertSubview: 方法。

子线程操作UI引起的问题如何处理?


答:GIO SDK 会在主线程中遍历找寻某个subView,如果此时在子线程中删除了该subView,就会造成错乱甚至 crash。此外,Apple 不建议用户在子线程更新 UI。建议客户在 Xcode 中打开 "Main Thread Checker" 检测线程使用是否合理。参考:Main Thread Checker


圈选时,来自不同VC的元素显示的VC名称相同?


这种情况,一般是因为父 VC 添加子 VC 方式不正确引起的,请客户排查子VC的生命周期是否完整。


是否允许何止view的growingAttributesValue为的那个的数字或者字母?


不允许这种设置。出于安全考虑,金融类 App 会自定义键盘(默认键盘容易被黑),如果 SDK 允许采集 V 值为单个数字或字母的点击事件,则有可能会记录用户输入的账号或密码。由于上述原因, SDK 不会发送V 值为单个字母或数字的点击事件,如果用户违反约定,则会导致某个元素只有浏览量而没有点击量。

埋点为什么也要在主线程调用?


埋点采集UI操作,建议在UI线程(主线程)中调用。

是否支持在开启热图的时候圈选?


不支持,开启热图可能会导致被圈选元素的 x 值发生变化。

是否支持用UITouch事件的点击事件?


不支持,建议使用 UITapGestureRecognizer

GIO SDK是否支持Swift项目?

支持

App做了Buttom防重复点击,集成SDK后发现按钮无法点击?


请使用GrowingIO提供的方重复点击解决方式:DJRepeatClickFilter

如果项目中使用了Firebase SDK,需要注意什么?


如果您的iOS项目中集成了Firebase SDK,请确保使用的Firebase SDK版本在4.8.1及以上,否则会造成数据采集不到的情况。

关于苹果隐私政策相关事宜的公告


亲爱的客户:

您好!

从2018年10月3日开始,App Store Connect将要求所有新应用和应用更新版本时提供隐私政策,添加后才可以在App Store上提交或通过TestFlight外部测试进行分发。

【苹果通知:As a reminder, in June the App Store Review Guidelines were updated to require a privacy policy for all new apps and app updates as part of the app review process. Starting October 3, 2018, App Store Connect will require a privacy policy for all new apps and app updates before they can be submitted for distribution on the App Store or through TestFlight external testing. In addition, your app’s privacy policy link or text will only be editable when you submit a new version of your app.(详情可参见:https://developer.apple.com/news/?id=08312018a

所以,在此提醒各位开发者:提交App Store 审核前一定要准备自己的隐私权政策,并在app SafariViewContoller中弹出,否则会无法通过审核哦!如需要专业的法律意见,还请各位开发者小伙伴咨询您的律师或法律顾问哦!

Webview crash

如果遇到此类崩溃:Cannot form weak reference to instance (xxxxx) of class xxxxxx. It is possible that this object was over-released, or is in the process of deallocation 或者程序卡死通过 bt 打印出的堆栈含有weak_register_no_lock并且错误是关于UIWebView+Growing的。 解释如下: 由于业务需要我们会 hook UIWebViewsetDelegate 方法 拿到传入的对象从而进行对UIWebViewDelegate 一系列方法的监听,并且对传入的对象实现 weak 处理,这样做是为了保证不影响客户对象的引用计数; 由于苹果 api 的不完善 UIWebViewDelegate 的声明至今为assign,所以delegate对象在释放后不会被置为nil;由此可能会造成的后果是setDelegate方法调用时,如果传入的是一个over-released, or is in the process of deallocation 的对象而我们 SDK 又对此对象进行了 weak 处理,从而导致崩溃;

由于苹果api没有判断对象是否是over-released, or is in the process of deallocation的 api ,所以我们SDK暂时无法处理。需要手动解决一下,解决方式很简单,先分享一个发生崩溃的案例:

案例如下: 这是客户UIWebViewDelegate对象中dealloc方法


- (void)dealloc
{    
     self.webView.delegate = nil;
}

上面的代码引发了崩溃,很多人奇怪delegate不是已经设置为nil了么? 为什么还崩,请看下面是如何解决的


- (void)dealloc
{    
    _webView.delegate = nil;
}


发现差异了么?我们没有调用的webViewget方法,因为在此案例中webViewget方法会提前执行一遍setDelegate方法从而导致崩溃。 所以希望您在delegate对象的dealloc方法中不要调用webViewget方法即self.webView,而是采用_webView的形式把delegate设置为nil

在实际开发中,除了上述场景之外,还有如下两种情况:

  • UIWebViewDelegate 对象是自定义的 UIWebView 本身,通过上述方式会发现在自定义的 UIWebView 类中无法通过下划线的方式来访问 delegate 属性,即 _delegate,因此也就无法调用 _delegate = nil;解决方式如下:


-(void)dealloc 
{        
        @autoreleasepool {        
        self.growingAttributesDonotTrack = YES;    
     }    
     self.delegate = nil;
}

如果在其他的第三方SDK中发生了这样的问题,但是无法修改其源码的情况下,可以按照如下方式进行处理,首先根据崩溃信息找到崩溃产生的类名信息,例如


Cannot form weak reference to instance (0xXXXXX) of class NSKVONotifying_XXUIWebView

XXUIWebView 即为自定义的 UIWebView 类, 此时可以新增一个UIView的分类,具体实现代码如下,特别需要注意的是 Class clazz = NSClassFromString(@"XXUIWebView");


#import "UIView+Growing_fix.h"
#import <objc/runtime.h>
#import "Growing.h"
@implementation UIView (Growing_fix)
+(void)load {    
       static dispatch_once_t onceToken;    
       dispatch_once(&onceToken, ^{        
           Class clazz = NSClassFromString(@"XXUIWebView");                
            SEL originalSelector = NSSelectorFromString(@"dealloc");             
            SEL swizzledSelector = @selector(grow_dealloc);                
            Method originalMethod = class_getInstanceMethod(clazz, originalSelector);       
            Method swizzledMethod = class_getInstanceMethod(clazz, swizzledSelector);               
         
            BOOL didAddMethod =        
            class_addMethod(clazz,                        
                            originalSelector,                        
                            method_getImplementation(swizzledMethod),                       
                            method_getTypeEncoding(swizzledMethod));                
            if (didAddMethod) {            
                 class_replaceMethod(clazz,                               
                                     swizzledSelector,                                
                                     method_getImplementation(originalMethod),                                
                                     method_getTypeEncoding(originalMethod));        
           } else{            
                  method_exchangeImplementations(originalMethod, swizzledMethod);        
           }  
             
      });
}

- (void)grow_dealloc
{    
     @autoreleasepool {        
          self.growingAttributeDonotTrack = YES;    
     }    
     [self grow_dealloc];
}
@end