[Objective-C]什么牛头人-把基本类型变成对象然后动手动脚
文章目录
变成对象
包装类
Objective-C 提供 NSValue 与 NSNumber 来封装 C 的基本类型,使它们具有面向对象的特征
NSValue 与 NSNumber 都是包装类,且 NSValue 是 NSNumber 的父类;这点很好记,看名称就能理解了,Number(数值)当然是 Value[(价)值] 的一个子类
它们两个代表的个性与共性的关系,具体与通用的关系,点与面的关系,也可以通过如此类比来理解
NSValue 包装类
NSValue 代表更通用的包装类,可用于包装许多常见的数据项;通过该(NSValue)包装,可以将数据项添加到 NSArry,NSSet 等集合中
这些集合要求它们的元素必须是对象
NSNumber 包装类
NSNumber 是更具体的包装类,用于包装 C 的各种数值类型
NSNumber主要包括以下三种方法
+ numberWithXxx:
//将特定类型包装为 NSNumber
- initWithXxx:
//需要先创建一个 NSNumber 对象,再用一个基本类型初始化 NSNumber
- xxxValue:
//返回 NSNumber 对象包装的基本类型的值
其中的xxx可代表各种基本类型,详见Xcode的帮助系统
前两个方法将基本类型包装成包装类单实例,第三则是取出其值
来个程序示范一下吧
#import<Foundation/Foundation.h>
int main(int arge, char * argv []) {
@autoreleasepool {
// 调用类方法将 int 类型的值包装成 NSNumber 对象
NSNumber* num = [NSNumber numberWithInt: 20];
// 调用类方法将double类型的值包装成NSNumber对象
NSNumber* de = [NSNumber numberWithDouble: 3.4];
NSLog (@"%d", [num intValue]);
NSLog (@"%g", [de doubleValue]);
// 先创建NSNumber 对象,再调用initWithxxx:方法执行初始化
NSNumber* ch = [[NSNumber alloc] initWithChar: 'J' ] ;
//直接输出NSNumber对象,使用8@格式字符串
NSLog (@"%@" , ch);
}
}
注意: Objective-C 也提供类似自动装箱的机制,将一个整型赋值与 NSNumber 变量,当其并不完善,不支持 ARC 且不能将浮点数赋值
ARC 是 iOS 中管理引用计数的技术,帮助 iOS 实现垃圾自动回收
自动将原始类型值转换成对应的对象,比如将int的变量转换 NSNumber 对象,这个过程叫做装箱;
反之将 NSNumber 对象转换成int类型值,这个过程叫做拆箱;
因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。
基本类型
介绍几个新的基本类型
以下仍为基本类型而非包装类
NSInteger 类型
大致等于 long
NSUInteger 类型
大致等于 unsigned long
CGFloat 类型
大致等于 double
此处的大致等于是在 64 位平台下而言,详见系统文件中的定义
注意:
在64位与类似的各种平台上,使用上述基本类型的兼容性更好,因为它们的范围固定
但是在32位与64位平台之间,上述基本类型的范围不同,可能出现数据溢出,还是int, long long(或者int32_t,int64_t)这样的数据类型兼容性更好
处理对象
打印对象
NSLog 函数打印对象
NSLog 函数不仅可用于输出基本类型的值,也可用于输出 Object-C 对象,且输出的是该对象的 desperation 方法的返回值
故以下代码效果相同
NSLog(@"%@", p);
NSLog(@"%@", [p description]);
description 方法是 NSObject 类的一个实例方法
所有的 Objective-C 类都是 NSObject 类的子类
故所有的Objective-C 对象都具有description 方法
重写 description 方法
description 是一个“自我描述” 方法
当程序员直接打印该对象时,系统将会输出该对象的“自我描述” 信息
NSObiect 类提供的description 方法
总是返回十六进制的首地址
这个返回值并不能真正实现“ 自我描述” 的功能
如果用户需要自定义类实现“自我描述”的功能
则必须重写 NSObject 类的description 方法
大部分时候
我们动手写一个吧!
接口部分
#import<Foundation/Foundation.h>
@interface FKApple: NSObject
@property (nonatomic, copy)NSString* color;
@property (nonatomic, assign)double weight;
- (id)initWithColor: (NSString*) color weight: (double) weight;
@end
实现部分
#import "FKApple.h"
@implementation FKApple
- (id) initWithColor: (NSString*) color weight: (double) weight {
if (self = [super init]) {
self.color = color;
self.weight = weight;
}
return self;
}
// 重写父类的decription方法
- (NSString*) description {
// 返冋一个字符串
return [NSString stringWithFormat: @"<FKApple[_color=%@, _weight=%g]>", self.color, self.weight];
}
@end
当程序重写了 FKApple 的 description 方法之后,接下来输出 FKApple 对象时,程序实际上输出的是 FKApple 对象的 description 方法的返回值
这个 description 方法提供了足够的有效信息来描述这个 FKApple 对象,也就实现了 description 方法的作用,即对该对象进行自我描述。
判断相等
在 Objective-C 程序中测试两个变量是否相等有两种方式
一种是利用 == 运算符
另一种是利用 isEqual: 方法
== 方法判断相等
当使用 == 方法来判断两个变量是否相等时
如果两个变量是基本类型的变量
且都是数值型 ( 不一定要求数据类型严格相同 )
则只要两个变量的值相等
使用 == 判断就将返回真
注意: 对于两个指针类型的变量,它们必须指向同一个对象( 也就是两个指针变量保存的内存地址相同)时,才会返回真
#import<Foundation/Foundation.h>
int main(int arge, char* argv[]) {
@autoreleasepool {
int it = 65;
float f1 = 65.0f;
NSLog(@"65和65.0£是否相等?:%d", (it == f1)); //将输出1代表真
char ch= 'A';
NSLog(@" 65和“A,是否相等?:%d", (it == ch)); //将输出1代表真
//65、65.0f 和”A”相等
NSString* str1 = [NSString stringWithFormat: @"疯狂iOS讲义"];
NSString* str2 = [NSString stringWithFormat: @"疯狂iOS讲义"];
NSLog(@"str1和str2是否相等?%d", (str1 == str2)); //将输出0代表假
NSLog(@"strl 是否 isEqual: str2?%d", [str1 isEqual: str2]); //将输出1代表真
//str1 和 str2 都是指针类型变量
//它们分别指向两个通过 NSString 的类方法创建出来的 NSString 对象
//因此 str1 和 str2 两个变量不相等
NSLog(@"%d" , [NSDate new] == [NSString new]);
// 由于NSDate与Nsstring类没有继承关系,
// 所以将导致编译警告
}
}
对初学者而言, NSString 还有一个非常容易迷惑的地方
@"疯狂iOS讲义"直接量和 [NSString stringWithFormat: @“疯狂iOS讲义”] 有什么区别呢?
当Objective-C 程序直接使用形如 “@“疯狂iOS讲义”” 的字符串直接量时
系统将会使用常量池来管理这些字符串
常量池保证相同的字符串直接量只有一个,不会产生多个副本
使用 NSString 的 stringWithFormat: 类方法创建的字符串对象是运行时创建出来的
保存在运行时**内存区 (即堆内存)**内,不会放入常量池
可以通过下面的 s1 s2 s3 地址的异同来理解这两个的区别
int main(int agre, char* argv[]) {
@autoreleasepool {
//s1、s2直接指向常量池中的"疯狂ios"
NSString* s1 = @"疯狂iOS";
NSString* s2 = @"疯狂iOS";
//可以看出s1、s2 两个指针变量中保存的地址值完全相等
NSLog(@"s1 地址: %p, s2 地址: %p", s1, s2);
//所以下面的程序输出1代表真
NSLog(@"s1 与 s2 是否相等:%d", (s1 == s2));
//让s3指向新生成的对象
NSString* s3 = [NSString stringWithFormat: @"疯狂iOS"];
//输出 s3 指针变量中保存的地址值与 s1、s2 并不相同
NSLog(@"s3地址: %p", s3);
// 所以下面的程序输出0代表假
NSLog(@"s1 与s3是否相等:%d", (s1 == s3));
}
}
isEqual: 方法
当我们只是希望判断值相等的时候,== 方法显然就不适合我们了
此时就可以利用 NSString 对象的 isEqual: 方法来判断
isEqual: 方法是 NSObject 类提供的一个实例方法
因此所有的指针变量都可调用该方法来判断是否与其他指针变量相等
在默认情况下,NSObject 提供的 isEqual: 方法判断两个对象相等的标准同样要求两个指针变量指向同一个对象才会返回真
故 NSObject 类提供的 isEqual: 方法没有太大的实际意义,如果希望采用自定义的相等标准,则可通过重写isEqual:方法来实现
NSString 已经重写了 NSObject 的 isEqual: 方法,NSString 的 isEqual: 方法判断两个字符串相等的标准是:只要两个字符串所包含的字符序列相同,通过isEqual: 比较就将返回真
来个程序示范了重写 isEqual: 方法的实现部分
#import <Foundation/Foundation.h>
#import"FKUser.h"
@implementation FKUser
- (id)initWithName: (NSString*)name idstr: (NSString*)idstr {
if (self = [super init]) {
self->_name = name;
self->_idstr = idstr;
return self;
}
return nil;
}
//重写isEqual:方法,提供自定义的相等标准
- (BOOL)isEqual: (id)other {
//如果两个对象为同一个对象
if (self == other) {
return YES;
}
//当other不为nil, 且它是 FKUser 类的实例时
if (other != nil && [other isMemberOfClass: FKUser.class]) {
FKUser* target = (FKUser*)other;
//并且当前对象的 idstr 与 target 对象的 idstr 相等才可判新两𠆤对象相等
return [self->_idstr isEqual: target->_idstr];
}
return NO;
}
@end
上面程序重写 FKUser 类的 isEqual: 方法以指定了 FKUser 对象和其他对象相等的标准:
另 一个对象必须是FKUser 类的实例,且两个FKUser 对象的 idstr 相等
即可判断两个FKUser 对象相等
在这种判断标准下,只要两个 FKUser 对象的 idstr 相等,即可判断相等
通常而言,正确地重写 isEqual: 方法应该满足下列条件
- 自反性: 对任意 x,[x isEqual: X]一定返回真
- 对称性: 对任意 x 和 y ,如果 [y isEqual: x ] 返回真,则 [x isEqual: y] 也返回真
- 传递性: 对任意 x 和 y、z,如果有 [x isEqual: y] 返回真,[y isEqual: z] 返回真,则[x isEqual: z] 一定返回真
- 一致性: 对任意 x 和 y,如果对象中用于等价比较的关键属性没有改变,那么无论调用 [ x isEqual: y] 多少次,返回的结果都应该保持一致
- 对任何不是 nil 的 x,[x isEqual: nil] 一定返回假
实际上,NSString 不仅重写了 isEqual: 方法,用于判断两个字符串的字符序列是否相等,还定义了一个 isEqualToString: 方法,该方法专门用于判断当前宇符串与另一个宇符串的宇符序列是否相等
本章小结
- NSValue 包装类 与 NSNumber 包装类以及三种用法
- 三种新的基本类型,与什么情况适合使用这些基本类型
- 通过 NSLog 函数与 description 方法打印对象
- == 方法与 isEqual 方法的区别与使用