HappyCoding:] ChildhoodAndy

思维世界的发展,在某种意义上说,就是对惊奇的不断摆脱。——爱因斯坦

ios之"performSelector may cause a leak because its selector is unknown"警告原因及其解决办法

| Comments

问题描述

项目中使用到了从字符串创建选择器,编译时发现警告:"performSelector may cause a leak because its selector is unknown"(因为performSelector的选择器未知可能会引起泄漏),为什么在ARC模式下会出现这个警告?

经过搜索后,在Stackoverflow上发现了一个令人满意的答案。见http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown

原因

在ARC模式下,运行时需要知道如何处理你正在调用的方法的返回值。这个返回值可以是任意值,如void,int,char,NSString,id等等。ARC通过头文件的函数定义来得到这些信息。所以平时我们用到的静态选择器就不会出现这个警告。因为在编译期间,这些信息都已经确定。

如:

...
[someController performSelector:@selector(someMethod)];
...
- (void)someMethod
{
  //bla bla...
}

而使用[someController performSelector: NSSelectorFromString(@"someMethod")];时ARC并不知道该方法的返回值是什么,以及该如何处理?该忽略?还是标记为ns_returns_retained还是ns_returns_autoreleased?

解决办法

1.使用函数指针方式

SEL selector = NSSelectorFromString(@"someMethod");
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(_controller, selector);

当有额外参数时,如

SEL selector = NSSelectorFromString(@"processRegion:ofView:");
IMP imp = [_controller methodForSelector:selector];
CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp;
CGRect result = func(_controller, selector, someRect, someView);

2.使用宏忽略警告

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Warc-performSelector-leaks" 
   [someController performSelector: NSSelectorFromString(@"someMethod")]
#pragma clang diagnostic pop

通过使用#pragma clang diagnostic push/pop,你可以告诉Clang编译器仅仅为某一特定部分的代码来忽视特定警告。

如果需要忽视的警告有多处,可以定义一个宏

#define SuppressPerformSelectorLeakWarning(Stuff) \
    do { \
        _Pragma("clang diagnostic push") \
        _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
        Stuff; \
        _Pragma("clang diagnostic pop") \
    } while (0)

在产生警告也就是performSelector的地方用使用该宏,如

SuppressPerformSelectorLeakWarning(
    [_target performSelector:_action withObject:self]
);

如果需要performSelector返回值的话,

id result;
SuppressPerformSelectorLeakWarning(
    result = [_target performSelector:_action withObject:self]
);

3.使用afterDelay

[self performSelector:aSelector withObject:nil afterDelay:0.0];

如果在接受范围内,允许在下一个runloop执行,可以这么做。xCode5没问题,但据反映,xCode6的话这个不能消除警告。

原文链接:https://github.com/dabing1022/Blog/issues/2

行走在黑暗中

| Comments

2014年11月08号,心城v1.1.0通过了苹果的审核。如果阅读过前面心城1.1.0今天提交审核了的朋友们会发现,时间俨然过了2周多。期间发生了什么事情?正常的Waiting For Review进入InReview大概1周差不多,这次怎么两周多了?

被拒了一次。

来看下被拒的原因吧,希望后面的朋友别和我一样。

1.问题描述

Reasons
2.23: Apps must follow the iOS Data Storage Guidelines or they will be rejected 
3.3: Apps with names, descriptions, screenshots, or previews not relevant to the content and functionality of the App will be rejected 
----- 2.23 -----

We found that your app does not follow the iOS Data Storage Guidelines, which is required per the App Store Review Guidelines.

In particular, we found that on launch and/or content download, your app stores 2.04MB. To check how much data your app is storing:

- Install and launch your app
- Go to Settings > iCloud > Storage & Backup > Manage Storage 
- If necessary, tap "Show all apps" 
- Check your app's storage

The iOS Data Storage Guidelines indicate that only content that the user creates using your app, e.g., documents, new files, edits, etc., should be backed up by iCloud. 

Temporary files used by your app should only be stored in the /tmp directory; please remember to delete the files stored in this location when the user exits the app.

Data that can be recreated but must persist for proper functioning of your app - or because customers expect it to be available for offline use - should be marked with the "do not back up" attribute. For NSURL objects, add the NSURLIsExcludedFromBackupKey attribute to prevent the corresponding file from being backed up. For CFURLRef objects, use the corresponding kCFURLIsExcludedFromBackupKey attribute. 

For more information, please see Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes?.

It is necessary to revise your app to meet the requirements of the iOS Data Storage Guidelines. 

----- 3.3 -----

In addition, your marketing screenshots do not sufficiently reflect the app in use, which does not give the user an accurate understanding of what the app does, as required by the App Store Review Guidelines.

Weve attached the relevant screenshot(s) for your reference. 

It would be appropriate to revise your screenshots to demonstrate the app functionality in use.

If your iTunes Connect Application State is Rejected, a new binary will be required. Make the desired metadata changes when you upload the new binary.

For discrete code-level questions, you may wish to consult with Apple Developer Technical Support. When the DTS engineer follows up with you, please be ready to provide:

- complete details of your rejection issue(s)
- screenshots
- steps to reproduce the issue(s)
- symbolicated crash logs - if your issue results in a crash log

一共两个原因。
分别对应苹果审核规则的2.23和3.3规则。

2.先说3.3 元数据问题

截图不够丰富,表达不了应用的功能,增加了3张图片解决问题。

3.再谈2.23 数据存储问题

心城将用户拍照以及头像设置的图片以及用户的录音文件都保存在了沙箱Documents文件夹,MagicalRecord用的数据库文件默认存放在了Library/Application Support目录下。

我举个例子来说明:

Documents下的文件是会被 iCloud 自动备份的,苹果的测试人员发现,他们拍了一些照片,录制一些音频,然后退出应用,同步iCloud发现数据有2.04M,很明显,照片和音频数据以及数据库文件(存放在Library下的ApplicationSupport下,同样会被iCloud自动同步)是造成这2.04M的罪魁祸首,所以被拒。

究其原因是我存储文件的位置不对或者说是规则不对造成的。为了避免被 iCloud 同步,需要对这些文件进行“不备份标记”。将文件夹标记为不备份,那么其子文件和子文件夹都不会被备份。这里还不敢对 Documents 标记不备份,那样“太过分了”,毕竟还有其他数据和文稿需要备份。对Documents下的不需要的文件夹进行标记即可。

和一个朋友交流中,他提到了他以前犯过的一个错误,他的应用在访问网络时,把一些图片存放在了 Documents 文件夹里面了,也是导致被拒,这些图片应该视情况放在tmp或者cache里面。

//沙盒Documents目录路径

 #define DOC_PATH ([NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0])
//沙盒Cache目录路径

 #define CACHE_PATH ([NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0])
//沙盒Library目录路径

 #define LIB_PATH ([NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0])
//沙盒Temp目录路径

 #define TEMP_PATH (NSTemporaryDirectory())
 // app data directory name

 #define APP_DATA_PHOTOS_DIR_NAME     @"photos"
 #define APP_DATA_AUDIOS_DIR_NAME     @"audios"
 #define APP_DATA_AVATARS_DIR_NAME    @"avatars"

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

  [self addSkipBackup];
  //.....

}

-(void)addSkipBackup
{
  NSString* photoDirPath = [DOC_PATH stringByAppendingPathComponent:APP_DATA_PHOTOS_DIR_NAME];
  NSString* audioDirPath = [DOC_PATH stringByAppendingPathComponent:APP_DATA_AUDIOS_DIR_NAME];
  NSString* avatarDirPath = [DOC_PATH stringByAppendingPathComponent:APP_DATA_AVATARS_DIR_NAME];
    
  [self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:photoDirPath]];
  [self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:audioDirPath]];
  [self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:avatarDirPath]];
  [self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:[NSPersistentStore MR_applicationStorageDirectory]]];
}

-(BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
    
    NSError *error = nil;
    BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
                                  forKey: NSURLIsExcludedFromBackupKey error: &error];
    if(!success){
        NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
    }
    return success;
}

修改之后,重新提交,漫长的WaitingForReview又重新开始。今天进入了InReview后顺利通过上架。

4.杂说

在等待的期间,v1.2.0也在另外的分支开发着。今天1.1.0上架后,v1.2.0 也做了提交。

v1.2.0的改动
1.崭新的UI界面;
2.增加密码锁保护隐私;
3.消除了5连拍时存在的闪退隐患。

襁褓中的心城,并不完美也并不强大,虽然收到了负面的反馈,但不妨碍我把TA坚持做下去做好的决心。

行走在黑暗中,也许某天,会看到光明。

心城前期的UI选择

| Comments




心城

在心城前期的开发过程中,因为暂时还没有美术人员,所以UI上我们会选择一些”开源“的一些好的设计。这里不得不提 mariodelvalle 的 http://mariodelvalle.github.io/CaptainIconWeb/ 这些优秀的设计。网站中提供有所有ICON的PSD源文件,甚至也包括了EPS、SVG矢量源文件,方便了在Illustrator下无限放大不失精度的要求。

感谢开源!

设计方面,我们会保持一个扁平的风格,一个是现在流行,另外扁平的风格的确看着特别舒服。在接受程度方面,IOS设备的使用者随着IOS7、IOS8的升级使用,对于扁平化style基本上都处于一个认可并喜欢的态度。

那下面 v1.2.0 这个的版本,我们会在细节和UI上处理的更好,敬请期待。也非常期待身边朋友和设计人员能给提出宝贵的意见!


海纳百川,有容乃大。心城会在一路的荆棘和鼓励下逐渐成长。

心城1.1.0今天提交审核了

| Comments

今天是2014年10月22号,距离上次心城1.0.0版本的提交快到了三周。在这三周里,我们围绕着身边朋友的反馈以及我们预计要做的功能,完成了1.1.0版本。

看看我们的成果!新加功能,以后会有更多!

1、增加了自定义背景色,颜色随心随手随时换;
2、增加了文本、照片两种表达方式,爱写字爱拍照的可以大展拳脚了;
3、增加了删除人物功能,请慎用;
4、调整了UI,常用功能轻点即立刻弹出,长按头像可展开更多功能。


小插曲

本来是昨天下午提交的,今天早上起来,看到一哥给我发的qq消息,我一看时间是凌晨1点多发的。他指出了新提交版本的一个问题,我核查了下代码,这个问题确实存在。

那么问题来了。

遇到这种情况,我们开发人员需要做什么工作?

1.关于代码方面

我一直用的git来进行代码版本控制,新建一个“1.1.0bugFix”分支后面进行合并或者在原1.1.0分支上进行修改都可以。关键是重现 BUG,找到代码问题所在,修复后,再次测试。没问题后,重新在Xcode里面Archive,上传到ITC后台。

2.ITC(ItunesConnect)方面的工作

一般这种情况,我们需要将提交的 APP,自己手动 reject(拒绝)掉。因为ITC网站苹果改版了,最新的网站界面和以前的有很大不同,可能有时候遇到问题,google出来的解决办法或许并不使用。

进入 ITC 后台,在 MyApps 里面,

将刚提交的版本,点击remove this version from review,将刚提交的版本从审核队列中移除掉。我们需要选择我们刚上传的app就可以了,而其他的元数据资料不用改动。所以在下方的Build一栏中,将之前的 build 删除,后面要做的就是重新 add 我们新上传的 build。

到这里有个问题,由于我们新上传 app,版本号仍然要保持 1.1.0,如果只修改了代码,就直接上传到ITC,在上传检查中就会报错,说你的这个 1.1.0 的 build 已经存在。问题出在了 build 的数字上面。第一次的1.1.0,在xcode项目中,Version为1.1.0,Build我也填成了1.1.0,而修改代码后,我忘记修改了Build的数字,所以会出现上面的报错信息。

我脑海中第一个反应,能不能在ITC里面把我上传的Version 1.1.0,Build 1.1.0这个提交的app prelease 删除掉。在Stackoverflow上我找到了答案,不能删除。解决方法很简单,我们重新Archive的时候,修改下Build的数值就可以了。ITC里面会记录上你同一个版本的所有Build,以Build数值来区分。

Build的填写没有很大要求,我这里改成了1.1.0.1。重新提交,这次顺利上传。


作为一个ios开发新入门的选手,在开发的过程中遇到了许多问题,有些问题我记录在了印象笔记里,但我觉得仍然有必要把我一路过来遇到的杂七杂八的问题与大家一起分享下,在后面的文章中会陆续记录些我在心城的开发中,遇到的技术问题以及一些想法。

也许你曾经遇到过,也许你没有,能一起有所收获,这么做便值了。

它既迟,也令我们感到羞愧

| Comments

今天,《心城》的1.0.0版本总算正式在App Store上发布了,这里是地址。因为我前期对应用开发的不熟悉,所以花了不少时间才敢推出最基本的功能。暂时没有美术,设计上也欠缺一些。

它既迟,也令我们感到羞愧。

这么说,是因为有个老外那么说:如果你发布第一个版本时没有感到羞愧,那么它发布的一定是太晚了。

现有的功能,已经有用户反馈说感觉无事可做。我们只能这么解释:它不是游戏,是不会粘用户的。当用户想起某人,感觉这人是重要的,就可以在心城里添加头像,录上一段话。它不是必需的,取决于用户表达情感的意愿。决不至于让用户自作多情。

所以作为一个目前只有不到10M的应用,装一个,它会静静在那里等着你某个情感漫溢的瞬间。

实用情境

1、总有那么一两个、三四个至亲至近的人;

2、忽然念想起某人,也不知道现在怎样了;

3、好喜欢好声音里的**,就粉TA了;

4、梦到过世的姥姥;

5、喜欢对面的新同事

......

按住头像,可以录音,可是说点什么呢?

1、什么都不说就确定、退出录音界面,欲言又止也是一种选择;

2、说出你的思念;

3、答应为TA做一件事却不必直接告诉TA;

4、说“对不起”;

5、祝福,祈祷,愿TA一切都好;

......

人们都太忙了。

1、打开心城啥都不干,看着一个个头像发发呆;

2、翻看下过往的记录,慢慢回忆;

3、想想是不是该联络联络那谁谁;

4、那答应谁的事情差点忘了;

5、深情抚摸那谁的头像;

......

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我不怕板砖,我只是认真。相信后面的版本迭代,会让心城成熟起来。

补充说明

【心城】 这个关键字不是特别好,在搜索方面有些吃亏。不少应用的关键字里面,带有心字,如果在 App Store 里面搜索,输入“心城”后,类别选择【社交】,可以快速的找到。

为ios模拟器相册库添加图片

| Comments

在app开发的时候,有时候需要用到ios模拟器中的相册图片。添加的方法非常简单,做个备注。

让模拟器显示Home页,拖放电脑中的图片到模拟器中,这时候模拟器的Safari浏览器会打开该图片,轻按图片数秒,会有保存菜单,保存即可。