总结

编号 主题 内容
NSFileManager NSFileManager介绍/用法(常见的判断)/文件访问/文件操作
集合对象的内存管理 集合对象的内存管理/内存管理总结
*copy copy基本概念/使用/copy快速入门/内存管理
@Property中的copy关键字 @property中的copy的作用/@property内存管理策略选择
自定义类实现copy操作 自定义类实现copy操作
单例设计模式 单例模式概念/简单的单例模式实现
宏定义抽取单例

一. NSFileManager

1.NSFileManager介绍
  • 什么是NSFileManager

    • 顾名思义, NSFileManager是用来管理文件系统的
    • 它可以用来进行常见的文件\文件夹操作
  • NSFileManager使用了单例模式

    • 使用defaultManager方法可以获得那个单例对象
[NSFileManager defaultManager]
2.NSFileManager用法
  • - (BOOL)fileExistsAtPath:(NSString *)path;

    • path这个文件\文件夹是否存在
    NSFileManager *manager = [NSFileManager defaultManager];
// 可以判断文件
BOOL flag = [manager fileExistsAtPath:@"/Users/MJ-Hee/Desktop/lnj.txt"];
NSLog(@"flag = %i", flag);
// 可以判断文件夹
flag = [manager fileExistsAtPath:@"/Users/MJ-Hee/Desktop/未命名文件夹"];
NSLog(@"flag = %i", flag);
  • - (BOOL)fileExistsAtPath:(NSString )path isDirectory:(BOOL)isDirectory;

    • path这个文件\文件夹是否存在, isDirectory代表是否为文件夹
    NSFileManager *manager = [NSFileManager defaultManager];
BOOL directory = NO;
BOOL flag = [manager fileExistsAtPath:@"/Users/MJ-Hee/Desktop/未命名文件夹" isDirectory:&directory];
NSLog(@"flag = %i, directory = %i", flag, directory);
  • - (BOOL)isReadableFileAtPath:(NSString *)path;

    • path这个文件\文件夹是否可读
  • - (BOOL)isWritableFileAtPath:(NSString *)path;

    • path这个文件\文件夹是否可写
    • 系统目录不允许写入

  • - (BOOL)isDeletableFileAtPath:(NSString *)path;

    • path这个文件\文件夹是否可删除
    • 系统目录不允许删除

3.NSFileManager的文件访问
  • - (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error;

    • 获得path这个文件\文件夹的属性
    NSFileManager *manager = [NSFileManager defaultManager];
NSDictionary *dict = [manager attributesOfItemAtPath:@"/Users/MJ-Hee/Desktop/hmj.txt" error:nil];
NSLog(@"dit = %@", dict);
  • - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error;

    • 获得path的当前子路径
  • - (NSData )contentsAtPath:(NSString )path;

    • 获得文件内容
    • 只能获取当前文件夹下的所有文件,不能获取子文件夹下的文件
    NSFileManager *manager = [NSFileManager defaultManager];
NSArray *paths = [manager contentsOfDirectoryAtPath:@"/Users/MJ-Hee/Desktop" error:nil];
NSLog(@"paths = %@", paths);
  • - (NSArray )subpathsAtPath:(NSString )path;

  • - (NSArray *)subpathsOfDirectoryAtPath:(NSString *)path error:(NSError **)error;

    • 都能获得path的所有子路径
    • 第二个有error监听
    NSFileManager *manager = [NSFileManager defaultManager];
NSArray *paths = [manager subpathsAtPath:@"/Users/LNJ/Desktop/"];
NSLog(@"paths = %@", paths);
4.NSFileManager的文件操作
  • - (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;

    • 拷贝(生成新的)
  • - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;

    • 移动(剪切,不生成新的)
  • - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;

    • 删除
  • - (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary *)attributes error:(NSError **)error;

    • 创建文件夹(createIntermediates为YES代表自动创建中间的文件夹)
    • createFileAtPath: 创建到那个位置
    • withIntermediateDirectories: 如果指定的文件中有一些文件夹不存在,是否自动创建不存在的文件夹
    • attributes:指定创建出来的文件夹的属性
    • error: 是否创建成功,失败的话会给传入的参数赋值
    NSFileManager *manager = [NSFileManager defaultManager];
BOOL flag = [manager createDirectoryAtPath:@"/Users/LNJ/Desktop/test" withIntermediateDirectories:YES attributes:nil error:nil];
NSLog(@"flag = %i", flag);
  • - (BOOL)createFileAtPath:(NSString )path contents:(NSData)data attributes:(NSDictionary *)attr;

    • 创建文件(NSData是用来存储二进制字节数据的)
    • createFileAtPath:指定创建文件的地址
    • contents:创建的文件的内容
    • attributes:指定创建出来的文件的属性
    NSString *str = @"lnj";
//NSData : 二进制数据
//将字符串转化成二进制数据
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
NSFileManager *manager = [NSFileManager defaultManager];
BOOL flag = [manager createFileAtPath:@"/Users/LNJ/Desktop/abc.txt" contents:data attributes:nil];
NSLog(@"flag = %i", flag);
  • 注意

    • 该方法只能用于创建文件,不能用于创建文件夹

二.集合对象的内存管理

1.集合对象的内存管理
  • 当一个对象加入到集合中,那么该对象的引用计数会+1
  • 当集合被销毁的时候,集合会向集合中的元素发送release消息
    • 通过类对象创建的对象不需要release

      • NSMutableArray *arr = [NSMutableArray array];
    NSMutableArray *arr = [[NSMutableArray alloc] init];

    Person *p = [[Person alloc] init];
NSLog(@"retainCount = %lu", [p retainCount]);
//如果将一个对象添加到一个数组/字典中,那么数组会对对象进行一次retain
[arr addObject:p];
NSLog(@"retainCount = %lu", [p retainCount]);
[p release];
NSLog(@"retainCount = %lu", [p retainCount]);
//当数组对象释放后,会给数组中所有的对象发送一条release消息
[arr release];
  • 当一个对象加入到集合中,那么该对象的引用计数会+1
  • 当把一个对象从集合中移除时,会向移除的元素发送release消息
    NSMutableArray *arr = [[NSMutableArray alloc] init];
Person *p = [[Person alloc] init];
NSLog(@"retainCount = %lu", [p retainCount]);
[arr addObject:p];
NSLog(@"retainCount = %lu", [p retainCount]);
//当数组移除一个对象之后,会给这个对象发送一条release消息
[arr removeObject:p];
NSLog(@"retainCount = %lu", [p retainCount]);
[p release];
[arr release];
2.集合对象内存管理总结
  • 1.官方内存管理原则

    • 1> 当调用alloc、new、copy(mutableCopy)方法产生一个新对象的时候,就必须在最后调用一次release或者autorelease
    • 2> 当调用retain方法让对象的计数器+1,就必须在最后调用一次release或者autorelease
  • 2.集合的内存管理细节

    • 1> 当把一个对象添加到集合中时,这个对象会做了一次retain操作,计数器会+1
    • 2> 当一个集合被销毁时,会对集合里面的所有对象做一次release操作,计数器会-1
    • 3> 当一个对象从集合中移除时,这个对象会一次release操作,计数器会-1
  • 3.普遍规律

    • 1> 如果方法名是add\insert开头,那么被添加的对象,计数器会+1
    • 2> 如果方法名是remove\delete开头,那么被移除的对象,计数器-1

三.copy

1.copy基本概念
  • 什么是copy

    • Copy的字面意思是“复制”、“拷贝”,是一个产生副本的过程
  • 常见的复制有:文件复制

    • 作用:利用一个源文件产生一个副本文件
  • 特点:

    • 修改源文件的内容,不会影响副本文件
    • 修改副本文件的内容,不会影响源文件
  • OC中的copy

    • 作用:利用一个源对象产生一个副本对象
  • 特点:
    • 修改源对象的属性和行为,不会影响副本对象
    • 修改副本对象的属性和行为,不会影响源对象
2.Copy的使用
  • 如何使用copy功能

    • 一个对象可以调用copy或mutableCopy方法来创建一个副本对象
    • copy : 创建的是不可变副本(如NSString、NSArray、NSDictionary)
    • mutableCopy :创建的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)
  • 使用copy功能的前提

    • copy : 需要遵守NSCopying协议,实现copyWithZone:方法
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
  • 使用mutableCopy的前提

    • 需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
  • 一般情况下,拷贝会生成一个新对象
2.深复制和浅复制
  • 不可变对象调用copy,不会生成一个新对象

    • 因为原来的和copy的对象都是不可变的,满足需求,所以不需要重新生成一个新对象
  • 浅复制(浅拷贝,指针拷贝,shallow copy)
    • 源对象和副本对象是同一个对象
    • 源对象(副本对象)引用计数器+1,相当于做一次retain操作
    • 本质是:没有产生新的对象
    NSString *srcStr = @"lnj";
NSString *copyStr = [srcStr copy];
NSLog(@"src = %p, copy = %p", srcStr, copyStr);
  • 深复制(深拷贝,内容拷贝,deep copy)

    • 源对象和副本对象是不同的两个对象
    • 源对象引用计数器不变,副本对象计数器为1(因为是新产生的)
    • 本质是:产生了新的对象
    NSString *srcStr = @"lnj";
NSMutableString *copyStr = [srcStr mutableCopy];
NSLog(@"src = %p, copy = %p", srcStr, copyStr);
NSLog(@"src = %@, copy = %@", srcStr, copyStr);
[copyStr appendString:@" cool"];
NSLog(@"src = %@, copy = %@", srcStr, copyStr); NSMutableString *srcStr = [NSMutableString stringWithFormat:@"lnj"];
NSString *copyStr = [srcStr copy];
[srcStr appendString:@" cool"];
NSLog(@"src = %p, copy = %p", srcStr, copyStr);
NSLog(@"src = %@, copy = %@", srcStr, copyStr); NSMutableString *srcStr = [NSMutableString stringWithFormat:@"lnj"];
NSMutableString *copyStr = [srcStr mutableCopy];
[srcStr appendString:@" cool"];
[copyStr appendString:@" 520it"];
NSLog(@"src = %p, copy = %p", srcStr, copyStr);
NSLog(@"src = %@, copy = %@", srcStr, copyStr);
  • 只有源对象和副本对象都不可变时,才是浅复制,其它都是深复制
  • 只有当源对象是不可变调用copy时才是浅拷贝
内存管理
  • 在iOS环境下运行
  • 总结 +如果是浅拷贝,那么系统会自动对源对象进行一次retain

    • 如果是深拷贝,会生成新对象,那么系统不会对源对象进行retain,但需要对新对象进行release
  • 浅拷贝的原则

    • alloc/new/retain/copy必须有对应的release

四.@Property中的copy关键字

1.@property中的copy的作用

  • 防止外界修改内部成员变量的值
    @interface Person : NSObject
//用retain,外界可以修改内部的数据,此处改为copy可以防止外界修改内部的数据
@property (nonatomic, retain) NSString *name;
@end NSMutableString *str = [NSMutableString stringWithFormat:@"lnj"]; Person *p = [[Person alloc] init];
p.name = str;
// person中的属性会被修改
[str appendString:@" cool"];
NSLog(@"name = %@", p.name);
  • 可以使用copy保存block,这样可以保住block中使用的外界对象的命
  • 防止访问对象时,对象已经释放
    • 不用copy情况

      Person *p = [[Person alloc] init];
      p.name = @"lnj";
      Dog *d = [[Dog alloc] init];
      d.age = 10;
      NSLog(@"retainCount = %lu", [d retainCount]); // 1
      p.pBlock = ^{
      // 报错, 调用之前就销毁了
      NSLog(@"age = %d", d.age);
      };
      [d release]; // 0
      p.pBlock();
      [p release];

    • 用copy情况
    Person *p = [[Person alloc] init];
p.name = @"lnj";
Dog *d = [[Dog alloc] init];
d.age = 10;
NSLog(@"retainCount = %lu", [d retainCount]); // 1
p.pBlock = ^{
// 会对使用到的外界对象进行一次retain
NSLog(@"age = %d", d.age);
NSLog(@"retainCount = %lu", [d retainCount]); // 1
};
[d release]; // 1
p.pBlock();
[p release];
  • copy block之后引发的循环引用

    • 如果对象中的block又用到了自己,那么为了泄露内存,应该将对象修饰为__block
2.@property内存管理策略选择
  • 非ARC

    • 1> copy : 只用于NSString\block
    • 2> retain : 除NSString\block以外的OC对象
    • 3> assign :基本数据类型、枚举、结构体(非OC对象),当2个对象相互引用,一端用retain,一端用assign
  • ARC

    • 1> copy : 只用于NSString\block
    • 2> strong : 除NSString\block以外的OC对象
    • 3> weak : 当2个对象相互引用,一端用strong,一端用weak
    • 4> assgin : 基本数据类型、枚举、结构体(非OC对象)
  • 注意

    • 只要是block使用copy,不是拷贝,而是将block从栈中转移到堆中
    • 只要

五. 自定义类实现copy操作

1.自定义类实现copy操作

  • 让类遵守NSCopying协议
  • 实现 copyWithZone:方法,在该方法中返回一个对象的副本即可。
  • 在copyWithZone方法中,创建一个新的对象,并设置该对象的数据与现有对象一致, 并返回该对象.
    • zone: 表示空间,分配对象是需要内存空间的,如果指定了zone,就可以指定 新建对象对应的内存空间。但是:zone是一个非常古老的技术,为了避免在堆中出现内存碎片而使用的。在今天的开发中,zone几乎可以忽略

  • 以后想让自定义的对象能够被copy,只需要遵守NSCopying协议
  • 实现协议中的-(id)copyWithZone(NSZone *)zone方法
  • 在-(id)copyWithZone(NSZone *)zone方法中创建一个副本对象,然后将当前对象的值赋值给副本对象即可

  • 无父类实现

-(id)copyWithZone(NSZone *)zone{

    //class用于获取一个类
CustomMode *custom = [[[self class] copyWithZone:zone] init]; Custom ->_a = [_a copyWithZone:zone]; Custom -> _c = _c;//不是对象的 直接赋值 Return custom; }
  • 有父类实现

    • 不调用父类方法, 无法拷贝父类中继承的属性
    • 不重新父类copyWithZone, 无法拷贝本来中的特有属性
    • 如果想让子类调用copy时,仍保留子类特有的属性,那么就需要重写copyWithZone,并在copyWithZone中调用父类的copyWithZone,即[super copyWithZone:zone]
-(id)copyWithZone(NSZone *)zone{

    CustomModel *custom = [super copyWithZone:zone];
//只调用子类特有的成员变量的setter方法即可
….
Return custom;
}

六.单例设计模式

  • 可以实现对象的共享
1.单例模式概念
  • 什么是单例模式:(Singleton)

    • 单例模式的意图是让类的对象成为系统中唯一的实例,供一个访问点,供客户类共享资源。
  • 什么情况下使用单例?

    • 1、类只能有一个实例,而且必须从一个为人熟知的访问点对其进行访问,比如工厂方法。
    • 2、这个唯一的实例只能通过子类化进行扩展,而且扩展的对象不会破坏客户端代码。
  • 单例设计模式的要点:

    • 1)某个类只能有一个实例。
    • 2)他必须自行创建这个对象
    • 3)必须自行向整个系统提供这个实例;
    • 4)这个方法必须是一个静态类
    • 5)一般情况下,创建一个单例对象都有一个与之对应的方法
    • 6)用于创建单例对象的方法名称都以share开头,或者以default开头
2.简单的单例模式实现
  • 调用alloc时,内部会调用allocWithZone
static 类名 *_instance = nil;
+ (instancetype)allowWithZone:(struct _NSZone *)zone
{
//由于所有的创建方法都会调用该方法,所以只需要在该方法中控制当前对象只创建一次即可
if (_instance == nil)
{
NSLog(@"创建了一个对象);
_instance = [[super allowWithZone:zone] init;
}
return _instance;
//以下代码在多线程中也能保证只执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[super allowWithZone:zone] init];
} ;)
return _instance;
} //copyWithZone方法用什么调用?对象
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
} - (id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
} - (oneway void) release
{
//为保证整个程序过程中只有一份实例,在这个方法中什么都不做
} - (instancetype)retain
{
return _instance;
} - (NSUInteger)retainCount
{
//注意:为了方便程序员之间沟通,一般情况下不会在单例中返回retainCount = 1,而是返回一个比较大的值
return MAXFLOAT;
}

七.宏定义抽取单例

// 如何判断当前是ARC还是MRC?
// 可以在编译的时候判断当前是否是ARC
#if __has_feature(objc_arc)
NSLog(@"ARC");
#else
NSLog(@"MRC");
#endif

/*********Singleton.h**********/
// 以后就可以使用interfaceSingleton来替代后面的方法声明
#define interfaceSingleton(name) +(instancetype)share##name


#if __has_feature(objc_arc)
// ARC
#define implementationSingleton(name) \
+ (instancetype)share##name \
{ \
name *instance = [[self alloc] init]; \
return instance; \
} \
static name *_instance = nil; \
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[super allocWithZone:zone] init]; \
}); \
return _instance; \
} \
- (id)copyWithZone:(NSZone *)zone{ \
return _instance; \
} \
- (id)mutableCopyWithZone:(NSZone *)zone \
{ \
return _instance; \
}
#else
// MRC #define implementationSingleton(name) \
+ (instancetype)share##name \
{ \
name *instance = [[self alloc] init]; \
return instance; \
} \
static name *_instance = nil; \
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[super allocWithZone:zone] init]; \
}); \
return _instance; \
} \
- (id)copyWithZone:(NSZone *)zone{ \
return _instance; \
} \
- (id)mutableCopyWithZone:(NSZone *)zone \
{ \
return _instance; \
} \
- (oneway void)release \
{ \
} \
- (instancetype)retain \
{ \
return _instance; \
} \
- (NSUInteger)retainCount \
{ \
return MAXFLOAT; \
}
#endif /********自定义类.h中********/ #import "Singleton.h" interfaceSingleton(类名); /********自定义类.m中********/ implementationSingleton(类名)

OC-copy,单例的更多相关文章

  1. iOS - OC SingleClass 单例类

    前言 单例对象能够被整个程序所操作.对于一个单例类,无论初始化单例对象多少次,也只能有一个单例对象存在,并且该对象是全局的,能够被整个系统访问到. 特点: 在内存中只有一个实例 提供一个全局的访问点 ...

  2. OC基础 单例

    #undef  AS_SINGLETON   #define AS_SINGLETON( __class ) \       + (__class *)sharedInstance;      #un ...

  3. OC 创建单例

    static BlockBackground *_sharedInstance = nil; + (BlockBackground*)sharedInstance { if (_sharedInsta ...

  4. 【Swfit】Swift与OC两种语法写单例的区别

    Swift与OC两种语法写单例的区别 例如写一个NetworkTools的单例 (1)OC写单例 + (instancetype)sharedNetworkTools { static id inst ...

  5. OC中两种单例实现方式

    OC中两种单例实现方式 写在前面 前两天探索了一下C++ 的单例,领悟深刻了许多.今天来看看OC中的单例又是怎么回事.查看相关资料,发现在OC中一般有两种实现单例的方式,一种方式是跟C++ 中类似的常 ...

  6. OC与Swift单例

    OC: +(instancetype)shareNetworkTools{ static id instance; static dispatch_once_t onceToken; //onceTo ...

  7. ios oc 和 swfit 用dispatch_once 创建单例

    网上已经有方法了,我这里就是抄了下,原文链接 http://bj007.blog.51cto.com/1701577/649413 http://blog.csdn.net/u010124617/ar ...

  8. OC中的单例设计模式及单例的宏抽取

    // 在一个对象需要重复使用,并且很频繁时,可以对对象使用单例设计模式 // 单例的设计其实就是多alloc内部的allocWithZone下手,重写该方法 #pragma Person.h文件 #i ...

  9. OC单例快速实现

    首先新建一个头文件,定义如下宏: 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 ...

  10. iOS开发——多线程OC篇&多线程中的单例

    多线程中的单例 #import "DemoObj.h" @implementation DemoObj static DemoObj *instance; // 在iOS中,所有对 ...

随机推荐

  1. 【java+selenium3】Tesseract-OCR识别图片验证码 (十六)

    [java+selenium+Tesseract-OCR(图片识别)+AutoIt(windows窗口识别)]完成自动化图片验证码识别! 一.AutoIt(windows窗口识别)参考:https:/ ...

  2. sqlalchemy insert on duplicate update

    sqlalchemy insert on duplicate update from sqlalchemy.dialects.mysql import insert insert_stmt = ins ...

  3. Java 关键字之 final

    欢迎学习 Java 基础文章系列之 final 关键字 final 代表什么意思? final 通常是指无法被改变或者不能被改变的,什么情况下不想被改变呢? 不想改变可能有两种原因:设计或者效率. 在 ...

  4. python递归三战:Sierpinski Triangle、Tower of Hanoi、Maze Exploring

    本文已做成视频教程投稿b站(视频版相对文本版有一些改进),点击观看视频教程 本文主要通过三个实例来帮助大家理解递归(其展示动画已上传B站): 谢尔宾斯基三角形(Sierpinski Triangle) ...

  5. .NET6运行时动态更新限流阈值

    昨天博客园撑不住流量又崩溃了,很巧正在编写这篇文章,于是产生一个假想:如果博客园用上我这个限流组件会怎么样呢? 用户会收到几个429错误,并且多刷新几次就看到了内容,不会出现完全不可用. 还可以降低查 ...

  6. 微信小程序(九)

    小程序运行环境与基本架构 每个小程序都是运行在它所在的微信客户端上的,通过微信客户端给它提供的运行环境,小程序可以直接获取微信客户端的原生体验和原生能力. wxml视图文件和wxss样式文件都是对渲染 ...

  7. 花了30天才肝出来,史上最全面Java设计模式总结,看完再也不会忘

    本文所有内容均节选自<设计模式就该这样学> 序言 Design Patterns: Elements of Reusable Object-Oriented Software(以下简称&l ...

  8. [FJ2021]D2T3题解

    考试的时候一点思路没有,最近听福州的神仙的一些做法. 想自己推一下. 题目大概是这样的 \(a_i = \frac{i\ *\ a_{i - 1} \ + \ i\ * \ (i\ -\ 1)\ * ...

  9. 常见 js 数组方法使用详解

    数组常用方法总结 concat filter map some every reduce sort includes join some every 语法:array.every(function(c ...

  10. PostgreSQL 数据库备份与还原

    PostgreSQL 数据库备份与还原 目录 备份 还原 栗子 备份 PostgreSQL提供的一个工具pg_dump,逻辑导出数据,生成sql文件或其他格式文件,pg_dump是一个客户端工具,可以 ...