日期: 2021 年 8 月 13 日

iOS 14上全面屏iPhone的状态栏高度

GitHub地址

问题
有些情况下,我们需要获取屏幕的状态栏高度,很多人使用类似下面的宏:

#define StatusBar_HEIGHT (DEVICE_IS_FULL_DISPLAY ? 44.: 20.)
然而从iOS 14开始,全面屏iPhone的状态栏高度不一定是44了,比如下面就是这些设备在iOS 14.1上的状态栏高度。

设备 状态栏高度
iPhone XR/11 48
iPhone X/11 Pro/ 11 Pro Max/12 mini 44
iPhone 12/12 Pro/Pro Max 47
解决办法
根据不同系统,通过相应方法获取状态栏高度。

+ (CGFloat)statusBarHeight {
CGFloat statusBarHeight = 0;
if (@available(iOS 13.0, *)) {
statusBarHeight = [UIApplication sharedApplication].windows.firstObject.windowScene.statusBarManager.statusBarFrame.size.height;
} else {
statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;
}
return statusBarHeight;

 

iOS组件化方案对比

GitHub地址
背景
随着公司业务的不断发展,项目的功能越来越复杂,各个业务代码耦合也越来越多,代码量也是急剧增加,传统的MVC或者MVVM架构已经无法高效的管理工程代码,因此需要用一种技术来更好地管理工程,而组件化(也可称为模块化)是一种能够解决代码耦合的技术。项目经过组件化的拆分,不仅可以解决代码耦合的问题,还可以增强代码的复用性,工程的易管理性等等。

市场上的方案:
方案一、url-block
这是蘑菇街中应用的一种页面间调用的方式,通过在启动时注册组件提供的服务,把调用组件使用的`url`和组件提供的服务`block`对应起来,保存到内存中。在使用组件的服务时,通过`url`找到对应的`block`,然后获取服务。

下图是`url-block`的架构图:

%title插图%num

注册:

[MGJRouter registerURLPattern:@”mgj://detail?id=:id” toHandler:^(NSDictionary *routerParameters) {
NSNumber *id = routerParameters[@”id”];
// create view controller with id
// push view controller
}];
调用:

[MGJRouter openURL:@”mgj://detail?id=404″]
优点:

h5外跳到蘑菇街app,app内部的h5和app的原生交互,都可以直接使用这些定义好的路由。

蘑菇街为了统一iOS和Android的平台差异性,专门用后台来管理url,然后针对不同的平台,生成不同类型的文件,来方便使用。

缺点:

需要在内存中维护url-block的表,组件多了可能会有内存问题。

url的参数传递受到限制,只能传递常规的字符串参数,无法传递非常规参数,如UIImage、NSData等类型。

没有区分本地调用和远程调用的情况,尤其是远程调用,会因为url参数受限,导致一些功能受限。

组件本身依赖了中间件,且分散注册使的耦合较多。

url注册对于实施组件化是完全没有必要的,查找 URL 的实现不够高效。

路由写错后编译没问题,而实现运行就出问题了,以后维护也不方便。

方案二、protocol-class
针对方案一的问题,蘑菇街又提出了另一种组件化的方案,就是通过protocol定义服务接口,组件通过实现该接口来提供接口定义的服务,具体实现就是把protocol和class做一个映射,同时在内存中保存一张映射表,使用的时候,就通过protocol找到对应的class来获取需要的服务。

下图是protocol-class的架构图:

%title插图%num

注册:

[ModuleManager registerClass:ClassA forProtocol:ProtocolA]
调用:

[ModuleManager classForProtocol:ProtocolA]
优点:

蘑菇街的这种方案确实解决了方案一中无法传递非常规参数的问题。

组件间的调用更为方便。

解耦代码量少,实现方便,以后维护也方便。

协议方法改变后,编译就会报错,避免代码修改遗漏。

协议方法未实现的话,会报编译警告。

方法查找容易,调用高效。

缺点:

组件的方法调用分散。

内存中维护映射表。

协议方法有可能未实现。

对组件协议需要注册,不注册就无法调用。

方案三、url-controller
这是LDBusMediator的组件化方案,它是通过组件实现公共协议的服务,来对外提供服务。具体就是通过单例来维护url-controller的映射关系表,根据调用者的url,以及提供的参数(字典类型,所以参数类型不受约束)来返回对应的controller来提供服务;同时,为了增强组件提供服务的多样性,又通过服务协议定义了其它的服务。

下图是LDBusMediator的组件化架构图:

%title插图%num

优点:

LDBusMediator解决了蘑菇街的这两种组件化方案的不足,比如:通过注册封装件connector而不是block来降低了内存占用。

通过字典传递参数,解决了url参数的限制性。

缺点:

内存中维护映射表。

组件本身依赖了中间件,且分散注册使的耦合较多。

url注册对于实施组件化是完全没有必要的,查找 URL 的实现不够高效。

路由写错后编译没问题,而实现运行就出问题了,以后维护也不方便。

方案四、target-action
通过给组件包装一层wrapper来给外界提供服务,然后调用者通过依赖中间件来使用服务;其中,中间件是通过runtime来调用组件的服务,是真正意义上的解耦,也是该方案*核心的地方。具体实施过程是给组件封装一层target对象来对外提供服务,不会对原来组件造成入侵;然后,通过实现中间件的category来提供服务给调用者,这样使用者只需要依赖中间件,而组件则不需要依赖中间件。

下图是CTMediator的组件化方案架构图:

%title插图%num

下面代码来自CTMediator的组件化demo

CTMediator分类

// CTMediator+CTMediatorModuleAActions.h

– (UIViewController *)CTMediator_viewControllerForDetail;

// CTMediator+CTMediatorModuleAActions.m

– (UIViewController *)CTMediator_viewControllerForDetail
{
return [self performTarget:@”TargetA” action:@”nativeFetchDetailViewController” params:@{@”key”:@”value”} shouldCacheTarget:NO];
}
target

A组件

// Target_TargetA.h

– (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params;
调用

// ViewController.h
#import “CTMediator+CTMediatorModuleAActions.h”

[self presentViewController:[[CTMediator sharedInstance] CTMediator_viewControllerForDetail] animated:YES completion:nil];
这就要求target类名要加前缀Target_,方法名要加前缀Action_,同时必须要有并且只有一个参数,参数类型必须是字典NSDictionary,还必须要要有返回参数,返回参数必须是指针类型,不能为int、float等C语言类型。

优点:

内存中不需要维护映射表。

不需要注册。

缺点:

中间件实现繁琐。

中间件方法类型、参数、返回值不够灵活。

中间件方法内部使用字符串来调用方法和类,调用死板。

中间件方法内部容易写错,写错后编译没问题,而实现运行就出问题了,以后维护也不方便。

方案五、Extension和Category结合使用
东方财富浪客直播的不同组件控制器跳转使用了这种方式,这个方式是我以前想出来的。在基础组件定义一个UIViewController 的Extension和Category,Extension里面封装各种组件间控制器跳转的调用方法,这里面没有实现,Category里面也和Extension对应封装相应方法,Category有实现,内部是调用的Extension封装的方法,调用时先判断Extension的方法有没有实现,做下保护。组件控制器跳转调用Category封装的方法。组件负责实现Extension定义的方法。

当然这种方案不只适用于控制器跳转,还可以改造成NSObject 的Extension和Category,适应所有的组件间通信。

Extension方法定义:

@interface UIViewController (EL)

/** 发直播 **/
– (void)el_startPushLive:(NSString *_Nullable)liveTitle;
Category方法定义:

@interface UIViewController (elbasic)

/** 发起直播 **/
– (void)elbasic_startPushLive:(NSString *_Nullable)liveTitle;
Category方法实现:

@implementation UIViewController (elbasic)
/** 发起直播 **/
– (void)elbasic_startPushLive:(NSString *_Nullable)liveTitle {
SEL sel = @selector(el_startPushLive:);
if ([self respondsToSelector:sel]) {
[self el_startPushLive:liveTitle];
}
}
Extension方法实现:

@implementation UIViewController ()

//发直播
– (void)el_startPushLive:(NSString *_Nullable)liveTitle {
UIViewController *pCtrl = [SBURLActionsb_initCtrl:el_actionurl_push_live(liveTitle)];
[self el_presentPushCtrl:pCtrl];
}
优点:

Category实现内部做了Extension方法是否实现的判断,避免了不实现出现的崩溃

确实解决了方案一中无法传递非常规参数的问题

组件间的调用方便。

协议方法改变后,编译就会报错,避免代码修改遗漏。

内存中不需要维护映射表。

缺点:

Extension和Category都定义了同样的方法,相比protocol-class方案不够简洁。

方法为了避免冲突,都要加前缀。

总结:
组件化是项目架构层面的技术,不是所有项目都适合组件化,组件化一般针对的是大中型的项目,并且是多人开发。如果,项目比较小,开发人员比较少,确实不太适合组件化,因为这时的组件化可能带来的不是便捷,而是增加了开发的工作量。另外,组件化过程也要考虑团队的情况,总之,根据目前项目的情况作出*合适的技术选型。没有*好的技术,只有*合适的技术。

ios appstore 上架应用被拒*原因

ios appstore 上架应用被拒*原因

应用程序崩溃

界面布局有明显错误
挂羊头卖狗头的应用
包括未公开的或隐藏功能的
使用私有API
应用程序读取或写入数据超出其指定的容器区域
以任何方式下载代码的应用
安装或启动其他可执行代码的应用
“试用”,“演示”,“试用”,或“测试”版本
iPhone应用程序也必须不加修改地运行在iPad,iPhone分辨率和2倍iPhone 3GS的分辨率
山寨或者重复的应用(App Store中已经存在有相同功能的应用程序)
应用程序必须有特定用途,不能为用户提供持久娱乐价值可能会被拒*
主要是市场推广或广告的应用程序
虚假功能,没有明确目的
应用程序在蜂窝网络环境下载大于20MB的数据(自动被禁止在App Store)
多任务的应用程序可能只使用与后台服务,一般只提供给:网络电话,音频播放等app使用,任务完成后,需要本地通知。
浏览网页,必须使用iOS WebKit框架和WebKit JavaScript
鼓励过量饮酒或非法物质,或鼓励未成年人饮酒或吸香烟的应用程序
应用程序提供了不正确的诊断或其他设备的数据不准确,
开发“垃圾邮件”等类似的
应用程序的数据中提到的其他任何移动平台的名称
带有占位符文本的应用程序
不相关应用程序内容和功能的描述
在iTunes Connect的应用程序名称和显示的设备上应该是相似的
应该是相似的小的和大的应用程序图标
应用程序与应用程序的图标和屏幕截图,不支持4 +年龄评级
应用程序类别和类型的选择是不恰当的应用程序内容
开发人员负责分配适当的评级给应用程序。不适当的评级可能会被苹果改变
开发他们的应用程序负责分配适当的关键字。不适当的关键字可能会被苹果 更改/删除
开发人员试图操纵或欺骗用户的评论或图表排名在App Store虚假或付费评论,或任何其他不适当的方法
应用程序不通知,并获得用户的同意,然后收集,传输,或使用位置数据
使用基于位置的API,用于自动或自主控制的车辆,飞机或其他设备的应用
应用程序使用基于位置的API,用于调度,车队管理,紧急服务
提供推送通知的应用程序,而无需使用苹果推送通知(APN)API
使用APN服务的应用程序,没有从苹果获得推送应用程序ID
没有首先获得用户同意发送推送通知的应用程序,
发送敏感的个人或机密信息的使用推送通知的应用程序,
使用推送通知的应用程序,发送不请自来的邮件或网络钓鱼或垃圾邮件的目的,
应用程序不能使用推送通知发送广告,促销或任何形式的直接营销
使用推送通知的应用程序不能向用户收费
过度使用网络容量或带宽的APN服务或造成过重的负担的设备推送通知的应用程序
传送病毒,文件,计算机代码或程序,可能会损害或破坏的APN服务的正常运行的应用程序,
*终用户或任何第三方应用程序显示任何玩家ID会被拒*
应用程序用于任何用途以外的其他游戏中心条款批准的使用玩家的ID
从iOS开发者计划的开发人员尝试反向查找,跟踪,关联,关联,矿山,收获,或以其他方式利用玩家的ID,别名或通过游戏中心获得的其他信息将被删除
游戏中心的信息,如排行榜成绩,只可用于在应用程序批准使用游戏中心
游戏中心服务的应用程序,使用发送不请自来的邮件或网络钓鱼或垃圾邮件的目的
过度使用网络容量或带宽的游戏中心的应用
传送病毒,文件,计算机代码或程序,可能会损害或破坏游戏中心服务的正常运行的应用程序,
“应用程序,人为地增加展示次数或点击的广告
应用程序包含空iAd的横幅
应用程序的设计主要是为广告的显示
应用程序必须遵守的所有条款和条件的解释中使用苹果商标和版权的的http://www.apple.com/legal/trademark/guidelinesfor3rdparties.html”>指引 和 http://www.apple.com/legal/trademark/appletmlist.html 苹果商标列表
应用程序建议或推断,苹果是源代码还是供应商的应用程序,或者,苹果表示赞同任何特定的质量或功能
现有的苹果产品或广告主题出现混淆性相似的应用程序
应用程序拼错苹果产品的名称,在他们的应用程序的名称(例如,GPS ,iPhone,iTunz)
使用受保护的第三方材料(商标,版权,商业秘密,,否则专有的内容),需要检查必须要求提供文件的权利
谷歌地图和谷歌地球获得的图像通过谷歌地图API可用于在应用程序中,如果所有的品牌特征的原始内容保持不变,完全可见。覆盖或修改谷歌标志或版权持有人识别的应用程序
不使用MediaPlayer框架访问媒体在音乐库中的应用程序
模仿任何iPod界面的应用程序的用户界面,
蜂窝网络音频流媒体内容,不得使用超过5分钟,超过5MB
通过蜂窝网络的时间超过10分钟的视频流媒体内容必须使用HTTP实时流媒体和包括一个基线64 kbps的只有音频的HTTP直播流
应用程序必须遵守所有条款和条件解释< iPhone人机接口指南</ A>和<a href=”/library/ios/documentation/General/Conceptual/iPadHIG/Introduction/Introduction.html”>苹果iPad的人机界面指南</ A>
类似于捆绑在iPhone上的应用程序,包括在App Store,iTunes商店和iBookstore的应用程序,
在<a描述的应用程序不使用系统提供的资料,如按钮和图标,正确和iPhone人机接口指南</ A>和<a href=”/library/ios/documentation/General/Conceptual/iPadHIG/Introduction/Introduction.html”>苹果iPad的人机界面指南“</ a>也被拒*
应用程序创建备用桌面/主屏幕环境或者模拟多应用程序的部件经验
应用程序改变的的标准按键功能,如音量上/下和响铃/静音开关,
苹果公司和我们的客户将高价值的,简单的,精致的,创造性的,以及想通过接口。他们采取更多的工作,但都是值得的。苹果公司设置了很高的门槛。如果您的用户界面是复杂的,或小于很好的,它可能会被拒*
解锁或启用额外功能或功能,在App Store以外的其他机制的应用程序
应用程序使用的In App Purchase API(IAP)以外的系统购买内容,功能或服务在一个应用程序会被拒*
使用IAP购买实物产品或商品和服务的应用程序之外的应用程序
应用程序使用IAP购买积分或其他货币的,必须在应用程序内消费贷
应用程序使用IAP购买积分或其他货币,到期
内容订阅,使用IAP必须持续至少30天,并从他们的iOS设备提供给用户
应用程序使用IAP购买物品的,必须指定正确的Purchasability类型
应用程序使用IAP购买访问内置的功能,如相机或陀螺仪内部监督办公室的,
应用程序含有“出租”内容或服务到期后,在有限的时间内
保险应用程序必须是免费的,在法律遵守的地区分布,而不能使用IAP
在一般情况下,您的应用程序更昂贵的,更彻底,我们会检讨
刮任何信息的应用程序,从苹果公司的网站(例如从<a href=”http://apple.com”> apple.com </ A>,iTunes的应用程序商店,商店,iTunes的连接,苹果开发者程序等)从苹果公司的网站和服务内容创建的排名
应用程序可以使用批准的苹果RSS饲料,如iTunes Store的RSS订阅
应用程序是网络的剪报,内容集成商,或链接的集合,可能会被拒*
鼓励用户使用苹果设备的方式,可能会导致设备损坏的应用
设备的电池很快耗尽的应用程序或产生过多的热量会被拒*
任何应用程序,是诽谤,攻击性的,卑鄙的,或可能危害方式将目标的个人或团体
专业的政治讽刺和幽默诙谐的可被豁免从进攻或心胸狭窄的评论禁止
应用程序展示:杀害或致残,枪击,刺伤,拷打或受伤的人或动物的描绘逼真的图像
描绘暴力或虐待儿童的应用程序
包含“虚拟敌人”的游戏,不能只针对一个特定的种族,文化,一个真正的政府或公司,或任何其他的实体
应用程序逼真的描绘,鼓励使用某种武器
包括俄罗斯轮盘赌的游戏
呈现过于令人反感的内容的,
扰乱或厌恶用户的应用程序,
应用程序不能没有获得用户的许可之前,为用户提供获取信息的方式和位置数据将被用于传输用户的数据
应用程序需要用户共享的个人信息,如电子邮件地址和出生日期,以功能
应用程序目标的数据收集未成年人
含有色。情内容的材料,“描述或显示性 。器 官或活动,旨在刺激,而不是审美情感和情绪的色 。情”
应用程序包含用户生成的内容,经常。色 。情(如“聊天。轮盘”的应用程序)
应用程序包含引用或评论有关宗教,文化或族群,诽谤,攻击,心胸狭窄,容易暴露目标群体伤害或暴力
应用程序可以包含或引用宗教文本提供的报价或翻译准确且无误导成份。评论应该是教育或信息,而不是炎症
抽*和竞赛必须由应用程序开发者/公司
抽*和竞赛的正式规则,必须在应用程序中的,并明确表示,苹果是不是保荐人或以任何方式参与的活动
它必须是法律允许的情况下运行的彩票应用程序的开发人员,和彩票的应用程序必须具有以下特点:考虑的机会,并颁发
应用程序,允许用户在应用程序中直接购买彩票或*券
应用程序,其中包括可以使认可慈善机构的捐款,必须是自由的
收集的捐款必须通过一个网站,在Safari或SMS
应用程序必须符合所有法律要求的任何位置,他们向用户提供。这是开发商的责任了解和遵守当地的所有相关法律法规
应用程序包含虚假,欺诈或误导性陈述,
应用程序,征集,推广,或鼓励犯罪或明显鲁莽行为
,使非法文件共享的应用程序
应用程序是专为用作非法赌博辅助工具,包括卡柜台,
应用程序,使匿名或恶作剧电话或SMS / MMS消息
开发人员创建的应用程序的人,偷偷摸摸地试图发现用户密码或其他用户的私人数据将被删除从iOS开发者计划

 

 

补充:

APP被苹果APPStore拒*的各种原因

 

1、程序有重大bug,程序不能启动,或者中途退出。

 

2、绕过苹果的付费渠道,我们之前游戏里的用兑换码兑换金币。

 

3、游戏里有实物*励的话,一定要说清楚,*励由本公司负责,和苹果没有关系。

 

4、用到苹果的标志。(应用的设计和Apple的Logo风格太像了也会被拒)

 

5、网络功能不能正常访问。

 

6、图标不能点击,不能点击的图标要置灰,或者直接隐藏。

 

7、没有设置default页,启动画面为黑屏,有一定概率被拒*。

 

8、一个应用在线,但你想在发一个豪华版之类的,再开一个应用也会被拒*。

 

9、用了著名游戏的关键字,比如说“愤怒的小鸟”之类的。

 

10、iPhone版不支持480*320分辨率被决*。

 

11、娱乐分类App,拒*理由:我们认为你的App不具有娱乐性。

 

12、你的软件不符合中国法律,还打电话来跟我说。(奶奶的,中国哪个法律说不可以fanqiang。)

 

13、技术支持地址写的微博地址,于是被拒*了。原因是:不能将需要登陆才能访问的网址作为技术支持地址。

 

14、说我们的应用不该用App开发,应该用HTML5。(而且还很热心的给出了参考链接哦)

 

15、调用截屏功能,当时没注意,是私有API,被拒.改成非私有API实现截屏功能就通过了.

 

16、开放了文件document分享功能(Application supports iTunes file sharing),被退回,理由:不需要分享,为何开放了.回复邮件说

明理由后上线.

 

17、年龄设置太低,说是有成人内容,被拒.修改内容后上线.

 

18、同一软件多个版本只是针对不同的国家和内置的语言不同.前面几个上线,后面几个被拒.让改成一个程序做程序内下载资源.

 

19、程序有crash被拒.修改bug后上线.

 

20、原因是我们的一个软件只允许联通用户验证真实身份并发布信息。apple要求要么放弃验证,要么允许移动和电信用户验证。

 

21、APP中出现了某本以乔布斯为封面的本集团出版物图片,遭拒。回复曰:Thank you for submitting your app commemorating

Steve Jobs.

 

We appreciate your efforts honoring Steve. However, we’ve decided to honor his life in other ways and we are not

accepting these types of apps.

 

22、原来做过一个显示假电池的App, 就是显示一个假的的电量, 这样别人找你借手机就可以以没电了为理由不借给他. 结果App Store说

会迷惑用户, 让我改简介. 改为简介后苹果中国给我打电话说这个App不用再提交了, 不可能过, 会迷惑用户… 我了个去的 于是这个App就一直存在在我的iTunes Connect里.

 

23、忘记提供测试账号。(如果你的APP有登录功能)

 

24、软件有个vip功能,涉嫌应用内收费。

 

25、拨打电话,涉嫌扣费。

 

26、图片如果比较清凉的话需要设置软件级别。

 

27、平板应用要支持*少2个翻转方向。

 

28、程序内按钮设计成标准的iOS icon。

 

29、内置付费的内容要明确告诉需要购买,购买前不能摆出来(这个属于运气不好,同样这么做的app很多)

 

30、提交旗下一款新应用,为统一旗下各个产品的品牌识别,采取了与上一款产品相似的色调和logo结构。苹果拒*理由是不知道这两款产

品有什么区别(其实区别点进去就很明显,连UI布局都大相径庭)。

 

31、应用内涉及到抽*的运营活动,未声明与苹果官方无关。

 

33、自认为*版产品还不够完善,于是过度谦虚地在启动画面加上了“beta”字样。苹果的反馈是,不允许测试版产品上架。囧,后来翻了一下 Review Guideline 似乎是有提到这点的。

 

34、链接堆砌,苹果建议用HTML5来做

 

35、UIWebView 嵌页面片,苹果建议让用户通过浏览器访问,比如爱知乎,开始几个版本通过了,后面的更新一直没提交上去屏幕坏点检测应用,苹果说会误导用户,未上架

 

36、登陆账号有两个字母写反了,改正后通过

 

37、因远程服务器反应慢,苹果的人估计没耐心等,就说有bug给拒了,重新提交一份通过

 

38、应用标题加了几个描述语句,苹果让在描述中写明达到此目的的操作流程,改正后通过

 

39、一个第三方的应用,描述文字中有该网站的链接,进去网站后能找到付费链接,苹果说该网站有自己的支付方式,去除网址后通过

 

40、应用请求使用地理位置的权限,但相关功能藏得比较深,Apple说没找到相关的功能。 — 回信说明后通过

 

41、应用描述中提到了是全平台应用,可以和其他系统同步数据。因为提到了“Android”的字眼被拒。 — 修改描述后通过

 

42、 App 的User Agreement中出现了 Beta、Preliminary 等字样。因为是发布*个版本,产品带有一定的beta性质,但是确实是可

发布产品了。按照公司LEGAL部门(外企,对User Agreement要求严格)的要求,必须有类似Beta的说明告知用户,而 Apple 是明确不允

许有 Beta 性质的字样的。

 

43、App 是一个机遇小区的社区工具,顺便做了房产广告,但是 apple 审核说是广告app,不允许发布。

 

44、应用里有个去给我们打分的功能,被拒了。。(这是不是史上*坑爹的拒*理由)

 

45、网络工具软件,要求支持国外的电信运营商网络。

 

46、曾经做一个 Chinajoy 美女图片 show 的,被认为不适合放在 App Store。

 

47、iOS 5 的数据存储问题…网络下载的资源不能直接搞在Documents目录下。这个反复整了5次左右才通过审核。

 

48、忘记提供测试账号……(审核的大爷们你们就不能自己弄个或者注册个账号么)

 

49、还有一次被拒,我回复说你们所说的问题不存在……后来……通过审核了。-.-

 

50、菜单中有一个文字包含测试,被打回,说不能上测试版本。

 

51、app名字包含pad,被打回,说容易名字不能有pad,会被误解为苹果自己的应用,不过同名的iphone版本就通过了…

 

52、upport地址在safari(仅仅是safari)下由于适配性问题打不开,结果就收到了apple的来信。

 

53、app里做了次抽*,*品是iphone4s,结果他们必须要我们将*品改成别的,同时申明此活动与苹果公司无关,才可以上架。。。

 

54、app里做了次抽*,*品是iphone4s,结果他们必须要我们将*品改成别的,同时申明此活动与苹果公司无关,才可以上架。。。

 

55、在程序的说明信息中有“越狱”俩字,被拒,后来把这俩字去了,PASS。但是:如果我在程序运行中检测出手机越狱了,这结果报出来

让不让过?

 

56、使用GPS常驻后台服务,要我给出一个合适的理由,否则不往下审核,还好,解释一翻过去了。

 

57、出现“给我们五星好评”之类的文字。

 

58、应用内提到付费项目但木有通过苹果付费渠道(妄图不让苹果老大赚钱)

 

59、问我服务是不是只在IOS平台,还是同时支持多平台(我当然拥护苹果老大的领导,木有其他平台,iPhone专属)

 

60、地图应用,Google大神的LOGO没有显示。

 

61、google地图下面logo被一个UIView给遮挡了被拒。调整位置后ok

 

62、对不存在普遍比较标准的几类人进行比较和评判。不比人比动物后ok

 

63、上传通讯录没有通知。这个等了几个月,苹果的法律纠纷差不多了之后拒掉。然后加了提示后ok。

 

64、我写的英文App介绍审核人员看不懂,被拒。后然直接用中文。唉。

 

65、因为上行短信实现用户认证被拒

 

66、果测试人员的手机号在国外,因为收不到国内短信,被拒

 

67、他们打开我的一个APP测试,显示空白无内容,哈哈,因为他们相册里没有960*640的图,就说我该程序功能没开发完毕吧?然后我回了信,然后第二天就上架了。虽然这个小应用是*其简单的。

68、*蛋疼的是,提供了测试用户帐号密码,被我们某个手贱的测试人员测试修改密码的时候给改掉了。。。于是苹果登录不进来,于是杯具

69、IAP价格不能超过99美金(其中有一个是99.99美元,你妹啊!!!!!!!你麻痹多出0.99美金啊!!人家游戏都可以有啊!!!!为什么我们就不行啊!!!)

70、不能强迫用户注册(网络游戏啊亲!!你妹有直接登陆的按钮啊!!!不需要注册的啊!!试一下会shi啊!!!!)

 

71、support URL无法访问(草草草草草!!!那几天GFW抽风,海外访问国内网站有问题,你妹躲在香港的体谅一下大陆人民啊!!!)

72、没有生成Paid iOS合同(游戏内含IAP,好吧,这个是合作公司搞的乌龙,就不吐槽苹果了)

73、某微博客户端,提供了测试账号,被拒的理由是「账号登录不进去」,还有截屏,我一看,这不把我们提供的测试账号的*后两个字母写反了么…赶紧反馈,之后三天才继续审核,这三天真是太赔了。

74、某门户新闻客户端,免费应用,被拒的理由是「找不到in app purchase的项目对应的界面」,可是我们的应用跟本没有应用内支付,继续反馈…又等了三天…

 

75、iPhone Human Interface Guidelines中指出了基本UI控件应该如何正确地使用。我们应该按照它的要求来检查Tab Bar、Navigation Bar和Alert View等控件是否符合Apple的口味。

76、确保应用不要模仿设备预装应用的样式和功能,如Music、iTunes Music Store和App Store等。这一条规则似乎是选择性实施的,因为许多浏览器和指南针类的应用都得到了批准。

77、不要加入过多脏话、无理由的性 爱场面和吸毒场面。

78、不要对硬件按钮进行重新编程,赋予其它功能。例如,不要将音量按钮作为照相应用的快门键,不要将home键作为游戏中的开火键。

你的iOS-App启动为什么缓慢

基本大纲

  1. 应用的启动分为Pre-main和mian两部分
  2. 在Pre-main中,可以大致分为load dylib->rebase->bind->Objc setup-> initializer,开发能掌握和度量的是initializer部分
  3. 在开发阶段(Xcode)如何查看启动的每个阶段的时间—通过在Xcode中,设置Edit Scheme -> Run -> Argument汇总的环境变量,会在console中输出
  4. 在应用上线后,统计Pre-mian的使用时间。利用的在加载动态库的一个顺序机制,定制自己的动态库,让他在*个被加载,并在load函数中hook住所有可执行文件,然后统计出*终的每一个的时间,得到*后的时间(后续文章具体讲述)
  5. Class Load 和 Static Initializers(+(void)load; + (void)initialize; 后续文章)
  6. Xcode For Static Initializer

Apple建议

Apple suggest to aim for a total app launch time of under 400ms and you must do it in less than 20 seconds or the system will kill your app.

Apple建议应用的启动时间控制在400ms之下。并且必须在20s以内完成启动,否则系统则会kill掉应用程序。那么话说我们如何知道app的在启动到调用main()方法之前的时间呢?在WWDC 2016的提到了这方面的信息。

Pre-main时间

从在屏幕上点击你的app icon开始,到应用执行到main()方法或者执行到applicationWillFinishLaunching的过程中,app执行了一系列的操作。在iOS10之前的系统中,我们无处得知其中的细节。而在iOS10系统中,可以通过简单在Xcode设置,在控制台就可以打印出Pre-main的具体信息细节。

通过Xcode中的Edit Scheme -> Run -> Argument,设置参数DYLD_PRINT_STATISTICS值为1

1626952-7a391be36110c8bf.png

设置DYLD_PRINT_STATISTICS

这里使用的Objective-C项目,iPad Air2,系统iOS10.3

1
2
3
4
5
6
7
8
Total pre-main time:   74.37  milliseconds ( 100.0 %)
    dylib loading time:   41.05  milliseconds ( 55.2 %)
   rebase/binding time:    8.10  milliseconds ( 10.9 %)
       ObjC setup time:    9.87  milliseconds ( 13.2 %)
      initializer time:   15.23  milliseconds ( 20.4 %)
      slowest intializers :
        libSystem.B.dylib :    6.58  milliseconds ( 8.8 %)
libBack trace Recording.dylib :    6.27  milliseconds ( 8.4 %)

上文中可以看出总共消耗的时间为74.37ms。

  • dylib loading time 载入动态库,这个过程中,会去装载app使用的动态库,而每一个动态库有它自己的依赖关系,所以会消耗时间去查找和读取。对于Apple提供的的系统动态库,做了高度的优化。而对于开发者定义导入的动态库,则需要在花费更多的时间。Apple官方建议尽量少的使用自定义的动态库,或者考虑合并多个动态库,其中一个建议是当大于6个的时候,则需要考虑合并它们
  • rebase/binding time 重构和绑定,rebase会修正调整处理图像的指针,并且会设置指向绑定(binding)外部的图像指针。所以为了加快rebase/binding,则需要更少的做指针修复。当你的app当中有太多的Objective-C的类,方法选择器,和类别会增加这一部分的启动时间。有一个数据当大于20000个时候,会增加800ms的时间。另一点:当你的app中使用了很少的C++的虚拟函数,使用Swift会更加高效
  • ObjC setup time 在Objective-C的运行时(runtime),需要对类(class),类别(category)进行注册,以及选择器的分配,所以参照rebase/binding time,尽量减少类的数量,可以达到减少这一部分的时间
  • initializer time 这一份指代的是执行+initialize方法的时间。如果你执行了+load方法(不建议),尽量使用+initialize代替。

加载框架使用的时间 – dylib loading time

这里使用一个快速的实验验证加载框架产生的时间变化。这里基于iPad Air2,系统iOS10.3。

1.新建一个Swift项目,并且每一次重启设备,保证没有应用缓存。

1
2
3
4
5
6
7
  Total pre-main time:  408.97  milliseconds ( 100.0 %)
      dylib loading time:  383.84  milliseconds ( 93.8 %)
     rebase/binding time:    7.86  milliseconds ( 1.9 %)
          ObjC setup time:    6.82  milliseconds ( 1.6 %)
         initializer time:   10.36  milliseconds ( 2.5 %)
         slowest intializers :
           libSystem.B.dylib :    2.33  milliseconds ( 0.5 %)

可以看到载入框架的时间在380ms之上,相比于Objective项目增加了很多。我的猜测是由于载入了Swift的dylib。

2.在项目中导入10个外部的dylib(Swift cocoapods)

1
2
3
4
5
6
7
  Total pre-main time:  682.90  milliseconds ( 100.0 %)
   dylib loading time:  631.17  milliseconds ( 92.4 %)
  rebase/binding time:   17.06  milliseconds ( 2.4 %)
      ObjC setup time:   17.47  milliseconds ( 2.5 %)
     initializer time:   17.09  milliseconds ( 2.5 %)
     slowest intializers :
       libSystem.B.dylib :    6.05  milliseconds ( 0.8 %)

由上可知,dylib加载的时间从380ms上升到了630ms,这不是一个很科学的实验,不过也应该意识到加载外部的dylib对加载时间有比较大的影响。

iOS苹果APP启动速度

有很多app的开发者都不重视app的启动速度,这样对于碎片化使用情景的用户来说,简直是毁灭性的灾难。

苹果APP的启动速度
应用启动时,会播放一个放大的动画,在iPhone上是400ms,在iPad上是500ms。*理想的启动速度是,在播放完动画后,用户就可以使用。间隔只有两秒左右为宜1

如果应用启动过慢,用户就会放弃使用,甚至永远都不再回来。抛开代码不谈,如果抱着PC端游和单机游戏的思维,在游戏启动时强加公司Logo,启动动画,并且用户不可跳过,也会使用户的成功使用率大大降低。

启动速度慢得有很多,比方说一些耗时的操作,或是在启动的时候启动了一些访问设备的代码,活着说线程存在问题,主线程的工作量过大,造成主线程阻塞,这样就需要我们迁移一些操作到子线程去实施!

因为设备默认启动时间为十秒左右,如果启动时间超过了十秒,设备会认为这可能是一个问题程序,就可能被系统kill掉,所以说启动时间很重要!尽量不要在Appdelegate里面做过多的操作,耗时的操作尽量去其它页面调用,进入程序以后再去走这个方法,这样会更加省时省力!大家一定要注重APP的启动速度!

python碎片|Python类属性描述

python碎片|Python类属性描述
__getattr__是当类调用一个不存在的属性时才会调用getattr魔法函数,他传入的值item就是你这个调用的不存在的值。
class User(object):
    def __init__(self, name, info):
        self.name = name
        self.info = info
ls = User(“李四”,{“gender”:”male”})
print(ls.info)
运行结果:
{‘gender’: ‘male’}
如果想获得male属性,则需要用到__getattr__魔法方法。
class User(object):
    def __init__(self, name, info):
        self.name = name
        self.info = info
    def __getattr__(self, item):
        return self.info[item]
ls = User(“李四”,{“gender”:”male”})
print(ls.gender)
运行结果:
male
属性描述符是一个强大的通用协议。它是properties, methods, static methods, class methods
和super()的调用原理。
属性描述符是实现了特定协议的类,只要实现了__get__,__set__和__delete__三个方法中的任意一个,这个类就是描述符,它能实现对多个属性运用相同存取逻辑的一种方式,通俗来说就是:创建一个实例,作为另一个类的类属性。
如果一个对象同时定义了__get__和__set__方法,它被称做数据描述符(data descriptor)。
只定义__get__方法的对象则被称为非数据描述符(non-data descriptor)。
使用类方法创建描述符 • 定义一个IntField类为描述符类 • 创建IntField类的实例,作为另一个User类的属性
class User:
    def __init__(self, age):
        self.age = age
    def get_age(self):
        return (str(self.age) + ‘岁’)
    def set_age(self, age):
        if not isinstance(age, int):
            raise TypeError(‘Type Error’)
        self.age = age
tt=User(55)
tt.set_age(60)
print(tt.get_age())
运行结果:
60岁
描述符查找顺序 • 当为数据描述符时, get__优先级高于__dict •
当为非数据描述符时,dict__优先级高于__get

求助,客户端访问数据库怎么指定端口号

现在有两台机器 a 和 b,a 机器上装有基于 c#编写的客户端程序,b 机器上装有 SQL 数据库。b 是政务云服务器,只能接收 ip 下特定端口请求,现在要设定 a 机器通过某一固定端口去访问 b 机器上的数据库,该怎么实现

机器 端口 数据库 装有11 条回复 • 2021-08-13 15:44:51 +08:00
sutra 1
sutra 35 分钟前
你的意思是你的数据库运行在 b 的端口 1 上,但是 a 却访问不了 b 的端口 1,只能访问 b 的端口 2 ?
shenxj 2
shenxj 32 分钟前
@sutra 可以访问,a 服务器只能用特定端口去连数据库,就想请教一下怎么固定端口。。。
sutra 3
sutra 29 分钟前
还是没有明白。
goodryb 4
goodryb 28 分钟前
@shenxj #2 按理来说是发起个 TCP 连接,但正常源端口是系统随机分配的,好像并没有能够指定说一定从某个端口出去。

坐等楼下大佬指导。
moen 5
moen 28 分钟前
如果那个云服务器有 ssh 的话可以用 ssh 转发
goodryb 6
goodryb 26 分钟前
网上搜到这个方法,没试过 https://blog.csdn.net/liangxiaozhang/article/details/8267854
sutra 7
sutra 23 分钟前
TCP Source Port 呀,为啥有这么奇怪的需求。得修改数据库连接驱动程序吧。
NikoXu 8
NikoXu 11 分钟前
这需求确实玄学
strict 9
strict 10 分钟前
搜索关键词: 本地端口转发
strict 10
strict 9 分钟前
@NikoXu 估计是 b 机器的防火墙有个奇葩设置.

keepeye 11
keepeye 9 分钟前
限定源端口,假如只允许一个端口,那岂不是只能单线程连接?

基本统计值的处理总结

基本统计值的处理总结
总个数:len()
求和:for … in
平均值:求和/总个数
方差:个数据与平均值的平方的和的平均值
中位数:排序,然后将奇数找中间一个,偶数找中间2个取平均值
通过以上,就可以获得基本统计值了。
求算这些数据主要是利用函数来是实现。把一个个功能都利用函数,*后在调用函数来实现*终想要的完成的工作。
1.这一段代码主要是获得用户想要输入的数据。
主要利用了input()函数,while语句,eval()函数来实现
#CalStatisticsV1.py
def getNum():
    “””获得用户不定长度的输入”””
    nums = []
    iNumStr = input(“请输入数字:”)
    while iNumStr != “”:
        #如果用户输入的不是数字,或者空字符则跳出循环
        nums.append(eval(iNumStr))
        iNumStr = input(“请输入数字:”)
    return nums  #*后将用户输入的每一个数字都返回给列表,作为列表中的一个元素,这样子就是把返回给函数,作为用户输入的数据。
2.这一段函数主要利用for…in遍历来实现对:
def mean(numbers):
    “””计算平均值”””
    s = 0.0
    for num in numbers:
        s = s + num
    return s / len(numbers)
3.同上也是利用了for…in循环遍历,利用了上一个函数所计算的结果。另外又找一个参数sdev,来进行累加。在*后的处理中其数据的个数时,也是利用了len()函数来计算,pow()函数来计算他的开方。
def dev(numbers,mean):
    “””计算方差”””
    sdev = 0.0
    for num in numbers:
        sdev = sdev + (num – mean)**2
    return pow(sdev / (len(numbers)-1),0.5)
4.这个函数sorted()可以将列表中的数据进行排序。随后,就是利用if语句来进行判断这个数据的个数如果是偶数,就把中间的两个数据相加在除以二,如果,奇数就把中间的那个拿来就OK了。
def median(numbers):
    “””计算中位数”””
    sorted(numbers)
    size =len(numbers)
    if size % 2 == 0:
        med = (numbers[size//2-1] + numbers[size//2])/2
    else:
        med = numbers[size//2]
    return med
5.*后一个部分就是调用函数,并且*后的输入是:平均数:… 方差: … 中位数: …,利用的是format()对数据进行格式化。
n = getNum()
m = mean(n)
print(“平均值:{},方差:{:.2},中位数:{}.”.format(m,dev(n,m),median(n)))
有没有小伙伴会对其中的参数有疑问的  !—!.

Windows 系统盘越用越大简直让人崩溃,都第三次分区了。。。

软件都安装在了另一个盘,就这样系统盘还一直在吃硬盘。。。

第 1 条附言 · 15 小时 7 分钟前
感谢大家的回复!
准备新买一个大容量 SSD 硬盘
统盘 Desktop file users109 条回复 • 2021-08-13 15:04:41 +08:00
1 2
2
❮ ❯
lap510200 101
lap510200 1 小时 19 分钟前
我的笔记本默认 500g 是系统盘,我自己装了 1 个 t 固态划成 d 盘,设置 4k 对齐就完事了,懒得划分分区了
netitgo 102
netitgo 1 小时 17 分钟前
好像没有人说到 pagefile….如果虚拟内存管理是定在 C 盘,并且为自动管理,那么 pagefile 的大小会根据你当前使用软件不停动态变多,起码我玩游戏的时候,观察是这样。
u011631336 103
u011631336 1 小时 17 分钟前
我笔记本 100G SSD,分了两个盘,C 盘一直从 D 扩分区。。。现在 C 盘用了 70G
你可以下个 windirstat (相当于 linux 下的 du )看,是什么占用了空间;
aguesuka 104
aguesuka 1 小时 15 分钟前
2021 年了还有人分区吗 X
2021 年了还有人买不起固态硬盘吗 X
2021 年了还有人用 windown 吗 O
ftu 105
ftu 57 分钟前
Mac 上这种暴利 SSD *低配也 256G 了,往省着买情有可原

但你 win 这么自由性价比的还分这 1 开头的一丢丢过分了啊?
Tinyang 106
Tinyang 56 分钟前
修改一下微信文件管理的目录,它会自动做迁移,我刚从 c 盘腾出来 30 多个 G
zjj19950716 107
zjj19950716 47 分钟前
之前被 wsl 和 docker 爆过 c 盘,迁移了好多了
Jeffreylulu 108
Jeffreylulu 25 分钟前
现在没有分区的必要了吧,系统自身一个 1T 的 SSD 盘,反正 SSD 硬盘也不贵,其余的软件,以及游戏,都安装在 M2 存储条上,不行就加。
clrss 109
clrss 22 分钟前
用 WinDirStat 看一下什么占空间

APP和服务器通讯为什么要用接口?

在我们公司,app和服务器通信请求数据,不可能直接请求的,要通过接口,其实就是直接访问接口,接口把数据返回。在之前,我在另一家公司,是不用接口的,我直接请求服务器。所以,关于这点我比较迷茫。

经理给我解答,我就明白了。服务器和app之间通过接口来访问,主要有2点作用。

1、app客户端太大,不利于复用。

如果没有接口,app客户端是可以直接请求数据的,这个是可以做到,但是相当于接口的全部工作在app端写了,这样会造成app端比较大。比如,访问数据库的代码,android要写,ios也要写,太不利于复用。高质量代码的标准是可维护、可复用、可扩展、灵活性高。所以,如果有了接口,接口就可以对数据进行封装和业务处理,然后给app端。

2、不利于数据库安全。

接口可以把关安全性。因为客户端在客户手里,可以破解,可以反编译,整个架构下,整个客户端都是V,数据库直接暴漏出来,别人可以扫描你的数据库端口,很危险。所以一般数据库,外部是不给访问的,你只能通过接口,而接口,会要求你登录,登录后,根据你的身份。分配身份标记,再决定你能请求多少东西,每次请求都是由接口判断一次是否合法,就是根据SessionString,也可以是cookie。其实cookie就是SessionString的ID。即使被破解,拿到一个,也是一个用户的数据被盗,其他用户不受影响。

 

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速