obj-c中用NSstringfromclass调用swift return nil类返回nil的问题怎么解决

Swift中使用可选类型完美解决占位问题
投稿:junjie
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了Swift中使用可选类型完美解决占位问题,本文讲解了为Dictionary增加objectsForKeys函数、Swift中更简便的方法、内嵌可选类型等内容,需要的朋友可以参考下
可选类型是Swift中新引入的,功能很强大。在这篇博文里讨论的,是在Swift里,如何通过可选类型来保证强类型的安全性。作为例子,我们来创建一个Objective-C API的Swift版本,但实际上Swift本身并不需要这样的API。
为Dictionary增加objectsForKeys函数
在Objective-C中,NSDictionary有一个方法-objectsForKeys:NoFoundMarker:, 这个方法需要一个NSArray数组作为键值参数,然后返回一个包含相关值的数组。文档里写到:"返回数组中的第N个值,和输入数组中的第N个值相对应",那如果有某个键值在字典里不存在呢?于是就有了notFoundMarker作为返回提示。比如第三个键值没有找到,那么在返回数组中第三个值就是这个notFoundMarker,而不是字典中的第三个值,但是这个值只是用来提醒原字典中没有找到对应值,但在返回数组中该元素存在,且用notFoundMarker作为占位符,因为这个对象不能直接使用,所以在Foundation框架中有个专门的类处理这个情况:NSNull。
在Swift中,Dictionary类没有类似objectsForKeys的函数,为了说明问题,我们动手加一个,并且使其成为操作字典值的通用方法。我们可以用extension来实现:
extension Dictionary{
&&& func valuesForKeys(keys:[K], notFoundMarker: V )-&[V]{
&&&&&&& //具体实现代码后面会写到
以上就是我们实现的Swift版本,这个和Objective-C版本有很大区别。在Swift中,因为其强类型的原因限制了返回的结果数组只能包含单一类型的元素,所以我们不能放NSNull在字符串数组中,但是,Swift有更好的选择,我们可以返回一个可选类型数据。我们所有的值都封包在可选类型中,而不是NSNull, 我们只用nil就可以了。
extension Dictionary{
&&& func valuesForKeys(keys: [Key]) -& [Value?] {
&&&&&&& var result = [Value?]()
&&&&&&& result.reserveCapacity(keys.count)
&&&&&&& for key in keys{
&&&&&&&&&&& result.append(self[key])
&&&&&&& return result
Swift中更简便的方法
小伙伴们可能会问,为什么Swift中不需要实现这么一个API呢?其实其有更简单的实现,如下面代码所示:
extension Dictionary {
&&& func valuesForKeys(keys: [Key]) -& [Value?] {
&&&&&&& return keys.map { self[$0] }
上述方式实现的功能和最开始的方法实现的功能相同,虽然核心的功能是封装了map的调用,这个例子也说明了为什么Swift没有提供轻量级的API接口,因为小伙伴们简单的调用map就可以实现。
接下来,我们实验几个例子:
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = dic.valuesForKeys(["1", "4"])
//结果为:[Optional(2), Optional(5)]
var t = dict.valuesForKeys(["3", "9"])
// 结果为:[Optional(3), nil]
t = dic.valuesForKeys([])
//结果为:[]
内嵌可选类型
现在,如果我们为每一个结果调用last方法,看下结果如何?
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = dic.valuesForKeys(["1", "4"]).last //结果为:Optional(Optional(5))
// Optional(Optional("Ching"))
var t = dict.valuesForKeys(["3", "9"]).last
// 结果为:Optional(nil)
var t = dict.valuesForKeys([]).last
// 结果为:nil
小伙伴们立马迷糊了,为什么会出现两层包含的可选类型呢?,特别对第二种情况的Optional(nil),这是什么节奏?
我们回过头看看last属性的定义:
var last:T? { get }
很明显last属性的类型是数组元素类型的可选类型,这种情况下,因为元素类型是(String?),那么再结合返回的类型,于是其结果就是String??了,这就是所谓的嵌套可选类型。但嵌套可选类型本质是什么意思呢?
如果在Objective-C中重新调用上述方法,我们将使用NSNull作为占位符,Objective-C的调用语法如下所示:
[dict valuesForKeys:@[@"1", @"4"] notFoundMarker:[NSNull null]].lastObject
[dict valuesForKeys:@[@"1", @"3"] notFoundMarker:[NSNull null]].lastObject
[dict valuesForKeys:@[] notFoundMarker:[NSNull null]].lastObject
不管是Swift版本还是Objective-C版本,返回值为nil都意味数组是空的,所以它就没有最后一个元素。 但是如果返回是Optional(nil)或者Objective-C中的NSNull都表示数组中的最后一个元素存在,但是元素的内容是空的。在Objective-C中只能借助NSNull作为占位符来达到这个目的,但是Swift却可以语言系统类型的角度的实现。
提供一个默认值
进一步封装,如果我字典中的某个或某些元素不存在,我们想提供一个默认值怎么办呢?实现方法很简单:
extension Dictionary {
&&& func valuesForKeys( keys:[Key], notFoundMarker: Value)-&[Value]{
&&&&&&& return self.valueForKeys(kes).map{ $0 ?? notFoundMarker }
dict.valuesForKeys(["1", "5"], notFoundMarker: "Anonymous")
和Objective-C相比,其需要占位符来达到占位的目的,但是Swift却已经从语言类型系统的层面原生的支持了这种用法,同时提供了丰富的语法功能。这就是Swift可选类型的强大之处。同时注意上述例子中用到了空合运算符??。
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具用Runtime的手段填充任意NSObject对象的nil属性 - 推酷
用Runtime的手段填充任意NSObject对象的nil属性
好久没有写东西了,忙啊。
前段时间参加了一下我们华科联创的HackDay(本人在读研=。=,目前在阿里实习),作品是一款
实时在线对战游戏 - 波波攒
,(介绍请看
从iOS游戏客户端(用的
)到后台(
PHP CI + Node + SocketIO + MySQL
)全是自己一个人倒腾出来的,做了一把真正的全栈工程师,爽啊~
后面会完善整个游戏,增加角色、优化啥的,过上一段时间会上线的哈~
回到正文,本文主要介绍了怎么用Runtime的手段遍历任意NSObject对象的所有property,检查其值是否是nil,是的话根据其类型为其填充一个默认值。
Runtime毕竟是个“危险”的技术,本文的代码只是个初步的尝试。
在做项目的过程中,总是会写一大堆if、else语句去检查对象的Property是否是nil,如从服务器返回的JSON中缺少属性,导致Entity的某些值为空;或者创建的对象没有对所有属性做初始化等等。写多了觉得好烦啊=。=所以想到本文的方法,嗯,程序员总是懒的。
遍历一个对象的所有属性(默认不包括父类属性)。
判断属性是否是nil。
为nil的属性,获取它的类型。
根据类型设置初始值(如NSString可以设为空字符串;NSNumber可以设为@0)
OC是一门“动态”、“基于消息”的语言,而Runtime就是利用OC的动态特性,在运行时对程序做出“调整”的技术。有关Runtime的官方文档、网上的资料很多,大家自学哈~
本文主要用了如下几个Runtime的函数:
// 获取类的所有Property1. objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)// 获取一个Property的变量名2. const char *property_getName(objc_property_t property)// 获取一个Property的详细类型表达字符串3. const char *property_getAttributes(objc_property_t property)
不好一块一块拆开说,直接上代码:
/** * 解析Property的Attributed字符串,参考Stackoverflow */static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
NSLog(@&%s&, attributes);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *
while ((attribute = strsep(&state, &,&)) != NULL) {
// 非对象类型
if (attribute[0] == 'T' && attribute[1] != '@') {
// 利用NSData复制一份字符串
return (const char *) [[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes];
// 纯id类型
} else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
return &id&;
// 对象类型
} else if (attribute[0] == 'T' && attribute[1] == '@') {
return (const char *) [[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
return &&;}/** * 给对象的属性设置默认值 */void checkEntity(NSObject *object) {
// 不同类型的字符串表示,目前只是简单检查字符串、数字、数组
static const char *CLASS_NAME_NSSTRING;
static const char *CLASS_NAME_NSNUMBER;
static const char *CLASS_NAME_NSARRAY;
// 初始化类型常量
static dispatch_once_t onceT
dispatch_once(&onceToken, ^{
// &NSString&
CLASS_NAME_NSSTRING =
NSStringFromClass([NSString class]).UTF8String;
// &NSNumber
CLASS_NAME_NSNUMBER = NSStringFromClass([NSNumber class]).UTF8String;
// &NSArray&
CLASS_NAME_NSARRAY = NSStringFromClass([NSArray class]).UTF8String;
unsigned int outCount,
// 包含所有Property的数组
objc_property_t *properties = class_copyPropertyList([object class], &outCount);
// 遍历每个Property
for (i = 0; i & outC i++) {
// 取出对应Property
objc_property_t property = properties[i];
// 获取Property对应的变量名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
// 获取Property的类型名
const char *propertyTypeName = getPropertyType(property);
// 获取Property的值
id propertyValue = [object valueForKey:propertyName];
// 值为空,才设置默认值
if (!propertyValue) {
// NSString
if (strncmp(CLASS_NAME_NSSTRING, propertyTypeName, strlen(CLASS_NAME_NSSTRING)) == 0) {
[object setValue:@&& forKey:propertyName];
// NSNumber
if (strncmp(CLASS_NAME_NSNUMBER, propertyTypeName, strlen(CLASS_NAME_NSNUMBER)) == 0) {
[object setValue:@0 forKey:propertyName];
// NSArray
if (strncmp(CLASS_NAME_NSARRAY, propertyTypeName, strlen(CLASS_NAME_NSARRAY)) == 0) {
[object setValue:@[] forKey:propertyName];
// 别忘了释放数组
free(properties);
} @catch (NSException *exception) {
NSLog(@&Check Entity Exception: %@&, [exception description]);
重点 - 解析property_getAttributes函数的结果
在整个处理过程中,property_getAttributes函数是关键,因为我们要首先确定Property的类型,才能根据类型赋初值,但是property_getAttributes函数返回的字符串比较“晦涩难懂”:
如下定义的Property:
@property (copy, nonatomic) NSString *@property (strong, nonatomic) NSNumber *@property (strong, nonatomic) NSArray *@property (assign, nonatomic) NSInteger@property (assign, nonatomic) CGFloat@property (assign, nonatomic) char *cS
依次通过property_getAttributes获取的结果是:
T@&NSString&,C,N,V_nameT@&NSNumber&,&,N,V_numberT@&NSArray&,&,N,V_arrayTq,N,V_iTd,N,V_fT*,N,V_cStr
我们大概可以知道,T表示Type,后面跟着@表示Cocoa对象类型,后面的表示Property的属性,如Copy、strong等,然后就是变量名。
所以getPropertyType函数的工作就是纯粹的解析字符串,获取T@后面的类型名。
例如我们有如下对象:
@interface UserEntity : NSObject@property (copy, nonatomic) NSString *@property (strong, nonatomic) NSNumber *@property (strong, nonatomic) NSArray *@end
设置默认值:
UserEntity *userEntity = [UserEntity new];// 检查属性,设置默认值。checkEntity(userEntity);// 使用...NSLog(@&name: %@&, userEntity.name);NSLog(@&number: %@&, userEntity.number);NSLog(@&array: %@&, userEntity.array);
2015-07-11 18:17:25.918 Common[6939:270543] name: 2015-07-11 18:17:25.918 Common[6939:270543] number: 02015-07-11 18:17:25.918 Common[6939:270543] array: ()
这样,一个对象的所有Property都有了初值。
上面的例子只是个粗略的版本,只是检查了字符串、数字、数组,其实完全可以扩展出很多功能,如针对不同的类型,根据对象的类型,设置不同的默认初值等,靠读者你了~
Runtime是个好东西,但是别乱用啊=。=
已发表评论数()
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
排版有问题
没有分页内容
视频无法显示
图片无法显示问题补充&&
本页链接:
而变量类型的确定是依赖于变量初值的因为Swift其实是门静态类型语言: var optionalName = nil 但这样写就麻烦了,如果你需要一个初值为nil的String变量,那么你可能会直接这么写
阳光小天使乐园 &
•回答
•回答
•回答
•回答
•回答
•回答
•回答
•回答
•回答
•回答
•回答
•回答
•回答
猜你感兴趣
服务声明: 信息来源于互联网,不保证内容的可靠性、真实性及准确性,仅供参考,版权归原作者所有!Copyright &
Powered by主题 : obj-c中用NSstringfromclass调用swift类返回nil的问题怎么解决?
级别: 新手上路
可可豆: 24 CB
威望: 23 点
在线时间: 22(时)
发自: Web Page
obj-c中用NSstringfromclass调用swift类返回nil的问题怎么解决?&&&
RT正在弄phonegap的swift和oc混合项目,phonegap库里用了NSstringfromclass这个方法,求大神指导
级别: 论坛版主
UID: 83747
发帖: 3805
可可豆: 19473 CB
威望: 19346 点
在线时间: 2246(时)
发自: Web Page
phonegap支持swift了么?没有的话只能等官方支持了,或者你不怕麻烦的话直接改phonegap的代码。可能NSStringFromClass要求参数必须是objc类,不支持swift吧。
级别: 新手上路
可可豆: 24 CB
威望: 23 点
在线时间: 22(时)
发自: Web Page
回 1楼(chenxin) 的帖子
我把他改成支持swift了,不过在处理插件调用时,就遇到我说的那个问题
级别: 新手上路
可可豆: 24 CB
威望: 23 点
在线时间: 22(时)
发自: Web Page
汗,我找到方法了,在swift定义的类前面加@objc(className) 例如:
import UIKit
import Foundation
@objc(CDVMyPlugin) class CDVMyPlugin:CDVPlugin{
&&&&var callbackID:NSString?
&&&&func pageLoad(commend:CDVInvokedUrlCommand){
&&&&&&&&println(&swift&);
本帖最近评分记录: 共1条评分记录
级别: 新手上路
可可豆: 24 CB
威望: 23 点
在线时间: 22(时)
发自: Web Page
囧,ps.我标题写错方法名了,应该是NSClassFromString
关注本帖(如果有新回复会站内信通知您)
4*5+2 正确答案:22
发帖、回帖都会得到可观的积分奖励。
按"Ctrl+Enter"直接提交
关注CocoaChina
关注微信 每日推荐
扫一扫 浏览移动版

我要回帖

更多关于 swift string nil 的文章

 

随机推荐