原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容
目录
- 一、公共用法
- 1、加载及初始化类
- 2、分配内存空间及初始化对象
- 3、给对象发送消息(执行方法)
- 4、复制对象
- 5、获取Class
- 6、判断方法
- 7、重写对象的系统方法
- 8、SharedApplication
- 9、NSProxy
- 二、头部声明
- 1、常量和宏
- 2、枚举
- 3、属性关键字
- 三、基本数据类型
- 1、Null/nil
- 2、BOOL
- 3、NSNumber
- 4、NSData
- 5、CG类型
- 6、路径
- 四、字符串
- 1、范围
- 2、格式
- 3、拷贝
- 4、替换
- 5、比较
- 6、路径
- 7、字符串转变
- 8、字符串属性
- 9、可变字符串
- 10、富文本
- 五、集合
- 1、NSSet
- 2、NSDictionary
- 3、NSArray
- 4、可变数组的实现原理
- Demo
- 参考文献
1、公共用法
1、加载及初始化类
// 运行时加载类或分类调用该方法, 每个类只会调用一次
+ (void)load;
// 类实例化使用前需要先初始化, 一个类调用一次, 如果子类没有实现该方法则会调用父类方法
+ (void)initialize;
load
是只要类所在文件被引用就会被调用,而initialize
是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load
调用;但即使类文件被引用进来,但是没有使用,那么initialize
也不会被调用。
load
每个类只会调用一次,initialize
也只调用一次,但是如果子类没有实现initialize
方法则会调用父类的方法,因此作为父类的initialize
方法可能会调用多次。
2、分配内存空间及初始化对象
Student *student = [Student new];
Student *student2 = [[Student alloc] init];
Student *student3 = [[Student allocWithZone:nil] init];
创建新对象时,首先调用alloc
为对象分配内存空间,再调用init
初始化对象,如[[NSObject alloc] init]
。而new
方法先给新对象分配空间然后初始化对象,因此[NSObject new]
等同于[[NSObject alloc] init]
。关于allocWithZone
方法,官方文档解释该方法的参数是被忽略的,正确的做法是传nil
或者NULL
参数给它。
3、给对象发送消息(执行方法)
a、直接调用
// 调用无参无返回值方法
[student running];
// 调用有参无返回值方法
[student readingWithText:@"Hello World!"];
// 调用有参有返回值方法
NSNumber *sum = [student sumWithNum:@(2) num2:@(3)];
我们通常都采用这种直接调用的方式,给对象发消息执行方法。这种方式调用编译时会自动校验方法、参数、返回值是否正确。因此我们必须在头文件中声明方法的使用。
b、使用 performSelector 执行
// 先判断对象是否能调用方法,再执行调用方法
if ([student respondsToSelector:@selector(running)]) {
// 调用无参无返回值方法
[student performSelector:@selector(running)];
}
if ([student respondsToSelector:@selector(readingWithText:)]) {
// 调用有参无返回值方法
[student performSelector:@selector(readingWithText:) withObject:@"Hello World"];
}
if ([student respondsToSelector:@selector(sumWithNum:num2:)]) {
// 调用有参有返回值方法
NSNumber *sum = [student performSelector:@selector(sumWithNum:num2:) withObject:@(2) withObject:@(8)];
}
使用performSelector:
是运行时系统负责去找方法,在编译时候不做任何校验;因此在使用时必须先使用respondsToSelector:
检查对象是否能调用方法,否则可能出现运行崩溃。performSelector:
常用于调用运行时添加的方法,即编译时不存在,但是运行时候存在的方法。另外需要注意的是performSelector:
系统提供最多接受两个参数的方法,而且参数和返回都是id
类型,并不支持基础数据类型(如:int
, float
等)。
c、使用 IMP 指针调用
// 创建SEL
SEL runSel = @selector(running);
SEL readSel = NSSelectorFromString(@"readingWithText:");
SEL sumSel = NSSelectorFromString(@"sumWithNum:num2:");
// 调用无参无返回值方法
IMP rumImp = [student methodForSelector:runSel];
void (*runFunc)(id, SEL) = (void *)rumImp;
runFunc(student, runSel);
// 调用有参无返回值方法
IMP readImp = [[student class] instanceMethodForSelector:readSel];
void (*speakFunc)(id, SEL, NSString *) = (void *)readImp;
speakFunc(student, readSel, @"Hello World");
// 调用有参有返回值方法
IMP sumImp = [student methodForSelector:sumSel];
NSNumber *(*sumFunc)(id, SEL, NSNumber *, NSNumber *) = (void *)sumImp;
NSNumber *sum3 = sumFunc(student, sumSel, @(6), @(6));
SEL
是方法的索引。IMP
是函数指针,指向方法的地址。SEL
与IMP
是一一对应的关系,因此我们可以通过修改对应关系达到运行时方法交换的目的。
创建 SEL 对象两种方法
- 使用
@selector()
创建 - 使用
NSSelectorFromString()
创建
获取方法 IMP 指针两种方法
-
- (IMP)methodForSelector:(SEL)aSelector;
类方法 -
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
实例方法
4、复制对象
copy
拷贝为不可变对象,mutableCopy
拷贝为可变对象。虽然copy
对静态对象只是引用计数加1,但是并不影响我们对复制前后的对象进行使用。需要注意的是对于容器对象而言,这两个方法只是复制了容器本身,对容器中包含的对象只是简单的指针引用,并没有深层复制。
// 复制人物
- (instancetype)copyWithZone:(NSZone *)zone
{
Person *person = [[Person alloc] init];
person.firstName = self.firstName;
person.lastName = self.lastName;
return person;
}
// 使用复制
- (void)useCopy
{
// 两个源数组
NSArray *sourceArrayI = [NSArray arrayWithObjects:@"I", @"II", nil];
NSMutableArray *sourceArrayM = [NSMutableArray arrayWithObjects:@"M", @"MM", nil];
// 两个copy
NSArray *copyArrayI = [sourceArrayI copy];
NSArray *copyArrayM = [sourceArrayM copy];
// 两个mutableCopy
NSMutableArray *mutableArrayI = [sourceArrayI mutableCopy];
NSMutableArray *mutableArrayM = [sourceArrayM mutableCopy];
// 对象拷贝
Person *aPerson = [[Person alloc] init];
Person *copyPerson = [aPerson copy];
NSLog(@"源对象为:%@",aPerson);
NSLog(@"copy对象为:%@",copyPerson);
NSLog(@"copy对象的姓名为:%@", [copyPerson fullName]);
NSLog(@"sourceArrayI为:%p",sourceArrayI);
NSLog(@"sourceArrayM为:%p",sourceArrayM);
NSLog(@"copyArrayI为:%p",copyArrayI);
NSLog(@"copyArrayM为:%p",copyArrayM);
NSLog(@"mutableArrayI为:%p", mutableArrayI);
NSLog(@"mutableArrayM为:%p", mutableArrayM);
}
输出结果为:
2020-10-20 17:10:50.942504+0800 BasicGrammarDemo[27278:4928170] 源对象为:<Person: 0x600002515320>
2020-10-20 17:10:50.942579+0800 BasicGrammarDemo[27278:4928170] copy对象为:<Person: 0x600002515340>
2020-10-20 17:10:50.942638+0800 BasicGrammarDemo[27278:4928170] copy对象的姓名为:LuoMei Bai
2022-02-16 21:30:52.354940+0800 OCDemo[30024:1349421] sourceArrayI为:0x600003232da0
2022-02-16 21:30:52.355093+0800 OCDemo[30024:1349421] sourceArrayM为:0x600003c494d0
2022-02-16 21:30:52.355205+0800 OCDemo[30024:1349421] copyArrayI为:0x600003232da0
2022-02-16 21:30:52.355325+0800 OCDemo[30024:1349421] copyArrayM为:0x600003233560
2022-02-16 21:30:52.355434+0800 OCDemo[30024:1349421] mutableArrayI为:0x600003c49800
2022-02-16 21:30:52.355545+0800 OCDemo[30024:1349421] mutableArrayM为:0x600003c49c20
5、获取Class
// 获取类
Class curClass = [student class];
// 获取父类
Class supClass = [student superclass];
6、判断方法
在OC的世界中,除了NSProxy
类以外,所有的类都是NSObject
的子类。在Foundation框架下,NSObject
和NSProxy
两个基类,定义了类层次结构中该类下方所有类的公共接口和行为。NSProxy
是专门用于实现代理对象的类,这个类暂时本篇文章不提。这两个类都遵循了NSObject
协议。在NSObject
协议中,声明了所有OC对象的公共方法。在NSObject
协议中,有以下5个方法,是可以从Runtime
中获取信息,让对象进行自我检查。
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'anObject.dynamicType' instead");
- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
- (BOOL)respondsToSelector:(SEL)aSelector;
-
class
方法返回对象的类; -
isKindOfClass:
和isMemberOfClass:
方法检查对象是否存在于指定的类的继承体系中(是否是其子类或者父类或者当前类的成员变量); -
respondsToSelector:
检查对象能否响应指定的消息; -
conformsToProtocol:
检查对象是否实现了指定协议类的方法;
在NSObject
的类中还定义了一个方法- (IMP)methodForSelector:(SEL)aSelector;
这个方法会返回指定方法实现的地址IMP
。
a、声明两个继承关系的类
@interface Person : NSObject
- (void)run;
@end
@interface Student : Person
@end
@implementation Person
- (void)run
{
NSLog(@"滚 能换一种说法吗? 蹿吧,孩儿。能文明一点吗?去吧,皮卡丘。能高大上一点吗?奔跑吧,兄弟。能再上档次点吗?世界这么大,你怎么不去看看。能有点诗意吗?你来人间一趟,你要看看太阳。能有点鼓动性吗?奔涌吧,后浪!");
}
@end
@implementation Student
@end
b、使用判断方法
- (void)useJudgment
{
// 初始化对象
Person *person = [Person new];
Student *studentXie = [Student new];
Student *studentFan = studentXie;
// 判断对象是否是个代理
if ([studentXie isProxy])
{
NSLog(@"student对象是个代理");
}
// 判断两个对象是否相等
if ([studentXie isEqual:studentFan])
{
NSLog(@"studentXie对象与studentFan对象相等,两位同学可能是同一个人");
}
// 判断对象是否是指定类
if ([person isKindOfClass:[Person class]])
{
NSLog(@"person对象是Person类,即人是人类");
}
// 判断对象是否是指定类或子类
if ([studentXie isKindOfClass:[Person class]])
{
NSLog(@"studentXie对象是Person类的子类,谢同学再颓废没有生气也是个活生生的人呀");
}
// 判断某个类是否是另一个类的子类
if ([Student isSubclassOfClass:[Person class]])
{
NSLog(@"Student类是Person类的子类");
}
// 判判断对象是否遵从协议
if ([studentXie conformsToProtocol:@protocol(NSObject)])
{
NSLog(@"studentXie对象遵循NSObject协议");
}
// 判断类是否遵从给定的协议
if ([Student conformsToProtocol:@protocol(NSObject)])
{
NSLog(@"Student类遵循NSObject协议");
}
// 判断对象是否能够调用给定的方法
if ([studentXie respondsToSelector:@selector(run)])
{
NSLog(@"student对象可以调用run方法");
}
// 判断实例是否能够调用给定的方法
if ([Student instancesRespondToSelector:@selector(run)])
{
NSLog(@"Student类可以调用run方法");
}
}
输出结果
2020-10-20 16:46:46.061658+0800 BasicGrammarDemo[26898:4909800] studentXie对象与studentFan对象相等,两位同学可能是同一个人
2020-10-20 16:46:46.061769+0800 BasicGrammarDemo[26898:4909800] person对象是Person类,即人是人类
2020-10-20 16:46:46.061831+0800 BasicGrammarDemo[26898:4909800] studentXie对象是Person类的子类,谢同学再颓废没有生气也是个活生生的人呀
2020-10-20 16:46:46.061902+0800 BasicGrammarDemo[26898:4909800] Student类是Person类的子类
2020-10-20 16:46:46.061988+0800 BasicGrammarDemo[26898:4909800] studentXie对象遵循NSObject协议
2020-10-20 16:46:46.062055+0800 BasicGrammarDemo[26898:4909800] Student类遵循NSObject协议
2020-10-20 16:46:46.062127+0800 BasicGrammarDemo[26898:4909800] student对象可以调用run方法
2020-10-20 16:46:46.062196+0800 BasicGrammarDemo[26898:4909800] Student类可以调用run方法
7、重写对象的系统方法
a、重写系统方法
@interface Animal : NSObject <NSCopying>
@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) NSString *age;
@end
@implementation Animal
// 重写isEqual:
- (BOOL)isEqual:(id)object
{
// 自身
if (self == object)
{
return YES;
}
// 类型相同
if ([object class] == [Animal class])
{
// 转化
Animal *target = (Animal *)object;
// 设置判断标准
BOOL result = ([self.name isEqualToString:target.name] && (self.age == target.age));
return result;
}
return NO;
}
// 重写hash
- (NSUInteger)hash
{
NSUInteger nameHash = (self.name == nil ? 0 : [self.name hash]);
NSUInteger ageHash = (self.age == nil ? 0 : [self.age hash]);
return nameHash * 31 + ageHash;
}
// 重写description
- (NSString *)description
{
return [NSString stringWithFormat:@"name:%@,age:%@", self.name,self.age];
}
// 重写copyWithZone
- (id)copyWithZone:(NSZone *)zone
{
Animal *new = [[[self class] allocWithZone:zone] init];
new.name = self.name;
new.age = self.age;
return new;
}
@end
b、调用方式
- (void)useCover
{
Animal *animal = [[Animal alloc] init];
animal.name = @"xiejiapei";
animal.age = @"23";
NSLog(@"重写了description后,源对象为:%@",[animal description]);
NSLog(@"源对象hash为:%lu",(unsigned long)[animal hash]);
Animal *copyAnimal = [animal copy];
NSLog(@"copy对象为:%@",copyAnimal);
if ([copyAnimal isEqual:animal])
{
NSLog(@"两个对象相等");
}
}
输出结果为:
2020-11-03 10:05:35.037203+0800 FoundationDemo[93106:3989340] 重写了description后,源对象为:name:xiejiapei,age:23
2020-11-03 10:05:35.037308+0800 FoundationDemo[93106:3989340] 源对象hash为:3770145254414366752
2020-11-03 10:05:35.037378+0800 FoundationDemo[93106:3989340] copy对象为:name:xiejiapei,age:23
2020-11-03 10:05:35.037432+0800 FoundationDemo[93106:3989340] 两个对象相等
8、SharedApplication
- (void)useSharedApplication
{
// 获得UIApplicationDelegate对象
[[UIApplication sharedApplication] delegate];
// 获得UIWindow对象
[[UIApplication sharedApplication] keyWindow];
// 打开设置界面
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
// 远程的控制相关
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
// 不让手机休眠
[UIApplication sharedApplication].idleTimerDisabled = YES;
// 后台剩余时间
NSTimeInterval remainTime = [[UIApplication sharedApplication] backgroundTimeRemaining];
NSLog(@"剩余时间 = %f",remainTime);
// 后台刷新的状态
[[UIApplication sharedApplication] backgroundRefreshStatus];
// 开启/关闭任务
[[UIApplication sharedApplication] beginBackgroundTaskWithName:@"task" expirationHandler:^{
}];
[[UIApplication sharedApplication] endBackgroundTask:1];
}
9、NSProxy
我们会利用runtime
消息转发机制创建一个动态代理。利用这个动态代理来转发消息。这里我们会用到两个基类的另外一个神秘的类,NSProxy
。
NSProxy
类和NSObject
同为OC里面的基类,但是NSProxy
类是一种抽象的基类,无法直接实例化,可用于实现代理模式。它通过实现一组经过简化的方法,代替目标对象捕捉和处理所有的消息。NSProxy
类也同样实现了NSObject
的协议声明的方法,而且它有两个必须实现的方法。
- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");
另外还需要说明的是,NSProxy
类的子类必须声明并实现至少一个init
方法,这样才能符合OC中创建和初始化对象的惯例。Foundation
框架里面也含有多个NSProxy
类的具体实现类。NSDistantObject
类定义其他应用程序或线程中对象的代理类。NSProtocolChecker
类定义对象,使用这对象可以限定哪些消息能够发送给另外一个对象。
二、头部声明
1、常量和宏
const NSString *constString = @"谢佳培";
NSString *const stringConst = @"甜的";
// 常用宏定义的颜色、字体字号
#define ROW_SIZE 20
#define Blue [UIColor colorWithRed:(0x##r)/255.0 green:(0x##g)/255.0 blue:(0x##b)/255.0 alpha:1]
// 宏定义获取rootViewController
#define RootVC [UIApplication sharedApplication].delegate.window.rootViewController
// 宏定义获取当前的界面
#define TopVC ([RootVC isKindOfClass:[UITabBarController class]]?[((UITabBarController *)RootVC).selectedViewController topViewController]:RootVC)
- 宏在预编译时处理(宏在编译开始之前就会被替换),而
const
会在编译时被处理 -
#define
宏没有类型,宏不做任何类型检查,不会报编译错误,只是替换,而const
常量有具体的类型,会编译检查,会报编译错误 - 宏能定义一些函数和方法,而
const
不能 - 使用大量宏,容易造成编译时间久,因为每次都需要重新替换
- 宏定义时不分配内存,变量定义时分配内存
- 预编译期间进行宏替换,分配内存,再进行宏替换,又一次分配内存,内存中有若干个拷贝。
const doulbe Pi
——double i=Pi;
此时为Pi
分配内存,以后不再分配!只有一份拷贝(因为是全局的只读变量,存在静态区)
宏
开发过程中的经常需要使用一些全局的常量,便于方法之间的参数传递和类型确定等,经常使用到的有宏定义,const
常量以及枚举等,我们经常使用宏定义来进行全局常量的定义,但是是不是所有的全局常量宏定义都是最好的选择呢?宏定义是我们最经常使用的全局常量定义方法,使用非常便捷,在使用过程中需要注意以下事项:
- 宏定义是预编译处理,在程序开始编译之前就已经完成
- 宏定义只是进行字符替换,并没有优先级限制,比如
#define add(x,y)x + y
。在使用时如果使用了add(3,4)*add(3,4)
,宏定义替换之后的结果是3+4*3+4=19
,而不是(3+4)*(3+4) =49
,一定要注意!!! - 宏定义不做类型的语法检查,宏定义中宏的所有参数都是没有类型的,可能会存在重复定义的问题,如果重复定义了相同名称的宏,程序只是会给出警告,而不会影响编译,所以如果你定义了同名称两个宏,而替换方式不一样的话,是很难发现的。
- 宏定义不是变量定义,所以并不分配内存
- 宏定义只是字符串替换,结尾不需要使用使用分号结束
const
const
是变量修饰符号,在定义全局变量的时候,应该优先考虑static
加上const
来替换宏定义常量,原因有以下几点:
- 当项目比较大的时候,过多的宏定义会使项目的编译变得缓慢;
-
const
修饰变量在编译期间会进行语法检查,可以防止意外的类型错误,以及重复定义,可以使定义更加安全; -
const
修饰变量时,该变量不允许改变,可以防止常量被意外修改; - 在外部需要使用的地方,只需要使用
extern
关键字扩大变量的作用域,使得变量的访问更加灵活。
对于const
的使用,需要注意以下事项:
-
const
修饰普通变量,表示该变量为常量,使用过程中不允许修改; -
const
修饰指针类变量;含有地址的变量时,以*
分为左右两部分,左侧部分含有const
修饰符号时,内容不能修改;右侧部分有const
修饰时,该变量地址不能改变,如果两侧都有const
修饰,则地址和内容都不可变; -
const
可以修饰形参,被修饰的参数在方法内部不能被修改; - 被
const
修饰的变量的作用域,可以通过extern
进行扩展,不需要进行初始化和分配空间,只是告诉编译器该变量会在其他地方进行定义。
2、枚举
-
typedef
是类型替换,直接参与编译,而define
只是简单的文本替换 -
define
写在方法/函数中则作用域从写的地方开始有效,直至使用#undef
(不写此指令则后面一直有效),typedef
写在方法/函数中则作用域只在此方法/函数中有效
给block
取别名MyBlock
:
typedef void(^MyBlock) (int a,int b);
C语言格式
// 给NSTimeInterval取别名为MyTime
typedef NSTimeInterval MyTime;
// c语言格式,给Person结构体取别名为MyPerson
// 使用:MyPerson p = {"jack"};
typedef struct Person {
char *name;
} MyPerson;
// c语言格式,给Gender枚举取别名为MyGender
// 使用:MyGender g = Man;
typedef enum Gender {
Man,
Woman
} MyGender;
OC语言格式
对于互斥(不能同时具有两种状态)表示的多种整型变量,一般推荐使用NS_ENUM
来表示,使用很简洁,也很方便。比如文本在竖直方向的对齐状态。这种表示的优点是可以将多种实用普通整型数字不易区分的状态,用易于区分的字符串表示,见名知意,不易出错,方便表示。
typedef NS_ENUM(NSInteger, NumberType)
{
NumberTypeInt = 0,
NumberTypeFloat = 1,
NumberTypeDouble = 2
};
NumberType type = NumberTypeInt;
但是这种表示方法的不足之处就是各种状态之间不能并存,但是有些时候我们需要使用多种状态同时存在,比如选择了一个通信录里的联系人需要获取的信息类型。
如果你需要同时选定姓名和联系方式,只需要定义_enums = APContactFieldFirstName|APContactFieldLastName|APContactFieldPhones
就可以了,
那么问题来了,对于这种可以同时选择多个属性的枚举类型,如何确定某个枚举值(比如APContactFieldFirstName
)是不是包含在选定枚举(比如_enums
)中呢?
只需要将(APContactFieldFirstName
)&(_enums
)运算,如果其值为真,则包含,否则为假。
typedef NS_OPTIONS(NSUInteger, TMEnumTest)
{
TMEnumTestOne = 0, // 0
TMEnumTestTwo = 1 << 0, // 1
TMEnumTestThree = 1 << 1, // 2
TMEnumTestFour = 1 << 2, // 4
};
TMEnumTest test = TMEnumTestTwo | TMEnumTestThree;
typedef NS_OPTIONS(NSUInteger, LifeRoleOptions)
{
LifeRoleOptionsFather = 1UL << 0,
LifeRoleOptionsSon = 1UL << 1,
LifeRoleOptionsHusband = 1UL << 3,
};
LifeRoleOptions lifeRole = LifeRoleOptionsFather | LifeRoleOptionsSon;
使用方式
- (void)useTypedef
{
// 添加TMEnumTestFour到test中
test += TMEnumTestFour;
// 将TMEnumTestThree从test中去除
test -= TMEnumTestThree;
// 判断 TMEnumTestFour枚举 是否被包含
if (test & TMEnumTestFour)
{
NSLog(@"数字是:四");
}
// 判断 TMEnumTestThree枚举 是否被包含
if (test & TMEnumTestThree)
{
NSLog(@"数字是:三");
}
if (lifeRole & LifeRoleOptionsFather)
{
NSLog(@"人生角色:父亲");
}
if (lifeRole & LifeRoleOptionsHusband)
{
NSLog(@"人生角色:丈夫");
}
}
输出结果
2020-10-20 15:24:20.763915+0800 BasicGrammarDemo[25511:4841344] 数字是:四
2020-10-20 15:24:20.764021+0800 BasicGrammarDemo[25511:4841344] 人生角色:父亲
3、属性关键字
类型
- atomic:系统默认,声明的属性保证赋值和获取是线程安全的,不保证添加和移除是线程安全的(如数组)
- noatomic:如果属性经常被使用的话通常声明为此属性关键字
- retain/strong
-
assign/unsafe_unretained:修饰基本数据类型,如
int
、Bool
。修饰对象类型时候,不改变其引用计数。对象释放后,会产生悬垂指针,继续访问会产生内存泄露 -
weak:不改变被修饰对象的引用计数,对象释放后,自动置为
nil
-
copy浅拷贝和深拷贝的区别:是否开辟了新的内存空间,是否影响了引用计数。可变对象
copy
后是NSArray
不可以调用add delete
等方法。只有不可变对象的copy
是浅拷贝,其他都是深拷贝
属性声明
//默认关键字
@property (atomic, strong, readwrite) NSString *firstName;
@property (readonly, getter=isShit) BOOL shit;
@property (nonatomic, strong) NSArray *vcTitles;
@property (nonatomic, strong) NSArray<NSArray *> *pickerData;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) personBlock runFinishBlock;
@property (nonatomic, weak) id<WorkProtocol> delegate;
@property (nonatomic, weak) id<CountToolDataSource> dataSource;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) BOOL bodyTextChanged;
@property (nonatomic, assign) NSTimeInterval created;
使用演示
@interface PersonClass : NSObject
@property (nonatomic, strong) NSObject *objStrong;
@property (nonatomic, weak) NSObject *objWeak;
@property (nonatomic, assign) NSObject *objAssign;
@property (nonatomic, copy) NSString *objCopy;
@end
// 使用属性关键字
- (void)useAttributeKey
{
PersonClass *aPerson = [[PersonClass alloc] init];
aPerson.objStrong = [[NSObject alloc] init];
// 系统会警告,但不会报错
aPerson.objWeak = [[NSObject alloc] init];
aPerson.objAssign = [[NSObject alloc] init];
NSMutableString *testStr = [[NSMutableString alloc] initWithString:@"我知道什么呢?"];
aPerson.objCopy = testStr;
[aPerson setObjCopy:testStr];
[testStr appendString:@"什么都知道"];
// 正确的方式
NSLog(@"objStrong %@", aPerson.objStrong);
// weak会释放为null
NSLog(@"objWeak %@", aPerson.objWeak);
// Assign会崩溃
// NSLog(@"objAssign %@", aPerson.objAssign);
// Copy后,原字符串改变对新字符串无影响
NSLog(@"testStr %@", testStr);
NSLog(@"objCopy %@", aPerson.objCopy);
}
输出结果
2020-10-20 15:38:17.996696+0800 BasicGrammarDemo[25777:4854926] objStrong <NSObject: 0x60000006c1b0>
2020-10-20 15:38:17.996794+0800 BasicGrammarDemo[25777:4854926] objWeak (null)
2020-10-20 15:38:17.996872+0800 BasicGrammarDemo[25777:4854926] testStr 我知道什么呢?什么都知道
2020-10-20 15:38:17.996936+0800 BasicGrammarDemo[25777:4854926] objCopy 我知道什么呢?
三、基本数据类型
1、Null/nil
// 默认值: NO/0/nil
Person *aPerson = nil;
aPerson.age // 0
// 用在不能在数组和词典对象中放入nil ,又确实需要一个特殊的对象来表示空值
+ (NSNull *) null
Nil
nil
的定义是null pointer to object-c object
,指的是一个OC对象指针为空,本质就是(id)0
,是OC对象的字面0值。不过这里有必要提一点就是OC中给空指针发消息不会崩溃的语言特性,原因是OC的函数调用都是通过objc_msgSend
进行消息发送来实现的,相对于C和C++来说,对于空指针的操作会引起Crash
的问题,而objc_msgSend
会通过判断self来决定是否发送消息,如果self
为nil
,那么直接返回,所以不会出现问题。
Class class = [NSString class];
if (class != Nil) {
NSLog(@"class name: %@", class);
}·
NSNull
NSNull
包含了唯一一个方法+(NSNull)null
。[NSNull null]
是一个对象,用来表示零值的单独的对象。NSNull
主要用在不能使用nil
的场景下,比如NSMutableArray
是以nil
作为数组结尾判断的,所以如果想插入一个空的对象就不能使用nil
,NSMutableDictionary
也是类似,我们不能使用nil
作为一个object
,而要使用NSNull
。
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
NSString *nameOne = @"Allen";
NSString *nameTwo = [NSNull null]; //not use nil
NSString *nameThree = @"Tom";
[dictionary setObject:nameOne forKey:@"nameOne"];
[dictionary setObject:nameTwo forKey:@"nameTwo"];
[dictionary setObject:nameThree forKey:@"nameThree"];
NSLog(@"names: %@", dictionary);
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:nameOne];
[array addObject:nameTwo];
[array addObject:nameThree];
NSLog(@"names : %@", array);
2、BOOL
BOOL hidden = YES;
BOOL bigger = 0 > 1;
if (-1)
{
NSLog(@"not 0 = YES");
}
NSObject *boolObj = [[NSObject alloc] init];
if (boolObj)
{
NSLog(@"not nil = YES");
}
输出结果为:
2020-10-21 11:40:25.715805+0800 BasicGrammarDemo[31446:5105441] not 0 = YES
2020-10-21 11:40:25.715873+0800 BasicGrammarDemo[31446:5105441] not nil = YES
3、NSNumber
// 用语法糖进行声明,可转换为多种数据类型
NSNumber *intNumber = @(-1);
NSNumber *boolNumber = @(YES);
NSNumber *charNumber = @('A');
NSLog(@"int(-1)值:%@,bool(YES)值:%@ ,char(A)值:%@", intNumber, boolNumber, charNumber);
NSLog(@"字面A的charValue值:%d,stringValue值:%@,intValue值:%d", charNumber.charValue, charNumber.stringValue, charNumber.intValue);
输出结果为:
2020-10-21 11:44:24.847241+0800 BasicGrammarDemo[31501:5108050] int(-1)值:-1,bool(YES)值:1 ,char(A)值:65
2020-10-21 11:44:24.847298+0800 BasicGrammarDemo[31501:5108050] 字面A的charValue值:65,stringValue值:65,intValue值:65
4、NSData
NSString *dataString = @"XieJiaPei";
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
NSLog(@"string(XieJiaPei) 转化为 data:%@", data);
NSData *zeroData = [NSData data];
NSLog(@"空数据为:%@", zeroData);
NSMutableData *appendData = [zeroData mutableCopy];
[appendData appendData:data];
NSLog(@"在空数据后追加数据后结果为:%@", appendData);
输出结果为:
2020-10-21 13:44:31.226384+0800 BasicGrammarDemo[32605:5160936] string(XieJiaPei) 转化为 data:{length = 9, bytes = 0x5869654a6961506569}
2020-10-21 13:44:31.226465+0800 BasicGrammarDemo[32605:5160936] 空数据为:{length = 0, bytes = 0x}
2020-10-21 13:44:31.226546+0800 BasicGrammarDemo[32605:5160936] 在空数据后追加数据后结果为:{length = 9, bytes = 0x5869654a6961506569}
5、CG类型
- (void)useCG
{
// 状态栏高度
CGFloat statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;
NSLog(@"状态栏高度:%f",statusBarHeight);
// 如何把一个CGPoint存入数组里
CGPoint point = CGPointMake(0, 0);
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:NSStringFromCGPoint(point), nil];
point = CGPointFromString(array[0]);
NSLog(@"如何把一个CGPoint存入数组里: %@",array);
}
输出结果为:
2020-10-30 13:40:44.198593+0800 FoundationDemo[42880:1608020] 状态栏高度:44.000000
2020-10-30 13:40:44.198740+0800 FoundationDemo[42880:1608020] 如何把一个CGPoint存入数组里: (
"{0, 0}"
)
四、字符串
1、范围
a、NSMakeRange
NSRange range = NSMakeRange(2, 4);
NSString *fullString = @"abcdefghijk";
NSString *subString = [fullString substringWithRange:range];
NSLog(@"原字符串为abcdefghijk,其(2,4)范围内的子字符串为:%@", subString);
输出结果为:
2020-10-21 13:52:18.515144+0800 BasicGrammarDemo[32785:5169598] 原字符串为abcdefghijk,其(2,4)范围内的子字符串为:cdef
b、substringToIndex
NSString *intString = @"456789";
NSString *charString = @"abcdef";
NSString *chineseString = @"中文哈哈哈哈";
NSString *subIntString = [intString substringToIndex:2];
NSString *subCharString = [charString substringToIndex:2];
NSString *subChineseString = [chineseString substringToIndex:2];
NSLog(@"substringToIndex:2,456789的子串为:%@,abcdef的子串为:%@,中文哈哈哈哈的子串为:%@", subIntString,subCharString,subChineseString);
输出结果为:
2020-10-21 13:57:36.238543+0800 BasicGrammarDemo[32871:5174143] substringToIndex:2,456789的子串为:45,abcdef的子串为:ab,中文哈哈哈哈的子串为:中文
c、substringFromIndex
NSString *subFromString = [chineseString substringFromIndex:2];
NSLog(@"原字符串为中文哈哈哈哈,FromIndex:2范围内的子字符串为:%@", subFromString);
输出结果为:
2020-10-21 14:14:07.313580+0800 BasicGrammarDemo[33183:5188958] 原字符串为中文哈哈哈哈,FromIndex:2范围内的子字符串为:哈哈哈哈
2、格式
a、stringWithFormat
NSString *stringWithFormat = [NSString stringWithFormat:@"字符串为: %@, 两位小数的浮点数为: %1.2f",@"XieJiaPei", 31415.9265];
NSLog(@"带格式的字符串 = %@", stringWithFormat);
输出结果为:
2020-10-21 14:04:51.764665+0800 BasicGrammarDemo[33017:5180791] 带格式的字符串 = 字符串为: XieJiaPei, 两位小数的浮点数为: 31415.93
b、stringByAppendingFormat
NSNumber *number = @12345;
NSDictionary *dictionary = @{@"date": [NSDate date]};
NSString *baseString = @"Test: ";
NSString *stringByAppendingFormat = [baseString stringByAppendingFormat:@"数字: %@, 字典: %@", number, dictionary];
NSLog(@"追加格式字符串 = %@", stringByAppendingFormat);
输出结果为:
2020-10-21 14:09:38.901710+0800 BasicGrammarDemo[33102:5185253] 追加格式字符串 = Test: 数字: 12345, 字典: {
date = "2020-10-21 06:09:38 +0000";
}
3、拷贝
NSString *intString = @"456789";
NSString *charString = @"abcdef";
NSString *chineseString = @"中文哈哈哈哈";
NSString *copyIntString = [intString copy];
NSString *copyCharString = [charString copy];
NSString *copyChineseString = [chineseString copy];
NSLog(@"复制后的字符串,%@,%@,%@", copyIntString,copyCharString,copyChineseString);
输出结果为:
2020-10-21 14:01:51.381827+0800 BasicGrammarDemo[32960:5177962] 复制后的字符串,456789,abcdef,中文哈哈哈哈
4、替换
NSString *chineseString = @"中文哈哈哈哈";
NSString *replaceChineseString = [chineseString stringByReplacingOccurrencesOfString:@"哈" withString:@"好"];
NSLog(@"原字符串为中文哈哈哈哈,替换后为:%@", replaceChineseString);
NSString *pureChineseString = [chineseString stringByReplacingOccurrencesOfString:@"哈" withString:@""];
NSLog(@"原字符串为中文哈哈哈哈,用替换的方式进行删除后为:%@", pureChineseString);
输出结果为:
2020-10-21 14:18:03.822172+0800 BasicGrammarDemo[33238:5192253] 原字符串为中文哈哈哈哈,替换后为:中文好好好好
2020-10-21 14:18:03.822240+0800 BasicGrammarDemo[33238:5192253] 原字符串为中文哈哈哈哈,用替换的方式进行删除后为:中文
5、比较
NSString *abc = @"abc";
NSString *aBc = @"aBc";
NSString *aB = @"aB";
NSComparisonResult abcOrder = [abc compare:aBc];
NSComparisonResult aBOrder = [aB compare:aBc];
NSLog(@"abc compare:aBc的结果为:%ld,即abc > aBc",(long)abcOrder);
NSLog(@"aB compare:aBc的结果为:%ld,即aB < aBc",(long)aBOrder);
// 不区分大小写进行比较
NSLog(@"abc和aBc不区分大小写进行比较:%ld",(long)[abc caseInsensitiveCompare:aBc]);
输出结果为:
2020-10-21 14:22:18.913094+0800 BasicGrammarDemo[33347:5197161] abc compare:aBc的结果为:1,即abc > aBc
2020-10-21 14:22:18.913165+0800 BasicGrammarDemo[33347:5197161] aB compare:aBc的结果为:-1,即aB < aBc
2021-03-14 13:40:26.201521+0800 FoundationDemo[30079:5574788] abc和aBc不区分大小写进行比较:0
6、路径
a、hasPrefix / hasSuffix
if ([aBc hasSuffix:@"Bc"])
{
NSLog(@"aBc的后缀为Bc");
}
if ([aBc hasPrefix:@"aB"])
{
NSLog(@"aBc的前缀为aB");
}
输出结果为:
2020-10-21 14:25:09.571763+0800 BasicGrammarDemo[33434:5201268] aBc的后缀为Bc
2020-10-21 14:25:09.571853+0800 BasicGrammarDemo[33434:5201268] aBc的前缀为aB
b、stringByAppendingPathComponent
NSString *homePath = NSHomeDirectory();// 能够访回当前用户的主目录
NSString *workPath = [homePath stringByAppendingPathComponent:@"LuckCoffee"];
NSLog(@"瑞幸咖啡的存储路径为:%@", workPath);
输出结果为:
2020-10-21 14:28:37.083043+0800 BasicGrammarDemo[33522:5205708] 瑞幸咖啡的存储路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/202BCFAD-50E8-499B-833C-93426D5EAB1E/LuckCoffee
c、lastPathComponent/pathExtension
NSString *filePath = @"/tmp/image/cat.tiff";
NSLog(@"路径为:/tmp/image/cat.tiff,最后一个部分为:%@,文件的扩展名为:%@",[filePath lastPathComponent],[filePath pathExtension]);
输出结果为:
2020-10-21 15:14:28.535638+0800 BasicGrammarDemo[34137:5237242] 路径为:/tmp/image/cat.tiff,最后一个部分为:cat.tiff,文件的扩展名为:tiff
7、字符串转变
a、componentsSeparatedByString / componentsJoinedByString
// 分割
NSArray *pathArray = [homePath componentsSeparatedByString:@"/"];
NSLog(@"NSHomeDirectory的路径为:%@", homePath);
NSLog(@"路径转变为数组后为:%@", pathArray);
// 拼接
NSString *joinPath = [pathArray componentsJoinedByString:@"/"];
NSLog(@"将数组拼接成为路径:%@",joinPath);
输出结果为:
2020-10-21 15:25:32.289578+0800 BasicGrammarDemo[34365:5248464] NSHomeDirectory的路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/E1A549FF-396C-402B-934A-35BB04F60A1A
2020-10-21 15:25:32.289679+0800 BasicGrammarDemo[34365:5248464] 路径转变为数组后为:(
"",
Users,
xiejiapei,
Library,
Developer,
CoreSimulator,
Devices,
"8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F",
data,
Containers,
Data,
Application,
"E1A549FF-396C-402B-934A-35BB04F60A1A"
)
2020-10-21 15:25:32.289762+0800 BasicGrammarDemo[34365:5248464] 将数组拼接成为路径:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/8D07F1D9-24C5-45A2-8AA2-3AB9EF752D6F/data/Containers/Data/Application/E1A549FF-396C-402B-934A-35BB04F60A1A
b、initWithData
NSString *name = @"XieJiaPei";
// 将NSString转化为NSData
NSData *data = [name dataUsingEncoding:NSUTF8StringEncoding];
// 用存储在data中的二进制数据来初始化NSString对象
NSString *dataName = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"用存储在data中的二进制数据来初始化NSString对象,结果为:%@",dataName);
输出结果为:
2020-10-21 15:14:28.536003+0800 BasicGrammarDemo[34137:5237242] 用存储在data中的二进制数据来初始化NSString对象,结果为:XieJiaPei
c、把NSArray集合转换为格式字符串
NSArray *array = @[@"xie",@"jia",@"pei",@"fan",@"yi",@"cheng",@"lin",@"feng",@"mian"];
NSMutableString *result = [NSMutableString stringWithString:@"["];
for (id object in array)
{
[result appendString:[object description]];
[result appendString:@","];
}
// 去掉字符串最后的两个字符
[result deleteCharactersInRange:NSMakeRange(result.length - 2, 2)];
[result appendString:@"]"];
NSLog(@"把NSArray集合转换为字符串:%@",result);
输出结果为:
2020-10-30 16:49:02.894530+0800 FoundationDemo[46760:1747913] 把NSArray集合转换为字符串:[xie,jia,pei,fan,yi,cheng,lin,feng,mia]
8、字符串属性
// 字符串对象内部使用Unicode 编码,返回C语言字符串的指针
NSLog(@"Unicode 编码:%s",[name UTF8String]);
// 小写与大写
NSLog(@"原字符串为:XieJiaPei,小写结果为:%@",[name lowercaseString]);
NSLog(@"原字符串为:XieJiaPei,大写结果为::%@",[name uppercaseString]);
NSLog(@"原字符串为:XieJiaPei,首字母变为大写结果为::%@",[name capitalizedString]);
// 分别被用来把NSString类型的字符串转为float、int、NSinteger和BOOL类型的数值
NSLog(@"原字符串为:414.5678,floatValue值为:%f",[@"414.5678" floatValue]);
NSLog(@"原字符串为:512,intValue值为:%d",[@"512" intValue]);
NSLog(@"原字符串为:1024,integerValue值为:%ld",(long)[@"1024" integerValue]);
NSLog(@"原字符串为:OK,boolValue值为:%ld",(long)[@"2" boolValue]);
输出结果为:
2020-10-21 15:16:56.468275+0800 BasicGrammarDemo[34195:5239924] Unicode 编码:XieJiaPei
2020-10-21 15:16:56.468392+0800 BasicGrammarDemo[34195:5239924] 原字符串为:XieJiaPei,小写结果为:xiejiapei
2020-10-21 15:16:56.468487+0800 BasicGrammarDemo[34195:5239924] 原字符串为:XieJiaPei,大写结果为::XIEJIAPEI
2020-10-21 15:16:56.468600+0800 BasicGrammarDemo[34195:5239924] 原字符串为:XieJiaPei,首字母变为大写结果为::Xiejiapei
2020-10-21 15:16:56.468715+0800 BasicGrammarDemo[34195:5239924] 原字符串为:414.5678,floatValue值为:414.567810
2020-10-21 15:16:56.468827+0800 BasicGrammarDemo[34195:5239924] 原字符串为:512,intValue值为:512
2020-10-21 15:16:56.468971+0800 BasicGrammarDemo[34195:5239924] 原字符串为:1024,integerValue值为:1024
2020-10-21 15:16:56.469081+0800 BasicGrammarDemo[34195:5239924] 原字符串为:OK,boolValue值为:1
9、可变字符串
// 随着字符串的变化而自动扩展内存,所以capacity不需要非常精密
NSMutableString* mutableString = [[NSMutableString alloc] initWithCapacity:20];
// 在原来的字符串尾部添加,返回void,不产生新字符串
[mutableString appendString:@"所谓平庸"];
// 在原来的字符串尾部添加格式化字符串
[mutableString appendFormat:@"是在于认清%i个人的限度,而安于这个限度。",1];
// 插入字符串
[mutableString insertString:@"," atIndex:4];
// 替换
[mutableString replaceCharactersInRange:NSMakeRange(2, 2) withString:@"幸福"];
NSLog(@"可变字符串为:%@",mutableString);
输出结果为:
2020-10-21 15:29:30.122108+0800 BasicGrammarDemo[34466:5253526] 可变字符串为:所谓幸福,是在于认清1个人的限度,而安于这个限度。
10、富文本
NSString *singleLineText = @"Single Line";
// 下划线
NSAttributedString *singleLineTextAttr = [[NSAttributedString alloc] initWithString:singleLineText attributes:@{NSUnderlineStyleAttributeName: @(1)}];
UILabel *singleLineLabel = [[UILabel alloc] initWithFrame:CGRectMake(150, 100, 100, 50)];
singleLineLabel.attributedText = singleLineTextAttr;
[self.view addSubview:singleLineLabel];
五、集合
1、NSSet
a、集合
NSSet *set = [NSSet setWithObjects:@"xie",@"jia",@"pei", nil];
NSSet *newSet = [NSSet setWithSet:set];
NSSet *differenceSet = [NSSet setWithObjects:@"xie",@"liu",@"ying", nil];
NSSet *deepCopySet = [[NSSet alloc] initWithSet:set copyItems:YES];
NSLog(@"如果是深拷贝,所有元素必须符合NSCoping协议,拷贝后集合为:%@",deepCopySet);
NSLog(@"集合中元素个数:%lu",(unsigned long)[set count]);
set = [set setByAddingObject:@"love"];
NSLog(@"将添加单个元素后生成的新集合赋给set:%@",set);
set = [set setByAddingObjectsFromSet:newSet];
NSLog(@"添加多个元素,相当于并集:%@",set);
if ([set intersectsSet:differenceSet])
{
NSLog(@"有交集");
}
if ([set isSubsetOfSet:differenceSet])
{
NSLog(@"有子集");
}
if ([set containsObject:@"xie"])
{
NSLog(@"集合中包含元素xie");
}
NSLog(@"随取元素:%@",[set anyObject]);
NSSet *filteredSet = [set objectsPassingTest:^BOOL(id _Nonnull obj, BOOL * _Nonnull stop){
return ([obj isEqualToString:@"xie"]);
}];
NSLog(@"过滤集合:%@",filteredSet);
输出结果为:
2021-03-14 14:36:14.281893+0800 FoundationDemo[30601:5621429] 如果是深拷贝,所有元素必须符合NSCoping协议,拷贝后集合为:{(
xie,
jia,
pei
)}
2021-03-14 14:36:14.282051+0800 FoundationDemo[30601:5621429] 集合中元素个数:3
2021-03-14 14:36:14.282181+0800 FoundationDemo[30601:5621429] 将添加单个元素后生成的新集合赋给set:{(
jia,
xie,
pei,
love
)}
2021-03-14 14:36:14.282299+0800 FoundationDemo[30601:5621429] 添加多个元素,相当于并集:{(
jia,
xie,
pei,
love
)}
2021-03-14 14:36:14.282429+0800 FoundationDemo[30601:5621429] 有交集
2021-03-14 14:36:14.282536+0800 FoundationDemo[30601:5621429] 集合中包含元素xie
2021-03-14 14:36:14.282633+0800 FoundationDemo[30601:5621429] 随取元素:jia
2021-03-14 14:36:14.282741+0800 FoundationDemo[30601:5621429] 过滤集合:{(
xie
)}
b、可变集合
// 添加
NSArray *array = @[@"xie",@"jia",@"pei"];
NSMutableSet *set = [NSMutableSet setWithCapacity:10];
[set addObjectsFromArray:array];
NSSet *newSet = [NSSet setWithSet:set];
// 删除
[set removeObject:@"xie"];
NSLog(@"删除元素后,新set为:%@",set);
// 并集
[set unionSet:newSet];
NSLog(@"set取并集为:%@",set);
// 交集
[set intersectsSet:newSet];
NSLog(@"set取交集为:%@",set);
// 差集
[set minusSet:newSet];
NSLog(@"set取差集为:%@",set);
输出结果为:
2020-11-03 09:37:00.269452+0800 FoundationDemo[92558:3964819] 删除元素后,新set为:{(
jia,
pei
)}
2020-11-03 09:37:00.269550+0800 FoundationDemo[92558:3964819] set取并集为:{(
xie,
jia,
pei
)}
2020-11-03 09:37:00.269630+0800 FoundationDemo[92558:3964819] set取交集为:{(
xie,
jia,
pei
)}
2020-11-03 09:37:00.269690+0800 FoundationDemo[92558:3964819] set取差集为:{(
)}
c、带重复次数的集合
NSCountedSet *set = [NSCountedSet setWithObjects:@"xie",@"jia", nil];
[set addObject:@"xie"];
[set addObject:@"pei"];
NSLog(@"带有重复次数的set为:%@",set);
NSLog(@"xie这个字符串出现的次数为:%lu",(unsigned long)[set countForObject:@"xie"]);
输出结果为:
2020-11-03 09:40:29.104474+0800 FoundationDemo[92640:3968983] 带有重复次数的set为:<NSCountedSet: 0x6000009692c0> (xie [2], jia [1], pei [1])
2020-11-03 09:40:29.104555+0800 FoundationDemo[92640:3968983] xie这个字符串出现的次数为:2
2、NSDictionary
a、创建字典并取值
PersonClass *person = [[PersonClass alloc] init];
// 通过语法糖创建字典
NSDictionary *dict = @{@"name": @"XieJiaPei", @"age": @22, @4: @5, person: @"FanYiCheng"};
NSLog(@"原始字典为:%@",dict);
// 从字典中取值
NSLog(@"字典中的姓名为:%@,年龄为:%@,倘若没有Key值为:%@",dict[@"name"],[dict objectForKey:@"age"],dict[@"noKey"]);
// 通过初始化方法创建字典
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys: @"XieJiaPei",@"name",@22,@"age",person,@"FanYiCheng",nil];
NSLog(@"key-value对数量: %lu",(unsigned long)[dictionary count]);
NSLog(@"allKey: %@",[dictionary allKeys]);
NSLog(@"allValues: %@",[dictionary allValues]);
NSLog(@"key不同,值相同,这种情况的所有key为: %@",[dictionary allKeysForObject:@"XieJiaPei"]);
输出结果为:
2020-10-30 17:34:07.613893+0800 FoundationDemo[47518:1783828] 原始字典为:{
age = 22;
"<PersonClass: 0x600002828300>" = (null);
name = XieJiaPei;
4 = 5;
}
2020-10-30 17:34:07.613982+0800 FoundationDemo[47518:1783828] 字典中的姓名为:XieJiaPei,年龄为:22,倘若没有Key值为:(null)
2020-10-30 17:34:07.614057+0800 FoundationDemo[47518:1783828] key-value对数量: 3
2020-10-30 17:34:07.614140+0800 FoundationDemo[47518:1783828] allKey: (
name,
age,
FanYiCheng
)
2020-10-30 17:34:07.614212+0800 FoundationDemo[47518:1783828] allValues: (
XieJiaPei,
22,
"<PersonClass: 0x600002828980>"
)
2020-10-30 17:34:07.614295+0800 FoundationDemo[47518:1783828] key不同,值相同,这种情况的所有key为: (
name
)
b、遍历字典
PersonClass *person = [[PersonClass alloc] init];
NSDictionary *dict = @{@"name": @"XieJiaPei", @"age": @22, @4: @5, person: @"FanYiCheng"};
// 使用指定代码块来迭代执行该集合中所有key-value对
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop)
{
NSLog(@"key的值为:%@,value的值:%@", key, obj);
}];
输出结果为:
2020-10-30 17:40:27.704477+0800 FoundationDemo[47616:1788769] key的值为:age,value的值:22
2020-10-30 17:40:27.704543+0800 FoundationDemo[47616:1788769] key的值为:<PersonClass: 0x600001435f80>,value的值:FanYiCheng
2020-10-30 17:40:27.704610+0800 FoundationDemo[47616:1788769] key的值为:name,value的值:XieJiaPei
2020-10-30 17:40:27.704690+0800 FoundationDemo[47616:1788769] key的值为:4,value的值:5
c、写入文件
// 我发现要写入plist文件,key必须为string类型
NSDictionary *dict = @{@"name": @"XieJiaPei", @"age": @22, @"4": @5};
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [documentPath stringByAppendingPathComponent:@"jia.plist"];
// 将路径转换为本地url形式
NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
// writeToURL 的好处是,既可以写入本地url也可以写入远程url,苹果推荐使用此方法写入plist文件
if ( [dict writeToURL:fileUrl atomically:YES] )
{
NSLog(@"成功写入文件,路径为:%@",filePath);
}
NSDictionary *dictionaryFromFile = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"从文件中读取到的字典为:%@",dictionaryFromFile);
输出结果为:
2020-10-30 17:58:34.469548+0800 FoundationDemo[47972:1807344] 成功写入文件,路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/4E4809CA-E567-4D3A-8ADE-790075200303/data/Containers/Data/Application/62175E05-6FFD-4EEF-A872-3C08B6FBACB4/Documents/jia.plist
2020-10-30 17:58:34.469723+0800 FoundationDemo[47972:1807344] 从文件中读取到的字典为:{
4 = 5;
age = 22;
name = XieJiaPei;
}
d、字典排序
NSDictionary *numberDict = @{@"Xie": @22, @"Liu": @18, @"Zou": @19};
// compare:
NSArray *newKeys = [numberDict keysSortedByValueUsingSelector:@selector(compare:)];
NSLog(@"集合元素自身的排序方法(compare:),字典通过比较值将key排序后为:%@", newKeys);
// block:
NSDictionary *stringDict = @{@"Xie": @"apple", @"Liu": @"Banana", @"Zou": @"watermelon"};
newKeys = [stringDict keysSortedByValueUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2)
{
if ([obj1 length] > [obj2 length])
{
return NSOrderedDescending;
}
if ([obj1 length] < [obj2 length])
{
return NSOrderedAscending;
}
return NSOrderedSame;
}];
NSLog(@"通过代码块排序,定义比较大小的标准:字符串越长越大,排序后key为:%@", newKeys);
输出结果为:
2020-10-30 18:10:00.621205+0800 FoundationDemo[48152:1816838] 集合元素自身的排序方法(compare:),字典通过比较值将key排序后为:(
Liu,
Zou,
Xie
)
2020-10-30 18:10:00.621368+0800 FoundationDemo[48152:1816838] 通过代码块排序,定义比较大小的标准:字符串越长越大,排序后key为:(
Xie,
Liu,
Zou
)
e、字典过滤
NSDictionary *numberDict = @{@"Xie": @22, @"Liu": @18, @"Zou": @19};
NSSet *keySet = [numberDict keysOfEntriesPassingTest:^BOOL(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop)
{
return ([obj intValue] < 20);
}];
NSLog(@"真羡慕,你俩都好小呀:%@",keySet);
输出结果为:
2020-10-30 18:13:51.339440+0800 FoundationDemo[48212:1821809] 真羡慕,你俩都好小呀:{(
Liu,
Zou
)}
f、可变字典
NSDictionary *numberDict = @{@"Xie": @22, @"Liu": @18, @"Zou": @19};
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithDictionary:numberDict];
mutableDict[@"Xie"] = @"love Liu";
NSLog(@"覆盖值后,字典为:%@",mutableDict);
mutableDict[@"marray"] = @"baby";
NSLog(@"添加新元素后,字典为:%@",mutableDict);
[mutableDict addEntriesFromDictionary:@{@"Lin":@21}];
NSLog(@"添加另外一个字典后,字典为:%@",mutableDict);
[mutableDict removeObjectForKey:@"Xie"];
NSLog(@"删除元素后,字典为:%@",mutableDict);
输出结果为:
2020-10-30 18:19:20.392259+0800 FoundationDemo[48302:1826112] 覆盖值后,字典为:{
Liu = 18;
Xie = "love Liu";
Zou = 19;
}
2020-10-30 18:19:20.392358+0800 FoundationDemo[48302:1826112] 添加新元素后,字典为:{
Liu = 18;
Xie = "love Liu";
Zou = 19;
marray = baby;
}
2020-10-30 18:19:20.392443+0800 FoundationDemo[48302:1826112] 添加另外一个字典后,字典为:{
Lin = 21;
Liu = 18;
Xie = "love Liu";
Zou = 19;
marray = baby;
}
2020-10-30 18:19:20.392513+0800 FoundationDemo[48302:1826112] 删除元素后,字典为:{
Lin = 21;
Liu = 18;
Zou = 19;
marray = baby;
}
3、NSArray
a、创建数组
NSArray *aArray = @[@1, @2, @3];
NSArray<NSString *> *bArray = @[@"2", @"4", @"3", @"1"];
NSArray *cArray = [NSArray arrayWithObjects:@1, @"XieJiaPei", person, nil];
NSArray *dArray = [NSArray arrayWithObjects:@1, @"XieJiaPei", nil, @5, person, nil];
NSLog(@"原始数组A为:%@",aArray);
NSLog(@"原始数组B为:%@",bArray);
NSLog(@"原始数组A为:%@",cArray);
NSLog(@"原始数组C为:%@",dArray);
输出结果为:
2020-10-21 16:48:29.731149+0800 BasicGrammarDemo[1505:46009] 原始数组A为:(
1,
2,
3
)
2020-10-21 16:48:29.731213+0800 BasicGrammarDemo[1505:46009] 原始数组B为:(
2,
4,
3,
1
)
2020-10-21 16:48:29.731286+0800 BasicGrammarDemo[1505:46009] 原始数组A为:(
1,
XieJiaPei,
"<PersonClass: 0x600002dfa140>"
)
2020-10-21 16:48:29.731345+0800 BasicGrammarDemo[1505:46009] 原始数组C为:(
1,
XieJiaPei
)
NSArray
是不是单例?这个问题乍一看好像很可笑,数组当然不是单例!但是真的所有数组都不是单例吗?下边我们来看一组有意思的验证:
NSArray *array1 = [NSArray alloc];
NSArray *array2 = [NSArray alloc];
NSArray *array3 = [NSArray alloc];
NSLog(@"arr1 == %p, arr2 == %p, arr3 == %p", array1, array2, array3);
array1 = [array1 init];
array2 = [array2 init];
array3 = [array3 init];
NSLog(@"arr1 == %p, arr2 == %p, arr3 == %p", array1, array2, array3);
输出结果为:
2022-02-14 10:21:45.777974+0800 OCDemo[56683:9544074] arr1 == 0x7fff8a4b3b90, arr2 == 0x7fff8a4b3b90, arr3 == 0x7fff8a4b3b90
2022-02-14 10:21:45.778151+0800 OCDemo[56683:9544074] arr1 == 0x7fff8a349a10, arr2 == 0x7fff8a349a10, arr3 == 0x7fff8a349a10
从上述验证中可以发现:使用alloc
方法创建出来的NSArray
对象指向了同一个内存空间,虽然看起来怪怪的,但是它真的是单例;init
初始化之后的对象变成了另一个空间地址,而且所有对象都指向了同一个空间地址,说明对于类簇为了提升效率节约不必要的内存消耗,apple还是做了很多处理和优化的。
b、从数组中取值
PersonClass *person = [[PersonClass alloc] init];
NSArray *cArray = [NSArray arrayWithObjects:@1, @"XieJiaPei", person, nil];
NSString *index1IncArray = [cArray objectAtIndex:1];
NSNumber *index0IncArray = cArray[0];
NSLog(@"数组C中下标0的值为:%@,下标1的值为:%@",index0IncArray,index1IncArray);
NSArray *array = @[@"xie",@"jia",@"pei",@"fan",@"yi",@"cheng",@"lin",@"feng",@"mian"];
NSArray *newArray = [array objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 3)]];
NSLog(@"获取索引从3~6的元素组成的新集合:%@",newArray);
NSLog(@"获取元素在集合中的位置:%lu",(unsigned long)[array indexOfObject:@"xie"]);
array = [array arrayByAddingObjectsFromArray:newArray];
NSLog(@"向array数组的最后追加另一个数组的所有元素:%@",array);
array = [array subarrayWithRange:NSMakeRange(5, 3)];
NSLog(@"索引为6~9处的所有元素:%@",array);
输出结果为:
2020-10-21 16:48:29.765873+0800 BasicGrammarDemo[1505:46009] 数组C中下标0的值为:1,下标1的值为:XieJiaPei
2020-10-30 15:08:34.786959+0800 FoundationDemo[45297:1678179] 获取索引从3~6的元素组成的新集合:(
pei,
fan,
yi
)
2020-10-30 15:08:34.787054+0800 FoundationDemo[45297:1678179] 获取元素在集合中的位置:0
2020-10-30 15:08:34.787135+0800 FoundationDemo[45297:1678179] 向array数组的最后追加另一个数组的所有元素:(
xie,
jia,
pei,
fan,
yi,
cheng,
lin,
feng,
mian,
pei,
fan,
yi
)
2020-10-30 15:08:34.787201+0800 FoundationDemo[45297:1678179] 索引为6~9处的所有元素:(
cheng,
lin,
feng
)
c、给数组排序
sortedArrayUsingSelector:
该排序方法需要数组对象自身实现compare:
方法,而有些类系统已经实现了compare:
方法,比如NSString
、NSNumber
等,所以对于系统类的简单排序来说非常方便;对于自定义类数组的排序来说就需要在自定类中实现compare:
方法。
NSArray<NSString *> *bArray = @[@"2", @"4", @"3", @"1"];
NSArray *sortedArray = [bArray sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"集合元素自身的排序方法(compare:),数组B排序后为:%@", sortedArray);
sortedArrayUsingComparator:
该方法需要传入一个cmptr
匿名函数用来做比较函数,使用起来要比单独定义比较函数(方法)方便很多;sortedArrayWithOptions:usingComparator:
多出了一个NSSortOptions
枚举类型的变量,这个枚举类型的变量有两个不同的枚举值:NSSortConcurrent
可以利用多核cpu的特性使排序并发执行;NSSortStable
可以保证排序算法的稳定性(相等的值在排序前后顺序不发生变化) 。
NSArray *sortedArrayByBlock = [bArray sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2)
{
if ([obj1 integerValue] > [obj2 integerValue])
{
return NSOrderedDescending;
}
if ([obj1 intValue] < [obj2 intValue])
{
return NSOrderedAscending;
}
return NSOrderedSame;
}];
NSLog(@"通过代码块排序,数组B排序后为:%@", sortedArrayByBlock);
sortedArrayUsingFunction:context:
方法需要传入一个C语言的函数comparator
,由于是C语言的函数,所以可以写在任意对排序方法可见的位置。第三个参数是任意的指针类型,可以使得排序函数内部可以依赖一些外部的参数。
NSArray *sortedArrayByFunction = [bArray sortedArrayUsingFunction:intSort context:nil];
NSLog(@"通过函数排序,数组B排序后为:%@", sortedArrayByFunction);
NSInteger intSort(id num1, id num2, void *context)
{
int v1 = [num1 intValue];
int v2 = [num2 intValue];
if (v1 < v2)
return NSOrderedAscending;
else if (v1 > v2)
return NSOrderedDescending;
else
return NSOrderedSame;
}
输出结果为:
2020-10-30 15:19:30.831961+0800 FoundationDemo[45456:1686606] 集合元素自身的排序方法(compare:),数组B排序后为:(
1,
2,
3,
4
)
2020-10-30 15:19:30.832052+0800 FoundationDemo[45456:1686606] 通过函数排序,数组B排序后为:(
1,
2,
3,
4
)
2020-10-30 15:19:30.832179+0800 FoundationDemo[45456:1686606] 通过代码块排序,数组B排序后为:(
1,
2,
3,
4
)
d、让数组过滤数据
使用LIKE
, BETWEEN
,BEGINSWITH
,ENDSWITH
,CONTAINS
,IN
等谓词来筛选(大小写不影响结果,习惯上用大写)。
NSArray<NSString *> *bArray = @[@"2", @"4", @"3", @"1"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF matches %@", @"[3-9]+"];
NSArray *filteredArray = [bArray filteredArrayUsingPredicate:predicate];
NSLog(@"数组B过滤数据(只留下3-9之间的数据)后为:%@", filteredArray);
输出结果为:
2020-10-21 16:53:27.058488+0800 BasicGrammarDemo[1615:51718] 数组B过滤数据(只留下3-9之间的数据)后为:(
4,
3
)
使用>,<,>=,<=
等运算符号来筛选:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name.length > 5"];
NSArray *filteredArray = [array filteredArrayUsingPredicate:predicate];
e、可变数组
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:@[@"xie",@"jia",@"pei",@"fan",@"yi",@"cheng"]];
// 向集合最后添加一个元素
[mutableArray addObject:@"liu"];
// 向集合尾部添加多个元素
[mutableArray addObjectsFromArray:@[@"ying",@"chi"]];
NSLog(@"向集合最后位置添加元素后数组为:%@",mutableArray);
// 指定位置插入一个元素
[mutableArray insertObject:@"GuanYu" atIndex:1];
// 指定位置插入多个元素
[mutableArray insertObjects:@[@"ZhangFei",@"ZhaoYun"] atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)]];
NSLog(@"指定位置插入元素后数组为:%@",mutableArray);
// 删除最后一个元素
[mutableArray removeLastObject];
// 删除指定索引处的元素
[mutableArray removeObjectAtIndex:5];
// 删除3~6处元素
[mutableArray removeObjectsInRange:NSMakeRange(2, 3)];
NSLog(@"删除元素后数组为:%@",mutableArray);
// 替换索引为2处的元素
[mutableArray replaceObjectAtIndex:2 withObject:@"MaChao"];
NSLog(@"替换可变数组中的数据后为:%@",mutableArray);
输出结果为:
2020-10-30 17:00:55.532047+0800 FoundationDemo[46981:1758054] 向集合最后位置添加元素后数组为:(
xie,
jia,
pei,
liu,
ying,
chi
)
2020-10-30 17:00:55.532186+0800 FoundationDemo[46981:1758054] 指定位置插入元素后数组为:(
xie,
ZhangFei,
ZhaoYun,
GuanYu,
jia,
pei,
liu,
ying,
chi
)
2020-10-30 17:00:55.532264+0800 FoundationDemo[46981:1758054] 删除元素后数组为:(
xie,
ZhangFei,
liu,
ying
)
2020-10-30 17:00:55.532336+0800 FoundationDemo[46981:1758054] 替换可变数组中的数据后为:(
xie,
ZhangFei,
MaChao,
ying
)
f、数组的拷贝
NSArray *shallowCopyArray = [bArray copy];
NSLog(@"原数组为不可变数组——使用copy——结果数组为不可变数组,比较是否相等的结果为:%@", shallowCopyArray == bArray ? @"相等" : @"不相等");
NSLog(@"原数组为不可变数组——使用copy——结果数组为不可变数组,比较元素是否相等的结果为:%@", shallowCopyArray[3] == bArray[3] ? @"相等" : @"不相等");
NSMutableArray *mutableCopyArray = [bArray mutableCopy];
NSLog(@"原数组为不可变数组——使用mutableCopy——结果数组为可变数组,比较是否相等的结果为:%@", mutableCopyArray == bArray ? @"相等" : @"不相等");
NSArray *copyArray = [mutableCopyArray copy];
NSLog(@"原数组为可变数组——使用copy——结果数组为不可变数组,比较是否相等的结果为:%@", copyArray == mutableCopyArray ? @"相等" : @"不相等");
NSMutableArray *anotherMutableCopyArray = [mutableCopyArray mutableCopy];
NSLog(@"原数组为可变数组——使用mutableCopy——结果数组为可变数组,比较是否相等的结果为:%@", anotherMutableCopyArray == mutableCopyArray ? @"相等" : @"不相等");
输出结果为:
2020-10-21 17:16:15.239156+0800 BasicGrammarDemo[2026:73856] 原数组为不可变数组——使用copy——结果数组为不可变数组,比较是否相等的结果为:相等
2020-10-21 17:16:15.239212+0800 BasicGrammarDemo[2026:73856] 原数组为不可变数组——使用copy——结果数组为不可变数组,比较元素是否相等的结果为:相等
2020-10-21 17:16:15.239280+0800 BasicGrammarDemo[2026:73856] 原数组为不可变数组——使用mutableCopy——结果数组为可变数组,比较是否相等的结果为:不相等
2020-10-21 17:16:15.239336+0800 BasicGrammarDemo[2026:73856] 原数组为可变数组——使用copy——结果数组为不可变数组,比较是否相等的结果为:不相等
2020-10-21 17:16:15.239401+0800 BasicGrammarDemo[2026:73856] 原数组为可变数组——使用mutableCopy——结果数组为可变数组,比较是否相等的结果为:不相等
g、数组的遍历
一般的遍历方式包括for循环
、NSEnumerator
、for in
。比较特殊的遍历方式enumerateObjectsUsingBlock
是通过block
回调,在子线程中遍历,对象的回调次序是乱序的,而且调用线程会等待该遍历过程完成。这几个遍历方式中for in
性能最好,for循环
较低, 多线程遍历方式是性能最差的。
使用for in快速快速遍历
- 该遍历方法是按照元素的放入顺序依次遍历元素
- 可以直接获取到对应的元素,使用方便
- 在可变数组遍历过程中不能对数组进行插入删除等更新操作,否则会出现异常闪退问题.
for (NSString *string in bArray)
{
NSLog(@"使用快速遍历,字符串为:%@",string);
}
使用枚举器遍历
- 按照元素在数组中的顺序进行遍历;
- 可以同时获取下标和对应的元素;
- 可以随时终止查找,只需要将
*stop=true
;
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"array[%@]:%@",@(idx), obj);
if ([obj isEqualToString:@"five"]) {
*stop = YES;
}
}];
以上顺序和逆序遍历还可以使用不带block
回调的枚举器来实现,如下:
NSEnumerator *enumerator = [bArray objectEnumerator];
id object;
while (object = [enumerator nextObject])
{
NSLog(@"使用顺序枚举器,数值为:%@",object);
}
NSEnumerator *reverseEnumerator = [bArray reverseObjectEnumerator];
while (object = [reverseEnumerator nextObject])
{
NSLog(@"使用逆序枚举器,数值为:%@" , object);
}
输出结果为:
2020-10-30 15:26:21.535811+0800 FoundationDemo[45562:1691741] 使用快速遍历,字符串为:2
2020-10-30 15:26:21.535861+0800 FoundationDemo[45562:1691741] 使用快速遍历,字符串为:4
2020-10-30 15:26:21.535928+0800 FoundationDemo[45562:1691741] 使用快速遍历,字符串为:3
2020-10-30 15:26:21.535992+0800 FoundationDemo[45562:1691741] 使用快速遍历,字符串为:1
2020-10-30 15:26:21.536155+0800 FoundationDemo[45562:1691741] 使用顺序枚举器,数值为:2
2020-10-30 15:26:21.536255+0800 FoundationDemo[45562:1691741] 使用顺序枚举器,数值为:4
2020-10-30 15:26:21.536402+0800 FoundationDemo[45562:1691741] 使用顺序枚举器,数值为:3
2020-10-30 15:26:21.536514+0800 FoundationDemo[45562:1691741] 使用顺序枚举器,数值为:1
2020-10-30 15:26:21.536622+0800 FoundationDemo[45562:1691741] 使用逆序枚举器,数值为:1
2020-10-30 15:26:21.536748+0800 FoundationDemo[45562:1691741] 使用逆序枚举器,数值为:3
2020-10-30 15:26:21.536884+0800 FoundationDemo[45562:1691741] 使用逆序枚举器,数值为:4
2020-10-30 15:26:21.536991+0800 FoundationDemo[45562:1691741] 使用逆序枚举器,数值为:2
并发遍历
- 该选项可以利用多核CPU的特性进行并发遍历,速度会比较快
- 该遍历不是线程安全的,所以设置
*stop=true
没有实际意义 - 该操作主线程会等待遍历操作完成之后继续进行后边的操作
[array enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"thread:%@, array[%@]:%@", [NSThread currentThread], @(idx), obj);
if ([obj isEqualToString:@"five"]) {
*stop = YES;// 无意义
}
}];
其他遍历方法
还可以使用dispatch_apply
函数来模拟遍历,但其实质只是一个重复指定次数的操作,并且可以指定完成操作的队列类确定是否需要开启并发遍历。在多数情况下,我们会使用并发队列来将耗时操作放在dispatch_apply
函数中来实现,以利用多核cpu的特性来提升性能。
dispatch_queue_t queue = dispatch_queue_create("com.xiejiapei", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(array.count, queue, ^(size_t iteration) {
//此处插入耗时操作
});
还可以使用ReactiveCocoa
创建信号使用subscribeNext:
方法进行遍历。
h、写入文件
NSArray<NSString *> *bArray = @[@"2", @"4", @"3", @"1"];
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [documentPath stringByAppendingFormat:@"/%@",@"xie.plist"];
if ( [bArray writeToFile:filePath atomically:YES] )
{
NSLog(@"成功写入文件,路径为:%@",filePath);
}
NSArray *arrayFromFile = [NSArray arrayWithContentsOfFile:filePath];
NSLog(@"从文件中读取到的数组为:%@",arrayFromFile);
输出结果为:
2020-10-30 16:40:54.419610+0800 FoundationDemo[46618:1741321] 成功写入文件,路径为:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/4E4809CA-E567-4D3A-8ADE-790075200303/data/Containers/Data/Application/27126598-B9C0-4156-BC10-D3EAC6697F7F/Documents/xie.plist
2020-10-30 16:40:54.419790+0800 FoundationDemo[46618:1741321] 从文件中读取到的数组为:(
2,
4,
3,
1
)
I、整体调用方法
- (void)performSelector
{
PersonClass *person = [[PersonClass alloc] init];
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:@[person,person,person,person]];
// 对集合元素整体调用方法
[mutableArray makeObjectsPerformSelector:@selector(printPetPhrase:) withObject:@"锤子"];
NSLog(@"对集合元素整体调用方法:%@",mutableArray);
// 迭代集合内指定范围内元素,并使用该元素来执行代码块
[mutableArray enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)] options:NSEnumerationReverse usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"正在处理第%ld个元素:%@" , idx , obj);
}];
}
- (void)printPetPhrase:(NSString *)petPhrase
{
NSLog(@"你的口头禅是什么呀?%@",petPhrase);
}
输出结果为:
2020-10-30 17:22:03.303271+0800 FoundationDemo[47347:1775321] 你的口头禅是什么呀?锤子
2020-10-30 17:22:03.303360+0800 FoundationDemo[47347:1775321] 你的口头禅是什么呀?锤子
2020-10-30 17:22:03.303433+0800 FoundationDemo[47347:1775321] 你的口头禅是什么呀?锤子
2020-10-30 17:22:03.303490+0800 FoundationDemo[47347:1775321] 你的口头禅是什么呀?锤子
2020-10-30 17:22:03.303692+0800 FoundationDemo[47347:1775321] 正在处理第2个元素:<PersonClass: 0x6000031ac880>
2020-10-30 17:22:03.303758+0800 FoundationDemo[47347:1775321] 正在处理第1个元素:<PersonClass: 0x6000031ac880>
J、数组去除重复元素
法一
利用NSSet
特性, 放入集合自动去重。这种方法更快,利用NSSet
不会添加重复元素的特性。
NSArray *selected = [deleteDuplicateSet allObjects];
不过去重的数组没有进行排序,如果需要排序,可以使用NSSortDescriptor
类。
NSSet *deleteDuplicateSet = [NSSet setWithArray:self.switchAlbumSelectList];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"displayName" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
NSArray *selected = [deleteDuplicateSet sortedArrayUsingDescriptors:sortDescriptors];
法二
利用NSDictionary
的AllKeys
(AllValues
)方法可以将NSArray
中的元素存入一个字典,然后利用AllKeys
或者AllValues
取得字典的所有键或值,这些键或值都是去重的。结果为无序的,也就是说不包含原有顺序,可自行加入排序算法。
NSArray *originalArr = @[@1, @2, @3, @1, @3];
NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
for (NSNumber *n in originalArr)
{
[dict setObject:n forKey:n];
}
NSLog(@"%@",[dictM allValues]);
法三
通过valueForKeyPath
去重只需一行代码。
NSArray *originalArr = @[@1, @2, @3, @1, @3];
NSArray *result = [originalArr valueForKeyPath:@"@distinctUnionOfObjects.self"];
法四
开辟新的内存空间,判断是否存在,若不存在则添加到数组中,得到最终结果的顺序不发生变化。
// 只能通过这种方式去重,而不能使用字典、Set,因为要保证批量上传的顺序按照选择时候的顺序
NSMutableArray *selected = [NSMutableArray array];
for (UCARPhotoModel *photo in self.switchAlbumSelectList)
{
if (![selected containsObject:photo])
{
[selected addObject:photo];
}
}
4、可变数组的实现原理
a、Demo演示
NSArray *placeHoldler = [NSArray alloc];
NSArray *arr1 = [[NSArray alloc] init];
NSArray *arr2 = [[NSArray alloc] initWithObjects:@0, nil];
NSArray *arr3 = [[NSArray alloc] initWithObjects:@0, @1, nil];
NSArray *arr4 = [[NSArray alloc] initWithObjects:@0, @1, @2, nil];
NSLog(@"placeHoldler:%s",object_getClassName(placeHoldler));
NSLog(@"arr1:%s",object_getClassName(arr1));
NSLog(@"arr2:%s",object_getClassName(arr2));
NSLog(@"arr3:%s",object_getClassName(arr3));
NSLog(@"arr4:%s",object_getClassName(arr4));
NSMutableArray *mutablePlaceHoldler = [NSMutableArray alloc];
NSMutableArray *mutableArr1 = [[NSMutableArray alloc] init];
NSMutableArray *mutableArr2 = [[NSMutableArray alloc] initWithObjects:@0, nil];
NSMutableArray *mutableArr3 = [[NSMutableArray alloc] initWithObjects:@0, @1, nil];
NSMutableArray *mutableArr4 = [[NSMutableArray alloc] initWithObjects:@0, @1, @2, nil];
NSLog(@"placeHoldler:%s",object_getClassName(mutablePlaceHoldler));
NSLog(@"mutableArr1:%s",object_getClassName(mutableArr1));
NSLog(@"mutableArr2:%s",object_getClassName(mutableArr2));
NSLog(@"mutableArr3:%s",object_getClassName(mutableArr3));
NSLog(@"mutableArr4:%s",object_getClassName(mutableArr4));
NSLog(@"placeHoldler地址:%p",placeHoldler);
NSArray *anotherPlaceHoldler = [NSArray alloc];
NSLog(@"anotherPlaceHoldler地址:%p",anotherPlaceHoldler);
输出结果为:
2020-07-23 17:52:33.886193+0800 Demo[76977:21942936] placeHoldler:__NSPlaceholderArray
2020-07-23 17:52:33.886604+0800 Demo[76977:21942936] arr1:__NSArray0
2020-07-23 17:52:33.886698+0800 Demo[76977:21942936] arr2:__NSSingleObjectArrayI
2020-07-23 17:52:33.886760+0800 Demo[76977:21942936] arr3:__NSArrayI
2020-07-23 17:52:33.886829+0800 Demo[76977:21942936] arr4:__NSArrayI
2020-07-23 17:52:33.886917+0800 Demo[76977:21942936] placeHoldler:__NSPlaceholderArray
2020-07-23 17:52:33.886975+0800 Demo[76977:21942936] mutableArr1:__NSArrayM
2020-07-23 17:52:33.887035+0800 Demo[76977:21942936] mutableArr2:__NSArrayM
2020-07-23 17:52:33.887205+0800 Demo[76977:21942936] mutableArr3:__NSArrayM
2020-07-23 17:52:33.887332+0800 Demo[76977:21942936] mutableArr4:__NSArrayM
2020-07-23 18:12:14.146565+0800 Demo[77022:21953656] placeHoldler地址:0x10dd56738
2020-07-23 18:12:14.146780+0800 Demo[77022:21953656] anotherPlaceHoldler地址:0x10dd56738
不管是NSArray
,还是NSMutableArray
,alloc
之后的得到都是__NSPlacrholderArray
。当我们创建一个空数组时,得到的是__NSArray0
。当NSArray
只有一个元素时,得到的是__NSSingleObjectArrayI
。当NSArray
的元素个数大于1个的时候, 得到 __NSArrayI
。NSMutablearray
无论元素个数有多少返回的都是__NSArrayM
。placeHolder
和 anotherPlaceHoldler
的内存地址一样,说明是一个单例,该类内部只有一个isa
指针,init
后被新的实例换掉了。
b、数组的数据结构
NSArrayI
__NSArrayI
{
// 数组的元素个数,调用[array count]时,返回的就是_userd的值
NSInterger _userd;
// 当做id_list来用,即一个存储id对象的buff.由于__NSArrayI的不可变,所以_list一旦分配,释放之前都不会再有移动删除操作了
id_list[0];
}
NSSingleObjectArrayI
__NSSingleObjectArrayI
{
//因为只有在创建只包含一个对象的不可变数组时,才会得到__NSSingleObjectArrayI对象,所以其内部结构更加简单
id object;
}
__NSArrayM
__NSArrayM
{
//当前对象数目 [NSMutablearray count]
NSUInterger _used;
//对象数组的起始偏移
NSUInterger _offset;
//已分配的_list大小,能存储的对象个数,不是字节数
int_size: 28;
int_unused: 4;
//修改标记
uint32_t _mutations;
//是个循环数组,并且在增删操作时会动态地重新分配以符合当前的存储需求
id *_list;
}
c、数组插入删除的原理
数组是一段连续的内存空间, 所以当在下标0处插入一个元素时,需要移动其后面所有的元素。同样的移除第一个元素,需要进行相同的操作。
当数组非常大时,就有问题了。NSMutableArray
使用环形缓冲区,这个数据结构相对简单,只是比常规数组复杂点。环形缓冲区的内容能在到达任意一段时绕向另一端,如果我们在中间进行插入和删除,只会移动最少的一边元素。
Demo
Demo在我的Github上,欢迎下载。
BasicsDemo