FourV Zhang'Blog

开发随记,生活随笔,读书随感,旅游随心


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 搜索

block中成员变量和属性的区别

发表于 2017-07-28 | 分类于 研发随笔 | 阅读次数
  • person模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    .h文件
    #import <Foundation/Foundation.h>
    @interface person : NSObject
    /** block */
    @property (nonatomic, copy) void(^ageBlock)(int age);
    @end
    .m文件
    #import "person.h"
    @implementation person
    @end
  • testVC

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    .m文件
    #import "person.h"
    #import "testViewController.h"
    @interface testViewController ()
    /** person */
    @property (nonatomic, strong) person *p;
    /** value */
    @property (nonatomic, assign) int value;
    @end
    @implementation testViewController {
    int _value;
    }

####属性
1.在生成对应的成员变量_value的同时;
2.也会自动生成setter/getter方法.
使用self.value实际在调用两个方法来实现对变量的操作.

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
-(void)dealloc {
NSLog(@"释放当前vc对象");
}
- (void)viewDidLoad {
[super viewDidLoad];
self.p = [[person alloc] init];
__weak typeof(self) weakSelf = self;
self.p.ageBlock = ^(int age) {
//1.1 成员变量写法(自动报警,提示会产生循环引用)
_value = 50;
NSLog(@"_value-----%d",_value);
// 1.2等价成员变量(自动报警,提示会产生循环引用)
self->_value = 30;
NSLog(@"self->_value---%d",self->_value);
// 1.3成员变量弱引用(报错)
//weakSelf->_value = 20;
//NSLog(@"weakSelf->_value----%d",weakSelf->_value);
// 1.4成员变量weak-strong dance
__strong typeof(self) strongSelf = weakSelf;
strongSelf->_value = 10;
NSLog(@"strongSelf->_value--%d",strongSelf->_value);
//2.1属性未弱引用写法(自动报警,提示会产生循环引用)
self.value = 50;
NSLog(@"%d",self.value);
//2.3 属性的写法(由成员变量可以类推到也会有风险,推荐使用weak-strong dance方式)
weakSelf.value = age;
NSLog(@"%d",weakSelf.value);
//2.3 属性的写法(weak-strong dance)
__strong typeof(self) strongSelf = weakSelf;
strongSelf.value = age;
NSLog(@"%d",strongSelf.value);
};
}
self.p.ageBlock(10);

几种写法最终测试结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1.1 没有释放当前vc,说明当前vc继续被强引用,没有被销毁
2017-07-28 15:02:25.114 test_Property[20484:1869126] _value-----10
2017-07-28 15:02:26.325 test_Property[20484:1869126] -[testViewController viewWillDisappear:],控制器pop出去
1.2 没有释放当前vc,说明当前vc继续被强引用,没有被销毁
2017-07-28 15:13:12.727 test_Property[20539:1878132] self->_value---10
2017-07-28 15:13:14.764 test_Property[20539:1878132] -[testViewController viewWillDisappear:],控制器pop出去
1.3 见下面 xcode自测报错图
1.4 释放当前vc,并被销毁
2017-07-28 15:17:27.160 test_Property[20568:1883004] strongSelf->_value--10
2017-07-28 15:17:28.627 test_Property[20568:1883004] -[testViewController viewWillDisappear:],控制器pop出去
2017-07-28 15:17:29.131 test_Property[20568:1883004] 释放当前vc对象
2.1 没有释放当前vc,说明当前vc继续被强引用,没有被销毁
2017-07-28 15:19:04.250 test_Property[20583:1884117] 10
2017-07-28 15:19:05.301 test_Property[20583:1884117] -[testViewController viewWillDisappear:],控制器pop出去
2.2 释放当前vc,并被销毁
2017-07-28 15:21:50.294 test_Property[20621:1886605] 10
2017-07-28 15:21:51.488 test_Property[20621:1886605] -[testViewController viewWillDisappear:],控制器pop出去
2017-07-28 15:21:51.994 test_Property[20621:1886605] 释放当前vc对象
2.3 释放当前vc,并被销毁
2017-07-28 15:22:52.713 test_Property[20637:1887821] 10
2017-07-28 15:22:54.250 test_Property[20637:1887821] -[testViewController viewWillDisappear:],控制器pop出去
2017-07-28 15:22:54.756 test_Property[20637:1887821] 释放当前vc对象

xcode自测警告图.png

Capturing 'self' strongly in this block is likely to lead to a retain cycle

xcode自测报错图.png

Dereferencing a __weak pointer is not allowed due to possible null value caused by race condition, assign it to strong variable first

简单翻译一个SO的答案:Why would you use an ivar?,展开说一下成员变量的好处。

  • 封装
    如果成员变量是private,程序中的其它对象很难直接访问该成员变量。如果是属性,相对更容易用父类方法读写属性。

  • 性能
    成员变量地址可以根据实例的内存地址偏移寻址。而属性的读写都需要函数调用,相对更慢。

  • 非基础类型
    对于复杂的C++类型,往往设为成员变量更合适,也许这种类型不支持copy,或者完全复制很麻烦。

  • 多线程
    多线程环境下,为保证数据一致性,在需要同步执行的代码段更应该使用成员变量。如果对需要同步更新的数据用getter/setter 方法,数据更新效率低,会带来更多的获取锁请求失败。

  • 程序正确性
    成员变量可以做直观的内存管理。属性可以一层层继承,还可以复写。容易出错。

  • 二进制文件的体积
    默认用属性,会生成不必要的getter/setter 方法,程序体积会变大。

注意:
在block中最好使用属性,并使用weak-strong dance弱引用
使用成员变量(_value)常常忽略掉前面的self指针,容易产生循环引用的,泄露内存;

参考文章:
属性(property)与成员变量(ivar)有什么区别
iOS中self.和下划线的区别

A nested bundle contains simulator platform listed in xxxx Info.plist key

发表于 2017-05-24 | 分类于 研发随笔 | 阅读次数

应用审核被拒,原因:

Invalid Bundle - A nested bundle contains simulator platform listed in CFBundleSupportedPlatforms Info.plist key.

分析:

内嵌的路径 里的info.plist 文件中 一个 CFBundleSupportedPlatforms 值 包含了” 模拟器平台”,

众所周知,提交审核上架的应用是不能包含模拟器的架构和平台的,所以根据问题描述针对性解决.

解决:

内嵌路径(一般为第三方的库中)找到plist文件中的 CFBundleSupportedPlatforms删除支持的模拟器平台

查找方法如下:(仅供参考)

图1

图2

手动发布应用无法上架

发表于 2017-05-24 | 分类于 发布上架 | 阅读次数

beta最新版0.5提交苹果的审核通过了,手动发布后不久却收到邮件提示:

该应用已经被开发人员下架!

原因:

之前上架了一个beta低版本,并且被同事下架了;

手动发布beta 0.5 的时候没有将应用的 销售范围 仍为 “下架”状态

下架应用
解决方法:修改为 “在所有地区供应”即可

重新上架应用图

苹果开发指南中有提到相关问题,描述如下:

注意: 如果您将某个版本下架,然后添加了一个新版本,则新版本也会处于“被下架”状态,即使新版本已获得“App 审核”批准也是如此。若要发行新版本,您需要再次使 App 获准销售。

将 App 重新上架

在“App 详细信息”页面,点按“价格与销售范围”。

在“销售范围”部分,选择“在所有地区供应”选项。

../Art/13D-RemovedAppAvailable_2x.png

点按“存储”。

App 状态从“被开发人员下架”更改为“可供销售”。24 小时内您的 App 会重新显示在商店中。

其他问题请参考 苹果itunes connect 开发指南

视频播放支持横屏

发表于 2017-05-15 | 分类于 研发随笔 | 阅读次数

应用整体只支持竖屏,只有特定的某个界面支持横屏

解决方法:

1.在项目中plist文件中设置支持转屏方向

转屏控制级别: tabar>导航控制器>普通控制器

2.在tabbar/ 导航控制器/ 普通控制器 的.m文件中 复写以下三个方法

1
2
3
4
5
- (BOOL)shouldAutorotate ; // 是否支持屏幕自动旋转
-(UIInterfaceOrientationMask)supportedInterfaceOrientations // 支持的转屏方向
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation // 进入后默认的屏幕方向(必须包含在支持的屏幕方向里)

1)TabBarVC 中重写三个方法的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return self.selectedViewController.supportedInterfaceOrientations;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return self.selectedViewController.preferredInterfaceOrientationForPresentation;
}
- (BOOL)shouldAutorotate {
return self.selectedViewController.shouldAutorotate;
}

2)导航控制器基类中重写三个方法的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (BOOL)shouldAutorotate{
return self.topViewController.shouldAutorotate;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
return self.topViewController.supportedInterfaceOrientations;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return self.topViewController.preferredInterfaceOrientationForPresentation;
}

3)普通控制器基类中代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (BOOL)shouldAutorotate {
return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}

针对没有tabbar,只有导航控制器的应用,可以直接省去TabBarVC中方法重写代码;

3.因为项目的大多控制器是不支持自动转屏,且只支持竖屏;因此这些ViewController 继承自BaseViewController;

针对特定的需要支持 左右横屏的视频播放界面,仍需要复写以上三个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@property(nonatomic,assign)BOOL autoRotate;
- (BOOL)shouldAutorotate{
return self.autoRotate;}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationPortrait;}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskAllButUpsideDown;}
旋转屏幕
- (void)switchToLandscapePotrait {
_autoRotate = YES;
// 如果当前设备是物理横屏,先恢复为竖屏,保证后面有转屏动画
if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft) {
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIDeviceOrientationPortrait] forKey:@"orientation"];
}
// 手动设置横屏,会调用 方法 shouldAutorotate
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIDeviceOrientationLandscapeLeft] forKey:@"orientation"];
[UIApplication sharedApplication].statusBarHidden = YES;
_autoRotate = NO;
}

代码手动设置横屏, [ [UIDevice currentDevice] setValue: forKey: ]会调用 方法- (BOOL)shouldAutorotate,如果该方法返回的是NO,则无法使用代码设置横屏;

所有需要在调用前 设置_autoRotate = YES; 调用完毕设置_autoRotate = YES;

注意事项:

当手机横放,已经是物理横屏的时候,再手动设置横屏是无效的;所以此处需要做处理(如果是物理横屏,先恢复为物理竖屏)

缓存之NSCache&NSDictonary

发表于 2017-05-07 | 分类于 研发随笔 | 阅读次数

实现缓存:

NSCache相比NSDictonary:

1)多数情况下,可以使用objectForKey: / setObject: forKey: /removeObjectForKey:

2)拥有多线程安全的特性,可以在任何线程上不加锁地修改NSCache;

3)还被设计为能与符合协议的对象整合,开发者可以通过调用 beginContentAccess 和 endContentAccess 控制何时丢弃对象是安全的

4)能在应用运行时/应用暂停时 提供自动缓存管理;也会在使用的常见类型NSurgreableData后, iOS会释放内存(即使应用处于暂停状态)。

—————————————-分割线——————————————

图层绘制:(CALayer和UIView)

UIView是一个相当重量级的对象,它管理绘制与事件处理(尤其是触摸事件)。CALayer完全关乎绘制。事实上,UIView依靠CALayer来管理绘制,这样两者就能协作的很好。图层会在它的contents属性中绘制任意东西,开发者负责进行设置。

方法一:直接分配

UIImage *image = …;

CALayer *layer = …;

layer.contents = (id)image.CGImage;

方法二:core Animation会按照以下顺序通过CALayer和委托方法来创建它。

1)[CALayer setNeedsDisplay] : 代码需要调用它。它会将图层标记为需要重绘的,要求通过列表中的步骤来更新contents。除非调用了setNeedsDisplay方法,房子contents属性永远不会更新。

2)[CALayer displayIfNeeded] :绘制系统会在需要时自动调用它。如果图层通过被调用setNeedsDisplay标记为需要重绘的,绘制系统就会接着执行后续步骤。

3)[CALayer display]: displayIfNeeded方法会在合适的时候调用它。开发者不应该直接调用它,如果实现了委托方法,默认实现会调用displayeLayer: 委托方法。否则,display方法会调用drawInContext:方法。可以在子类中覆盖display方法以直接设置contents属性

4)[delegate displayLayer: ] :默认的[CALayer display]会在方法实现这个方法时调用它。它的任务是设置contents。如果实现了这个方法(即使没有什么操作),后面就不会运行自定义的绘制代码。

5)[CALayer drawInContext:] :默认的display方法会创建一个视图图形上下文并将其传给drawInContext: 方法。它与UIView drawRect:方法相似,但不会自动设置UIKit上下文。为了使用UIKit来绘图,你需要调用UIGraphicsPushContext()方法指定接收到的上下文为当前上下文。否则,他只会使用Core Graphics在接收到的上下文中绘图。默认的display 方法获取最终的上下文,创建一个CGImage并将其分配给contents。默认的CALayer drawInContext:] 会在方法已实现时调用【delegate drawLayer: inContext】。否则,就不执行任何操作。不过可以直接调用这个方法

6)[delegate drawLayer: inContext:]: 如果实现了这个方法,默认的drawInContext:会调用这个方法实现更新上下文,从而是display方法可以创建CGImage。

总结:使用drawInContext:方法是设置contents的另一种方法。它是通过display方法调用的,而display 方法只有当你通过 setNeedsDisplay 方法明确标记图层为需要重绘是调用。

drawInContext:只在当前图层绘制(不包括期任何子图层)。要绘制图层及其子图层,可以使用renderInContext:方法,它可以捕捉图层当前动画的状态。renderInContext:使用当前渲染的状态(由Core Animation内部管理),因此它不会调用drawInContext:方法。

友盟推送那些问题(iOS10)

发表于 2017-02-19 | 分类于 研发随笔 | 阅读次数

上周,公司要实现推送通知的功能,并决定在项目中集成友盟推送的 SDK来代替之前集成的云巴推送。

云巴推送是什么鬼?让我们来看看它的功能和使用场景。

云巴是一个覆盖全国(即将覆盖美国欧洲)的实时消息发布订阅系统,可以以毫秒级的速度向百万乃至千万终端用户发送大量的消息。云巴适用于对实时性和到达率要求高的应用、游戏、设备。云巴支持手机、平板、笔记本、智能硬件、智能家居等作为终端。

云巴在 Github 上开源地址,云巴使用轻量级的 MQTT 作为协议,主要编程语言为 Erlang。云巴支持 IOS, Android, JavaScript, Socket.IO, PHP, C, C#等语言,可以从任一终端向另一终端发送消息。

云巴的特点是:高并发、低耗费、毫秒级、跨平台、跨设备、跨语言。

云巴的应用场景包括:双向消息推送,私信,即时聊天,多人大型游戏,租车打车通知,远程摄像头控制,远程开关空调,实时更新股票走势,实时更新库存量,活动文字直播,实时地理位置地图,智能硬件的 APP 控制,实时统计图表。

其实云巴和友盟在实现推送功能方面没什么太大区别,只是因为云巴推送是在项目赶工期时的产品,具体的交互界面以及参数都没来得及定!所以推送在本次开发任务中交给了不大熟知云巴的我来做,因此换成了被大家熟悉且曾经集成过的友盟推送!

下面介绍一些集成中常出的问题以及自己出现的问题:

1.友盟后台添加新应用,需要上传 P12文件,而 pem 文件是不能上传成功的(本人没有上传成功)

本人从钥匙串中导出文件是只能导出为 pem 文件,但是导出的 pem 文件却不能被友盟后台识别转换

后来发现所导出的证书下面缺少了私钥,并且左侧没有三角标识,如下图:

于是使用公司的苹果开发者帐号里面重新生成了一套 SSL证书(包含私钥,呈现下三角标识),如下图:

左侧的三角标识显示为上图状况时,导出的p12文件才是友盟后台所需要的证书文件(而非钥匙串的 p12文件),把导出的 p12文件(设置密码*)上传到友盟后台,并在相应位置输入刚才设置的相对应证书的密码即可添加应用成功;

随后就可以在 应用信息 一栏中查看应用刚刚添加的应用的 Appkey 和 AppMaster Secret,并可以设置公司推送服务器的IP 地址

2.通过 DeviceToken在友盟后台添加测试设备

查看友盟官方相关deviceToken问题的论坛

2.1
1)使用didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token获取 token并使用 NSLog 方法打印出来,但是打印出来的 token 如下图:

2.2
2)此时把以上字符串复制粘贴到图2.1的空格中,会标红报错——Device Token不能包含特殊字符,

分析原因得知:刚才拷贝的内容包含尖括号<>以及空格,所以处理办法是——去掉<>和空格后;

3)根据上一步分析,在图2.1空白处将拷贝的内容修改过后,发现仍然报错——Device Token长度错误

分析原因得知:第一步拷贝到2.1图中空白处的内容因为包含空格和尖括号,所以长度已经超出了64,达到了73,而空白处提示 “输入64位*“,所以此处拷贝过来的内容已经被截取了前64位,导致后面的一些字符串被删除掉了,即使删除了尖括号和空格,就会导致被自动截取的前64位中有效的字符串(非空格,非尖括号)不足64位, 所以最有效的处理方法就是———从 1)中拷贝的 token 去除尖括号以及空格

于是获取 token 的方法就使用如下方法:

DeLog(@”UmengDeviceToken-%@”,[[[[deviceToken description] stringByReplacingOccurrencesOfString: @”<” withString: @””]

stringByReplacingOccurrencesOfString: @”>” withString: @””]

stringByReplacingOccurrencesOfString: @” “ withString: @””]);

4)通过3)获取的 token 拷贝粘贴到空白处,并填写设备名称加以区分,即可添加成功测试设备。

3.友盟后台测试单播和自定义播(alias)都能成功,而公司服务器测试单播可成功,而 alias 报2010错误

查看友盟与Alias对应的Device_token 为空的官方论坛 && 单播可收到,广播收不到的官方论坛

1)检查 iOS 客户端代码是否设置别名成功

客户端设置别名(Alias)代码如下:

[UMessage setAlias:@”alias1310” type:@”alias” response:^(id _Nonnull responseObject, NSError * _Nonnull error) {

DeLog(@”成功返回数据–%@”,responseObject);

}];

并且打印信息显示:成功返回数据–{success = ok}

2)通过友盟后台的工具检测设置的 alias 是否成功

3)因为安卓都 OK,考虑到安卓和 iOS 的后台是分开集成的,所以让后台人员配合检查其后台相关代码

后台人员检查无误!!!

4)闹心有木有,到底哪里出了问题呢??

有点信不过后台哥们,于是跑到后台看他们的代码以及相关文档如下:

他们也有个 alias_type:”xx”,阅读后面描述发现端倪,于是查看后台代码中alias_type 值发现:我的代码中的 type 类型值 和后台的竟然没有统一写成一致的,是不是这个问题导致的呢???

于是飞速再键盘上敲下了相同的 alias_type 值,经过测试发现 OK 啦! 报的错误和真正的问题根源还是不大一样的!哦耶

4.自定义弹框偶尔 会弹出两个弹框——自定义的弹框+友盟的默认弹框

这个问题最初没有发现,后来有一次做断点调试的过程中,发现:两个弹框的Bug必现!!!!!

1)因为测试机是 iOS10以下系统,所以在方法 代理方法 didReceiveRemoteNotification: fetchCompletionHandler: 中查看是否关闭友盟弹框

如果是 iOS10系统,需要再方法userNotificationCenter: willPresentNotification: withCompletionHandler: 中查看是否关闭友盟弹框

结果如下图:

2)查看 友盟推送demo中的代码,是否调用了相同的方法

结果如下图:

比较二者,有没有发现二者的区别;两个方法的调用顺去颠倒也会出现 Bug 的,瞬间解决,哦耶!!!

5.在友盟后台测试推送通知时,客户端经常收不到通知!!!

1)请跳转到知乎相关网址去查找相对应的原因;

2)网络很差,推送有延迟,稍等1-3分钟即可收到;

这就是目前在集成友盟推送的开发过程中遇到的一些小插曲,

希望有同样的问题的人可以通过以上解决方法能够顺利解决!!谢谢

如有更多问题请移步到 【友盟+】消息推送常见问题索引

苹果内购(IAP)调研报告

发表于 2016-07-25 | 分类于 研发随笔 | 阅读次数

一.适用情况:

什么时候使用苹果内购?为什么时候苹果内购?

在苹果制定的游戏规则中,所有在App内提供的服务需要付费时,都应当使用IAP,比如解锁软件功能、游戏道具;所有在App外提供的服务需要付费时,都应使用其他支付方式,比如比如淘宝、京东等一堆电商,他们的商品都是不存在于App中的。

列举一些相关实例,

1.在IAP里,可以出售:

数字内容:比如杂志、图片、游戏关卡解锁、相机付费滤镜等;

软件功能:如各种扩展features;

一次性服务:比如一次语音通话等。

2.在IAP里,不能出售:

现实世界的商品或服务

3.现在有很多比如网易云课堂,腾讯课堂等等在线教育App,可以在App中购买视频并在APP中观看,也就是说’观看视频’这个功能是本身就存在于APP中的,只是需要你先购买视频,才能使用’观看视频’这个功能,这个时候,这种解锁APP已有功能的操作,就必须要使用iap

当然,肯定有APP不是这样,毕竟谁也不愿意让苹果抽那么多钱,那些APP也许是用了一些什么欺骗手段,比如后台写个开关,审核的时候打开,所有功能都能使用,上线后再关闭开关啊什么的。这样的APP有可能随时会被警告并下架,严重点的封号也不是没可能 o(╯□╰)o

第三点就提到了为什么使用苹果内购? 极大降低了应用审核被拒的风险以及被迫下架的可能性,还可以大力推广应用的付费功能.

二.关于商品类型:iap创建商品时选择的商品类型大概分三类

1.消耗型商品:

就是类似游戏中的钻石,还有现在某些APP中的货币,比如什么鱼丸,鱼翅什么的,会被消耗的,要选择消耗型商品

2.非消耗型商品:

无法被消耗的商品,比如上文提到的视频课程,一次购买,就应该永久可以观看,视频你怎么消耗?打赏多少多少视频?o(╯□╰)o

3.订阅类型商品:

多用于会员,需要注意的一个地方就是苹果似乎没有保存期限,所以这个期限要后台保存的.另外需要注意的一个地方是当APP中有过订阅类型商品,注意是有过,曾经有过删除也算的,这个APP是无法转移账号的.

三.在itunes connect 里面签署相关协议

将上图标注的关于联系人信息,税务,银行信息填写完成就ok.

四.添加内购项目

五.应用内代码实现 && 沙盒测试

可以参考此博客,博客中介绍的比较详尽.

六.内购的两种模式—内置模式 && 服务器模式(相对可靠,安全,首选)

使用内置模式。 需要交付的产品已经在程序内部。 这种方式通常用在一些被锁定的功能上。 也可以用来交付在程序束(App Bundle)中的内容。 该方式的一个重要的优点是你可以及时的给客户交付产品,大多数的内置产品应为非消耗性商品。

使用服务器模式,要提供另外的服务器将产品发送给程序。 服务器交付适用于订阅、内容类商品和服务,因为商品可以作为数据发送,而不需改动程序束。 例如,一个游戏提供的新的内容(关卡等)。 Store Kit不会对服务器端的设计和交互做出定义,这方面工作需要你来完成。 而且,Store Kit不提供验证用户身份的机制,你需要来设计。 如果你的程序需要以上功能,例如,纪录特定用户的订阅计划, 你需要自己来设计和实现。

1.内置模式的流程:

app从app store 获取产品信息

用户选择需要购买的产品

app发送支付请求到AppStore

AppStore处理支付请求,返回transaction信息

app将购买的内容展示给用户

2.服务器模式的流程:

app从服务器获取产品标识列表

app从app store 获取产品信息

用户选择需要购买的产品

app 发送支付请求到AppStore

AppStore处理支付请求,返回transaction信息

app将 transaction receipt 发送到服务器

服务器收到收据后发送到app stroe验证收据的有效性

app store 返回收据的验证结果

根据app store 返回的结果决定用户是否购买成功

上述两种模式的不同之处主要在于:交易的收据验证,内建模式没有专门去验证交易收据,而服务器模式会使用独立的服务器去验证交易收据。内建模式简单快捷,但容易被破解。服务器模式流程相对复杂,但相对安全

开发之初,苹果官方就很负责的告知:我们的服务器不稳定。真正开发之后,发现苹果方果然是很负责的,不仅是不稳定,而且足够慢。app store server验证一个收据需要3-6s时间

用户能否忍受3-6s的等待时间

如果app store server 宕机,如何确保成功付费的用户能够得到正常服务。

对于第一个问题,我们有理由相信用户完全无法忍受,所以采用异步验证的方式,服务器收到客户端的请求后,就将请求放到MCQ中去处理。

对于第二个问题,由于苹果人员很负责人的告知:我们的服务器不稳定,所以不排除收据验证超时的情况。对于验证超时的收据,保存到数据库中并标记为验证超时,定时任务每隔一定的时间去app store验证,确保能够获取收据的验证结果。此处摘自博客

设置tableView的分割线顶两端

发表于 2016-01-12 | 分类于 研发随笔 | 阅读次数

在最近的项目中设计图如下
示例图.png

分割线顶在 两端,于是搜集了相关的实现方法

在控制器空加入如下代码,

1
2
3
4
5
6
7
8
9
10
11
12
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
[self.tableView setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 0)];
}
if ([self.tableView respondsToSelector:@selector(setLayoutMargins:)]) {
[self.tableView setLayoutMargins:UIEdgeInsetsMake(0, 0, 0, 0)];
}
}

并且cell的相关设置方法- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 中调用下列方法

1
2
3
4
5
6
7
8
9
10
11
12
- (void)setSeparatorInsetForCell:(UITableViewCell*)cell {
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
[cell setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 0)];
}
if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsMake(0, 0, 0, 0)];
}
}

这是比较小众的方法来实现 特定的需求。

iOS的后台机制

发表于 2015-12-10 | 分类于 研发随笔 | 阅读次数

最近在修改项目中的bug过程中,遇到了 设备锁屏无法定位的情况.

于是搜查了Apple相关的后台机制,原文在此

OS 7中,实际上APP拥有四种后台模式,无论是哪一种后台机制,均需要利用苹果给予的相应后台接口实现。IOS7系统中,开发者可以灵活利用多种后台接口(API)实现更加智能的应用操作。

一、无后台仅推送

第 一种后台方式为传统的无后台操作,仅有苹果推送机制,这种方式出现在iOS 3.x以下的大部分系统版本上。这个方式下,应用在按下Home键后即会关闭退出,其数据通过苹果搭建的推送服务器传输,并不需要应用后台运行。这种方式 不太好的原因在于,每次推出后,重新进入均需要重新加载,虽然推送能够统一解决数据和信息的传输,但遇到需要频繁进入应用(如聊天APP)的时候便会显得 体验不好。

二、墓碑式

第二种方式为墓碑式的后台机制,这在iOS 4后被大量采用,也就是人们所说的伪多任务。这方式相比较第一种改进的地方在于,按下Home键至主界面后,应用随即进入后台,但其被冻结,并不能进行任何操作。

三、智能调度后台

第三种为系统智能调度的后台,iOS 7新增的background fetch, 这个后台接口在苹果WWDC 2013上有提及,其会根据用户行为自动调整达到效率最优的后台模式,能够处理不是很有时效性的信息获取。例如一些社交、新闻类的应用的后台信息更 新,iOS系统便会根据应用启动频率、时间和当前网络和电量的状况来智能分配每个应用的后台获取频率和启动时长。

由于拥有该接口的应用的数据后台 刷新操作是统一调度的,因此系统可以在一个进程里面获得多个应用的数据,类似统一的推送机制,这样就能够最大限度地省电。 不过这个方式也有一个缺点,那便是开发者不能设定数据具体什么时候更新,因此这个后台方式只能应用在一些时效性和敏感度不高的地方。

四、真后台

第四种方式便是真后台机制,但iOS的真后台与Android的后台机制是不一样的,为了兼顾系统体验和统一进程管理,iOS在这上面加入了众多的限制。大致拥有以下几个后台接口模式:

*1、Background Audio,这是后台的音频,这个很早之前便有,也是iOS设备中用得最多的后台应用,调用这个接口可以实现后台的音乐播放。

2、Location Services,这是后台的定位,系统会拥有统一页面进行管理。

3、VoIP,后台语音服务,类似Skype通话应用需要调用,可进行后台的语音通话。

4、Newsstand,报刊杂志后台自动下载更新,其能够自动实时更新。

5、 Background Task Completion,这个接口早在iOS 4时候便拥有,其可以供任意类型的APP使用,不过在旧系统中,这个接口的后台限制运行时间仅为10分钟,意味着当应用退至后台,其后台运行仅能持续10 分钟便会转至休眠状态。iOS 7中对这个接口作出了改变,原来的为连续10分钟,即不论你这10分钟内用户是否关闭屏幕进入休眠状态,应用仍然会在后台等待10分钟完结后推出,而新的 改进为假如遇到关闭屏幕休眠的情况,这后台运行的10分钟便会跟随一同休眠,剩余的后台时间将会留待用户再一次唤醒设备才计算。这样后台运行的时间仍然为 10分钟,但并不连续,这样做的优点为省电。

如现在有一些词典应用带有后台复制选词功能,实际上其是利用了这个接口,如果用户开启词典后并推出,即使屏幕关闭,但词典仍然在后台运行,电量消耗还是比较大的,在iOS 7上,这个问题可以得到解决。

6、 Remote Notification,这是本次较大的一个改进接口,以往聊天类应用接受推送后点进去需要再收一次信息,这情况在QQ、微信等应用上最为明显。不过拥 有了这个接口后,这情况将不复存在,以后推送将能够直接启动后台任务。值得注意的是remote notification支持silent notification(静默推送),这样dropbox这类同步应用可以在后台以最节能的模式实时静默同步了,类似布卡漫画这种也可以推送正在追的漫 画的新章节并在后台静默下载,待到下载好再给用户发送一个本地推送,用户点开即看无需再联网

7、Background Transfer Service,后台上传下载。iOS最接近传统多任务的后台接口,可供任意类型的app调用,无时间限制。应用场景包括后台上传和下载数据,这使得游戏 后台更新数据包,后台上传视频等等都成为可能,但是正如其名字,它只能用于处理上传下载这种传输类的任务,类似后台剪切板监控这种它就无能为力了。

可以在TARGETS中查找相关选项即可查找到background模式的相关设置,并且这些设置最后会在pilist文件中有体现…
示例图

Appdelegate中代理方法

发表于 2015-11-23 | 分类于 研发随笔 | 阅读次数

最近项目要加上一个手势解锁,然而手机解锁的逻辑十分混乱,需要与应用程序启动、应用前后台切换执行的delegate 方法分析清楚!

1.起初需求:要在程序第一次启动时,就需要设置解锁手势,于是在代理方法

1
- (void)applicationDidBecomeActive:(UIApplication *)application中写了相关判断方法;

2.需求改变:要在用户有需求时设置手势解锁;于是在代理方法

1
- (void)applicationWillEnterForeground:(UIApplication *)application中卸了相关判断方法。

3.在此过程中,仔细琢磨了 Appdelegate 中代理方法在前后台切换中的执行

首次运行:

1
2
3
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- (void)applicationDidBecomeActive:(UIApplication *)application

首次关闭(home):

1
2
3
- (void)applicationWillResignActive:(UIApplication *)application
- (void)applicationDidEnterBackground:(UIApplication *)application

再次运行:

1
2
3
- (void)applicationWillEnterForeground:(UIApplication *)application
- (void)applicationDidBecomeActive:(UIApplication *)application

再次关闭:

1
2
3
- (void)applicationWillResignActive:(UIApplication *)application
- (void)applicationDidEnterBackground:(UIApplication *)application

除此之外,再附上两个使用过的比较好的手势解锁框架—–LLLockView 和 CoreLock

非常感谢两位人员,为我们提供了如此强大的开源框架!

1234
FourV Zhang

FourV Zhang

记录生活的点点滴滴,待岁月逝去,回首往事,嘴角扬起久违的笑容

32 日志
4 分类
5 标签
© 2018 FourV Zhang
由 Hexo 强力驱动
主题 - NexT.Pisces