oc基本知识

字符串,occ[msg UTF8String]

coc[NSString stringWithUTF8String:oper]

 

@public : 公共的,在任意的地方都可以通过对象名来访问.

     @protected : 受保护的. 只能在本类以及本类的子类中访问.

     @package : @package 修饰的属性只能在当前框架中访问.

     @private 私有的,@private修饰的属性只能在本类的对象方法实现中访问.

              在子类中都无法访问.

              子类继承父类的所有的成员.包括私有成员,只不过私有的成员在子类无法直接访问

 

 当类第1次被访问的时候.会将这个类存储到代码段之中.类一旦被存储到代码段之中.直到程序结束的时候才会被回收.类是以Class对象的形式存储在代码段之中的.

Class c1 =  [Person class];

C1等价于Person

Class有个SEL类对象的属性,用来储存方法,

SEL s1 = @selector(sayHi);获取方法对象SEL

[p1 performSelector:SEL消息];发消息

传递参数

 

         [p1 performSelector:s1 withObject:@"东北大乱炖"];

     

     3). 如果参数超过了两个.

         参数设计为1个数组. 或者将参数设计为1个对象.对象的属性就是参数.

 

@property NSString *name,*gender;

Setter,getter声明

@property float height,weight;

@synthesize name = _name,age = _age ,weight = _weight,height = _height;//基本不用

@property增强了,一个搞定声明实现都有了

 

静态类型: 指针指向的对象就是1个本类对象 那么这样就叫做静态类型.

              Person *p1 = [Person new];

     动态类型: 指针指向的对象不是本类对象,而是子类对象 这样就叫做动态类型.

              Person *p1 = [Student new]; 

 1个父类指针指向1个子类对象的时候,只能通过这个父类指针调用这个子类对象中的父类成员.

     子类独有的成员无法调用.

 

a. NSObject指针在编译器编译的时候.会做编译检查.

         b. id指针在编译的时候,编译器不会做编译检查.直接通过.

a. 如果指针类型是id类型的,只能通过id类型的指针调用方法.

        

         b. 不能通过id指针访问属性,也不能通过id指针使用点语法.

如果方法的返回值是当前类的对象.返回值写instancetype.

 

  idinstancetype.

  1). instancetype只能作为方法的返回值.代表返回值是当前类的对象.

          id可以作为返回值也可以作为参数.

       2). id1个类型. instancetype不是1个类型是1个关键字.

 

   1). 判断指针指向的对象中是否有指定的方法.这个方法能不能执行.对象方法

 

        - (BOOL)respondsToSelector:(SEL)aSelector; *****

 

        + (BOOL)instancesRespondToSelector:(SEL)aSelector;

        判断类的对象是否可以调用指定的方法.

 

 

    2). 判断类方法是否可以执行.

 

        [类名 respondsToSelector:SEL];

 

 

    3). 判断对象是否为指定类的对象或者子类对象.

 

        - (BOOL)isKindOfClass:(Class)aClass;

 

    4).判断对象是否为指定类的对象.不包括子类.

         - (BOOL)isMemberOfClass:(Class)aClass;

 

    5).判断1个类是否为另外1个类的子类.

 

       + (BOOL)isSubclassOfClass:(Class)aClass;

 

1). 如果希望对象1创建出来 对象的属性的值是我们指定的值 那么就可以重写构造方法.

 

     2). 如果有1段代码.你希望在创建对象的同时就自动执行.那么就把这段代码写在构造方法中.

- (instancetype)init

         {

             if(self = [super init])

             {

                 初始化当前子类对象的成员.

             }

             return self;

         }

 

内存及MRCARC的使用

: 局部变量. 当局部变量的作用域被执行完毕之后,这个局部变量就会被系统立即回收.

     : OC对象.使用C函数申请的空间.

     BSS: 未初始化的全局变量、静态变量. 一旦初始化就回收 并转存到数据段之中.

     数据段: 已经初始化的全局变量、静态变量. 直到程序结束的时候才会被回收.

     代码段: 代码. 程序结束的时候,系统会自动回收存储在代码段中的数据.

 

     栈、BSS段、数据段、代码段存储在它们中的数据的回收,是由系统自动完成的.不需要我们干预.

 

 


 

 

MRC环境下

分配在堆区中的OC对象,是肯定需要程序员回收的.

1个对象都有1个属性.叫做retainCount.叫做引用计数器. 类型是unsigned long 占据8个字节.

NSUInteger count = [p1 retainCount];p1.retainCount获取引用计数器

+1[p1 retain];

-1[p1 release];

默认是1,销毁为0 ,自动调用dealloc方法

- (void)dealloc

{

    NSLog(@"当你看到这句话的时候,说明我已经离开这个世界了.");

    [super dealloc];

}

 

 

mrc内存管理的原则

     1). 有对象的创建,就要匹配1release

 

     2). retain的次数和release的次数要匹配.

 

     3). 谁用谁retain. 谁不用谁release.

         谁负责retain 谁就负责relase

 

     4). 只有在多1个人用的时候才retain 1个人使用的时候才release

 

野指针和僵尸对象

C语言中的野指针: 定义1个指针变量.没有初始化.这个指针变量的值是1个垃圾值,指向1块随机的空间.这个指针就叫做野指针.

 

     OC中的野指针: 指针指向的对象已经被回收了.这样的指针就叫做野指针.

 

内存回收的本质

     申请1个变量,实际上就是向系统申请指定字节数的空间.这些空间系统就不会再分配给别人了.

     当变量被回收的时候,代表变量占用的字节空间从此以后系统可以分配给别人使用了.

     但是字节空间中存储的数据还在.

 

     回收对象:

     所谓的对象的回收,指的是对象占用的空间可以分配给别人.

     当这个对象占用的空间没有分配给别人之前 其实对象数据还在.

僵尸对象

    1个已经被释放的对象,但是这个对象所占的空间还没有分配给别人.这样的对象叫做僵尸对象.

 

    我们通过野指针去访问僵尸对象的时候.有可能没问题 也有可能有问题.

 

    当僵尸对象占用的空间还没有分配给别人的时候.这是可以的.

    当僵尸对象占用的空间分配给了别人使用的时候 就不可以.

 

 

 我们认为只要对象称为了僵尸对象,无论如何 都不允许访问了.

    就希望如果访问的是僵尸对象,无论如何报错.

 

    僵尸对象的实时检查机制.可以将这个机制打开. 打开之后. 只要访问的是僵尸对象,无论空间是否分配 就会报错.

 

 

 为什么不默认打开僵尸对象检测.

 

    一旦打开僵尸对象检测 那么在每访问1个对象的时候 都会先检查这个对象是否为1个僵尸对象,

    这样是极其消耗性能的.

 

  使用野指针访问僵尸对象会报错. 如何避免僵尸对象错误..

 

    1个指针称为野指针以后.将这个指针的值设置nil

 

    1个指针的值为nil 通过这个指针去调用对象的方法(包括使用点语法)的时候.不会报错. 只是没有任何反应.

                     但是如果通过直接访问属性 -> 就会报错

 

 内存泄露

     指的是1个对象没有被及时的回收.在该回收的时候而没有被回收

     一直驻留在内存中,直到程序结束的时候才回收.

单个对象的内存泄露的情况.

     

     1). 有对象的创建,而没有对应的relase

 

     2). retain的次数和relase的次数不匹配.

 

     3). 在不适当的时候,为指针赋值为nil

 

     4). 在方法中为传入的对象进行不适当的retain

 

 

 

多对象引用时

- (void)setCar:(Car *)car

{

    if(_car != car)//说明新旧对象不是同1个对象.

    {

        [_car release];//才去release旧的

        _car = [car retain];//retain新的.

    }

}

对象挂掉前,把它指向的对象1

- (void)dealloc

{

    NSLog(@"人死了");

    [_car release];

    [_name release];

    [super dealloc];

}

   当属性的类型是OC对象类型的时候,那么就使用retain

     当属性的类型是非OC对象的时候,使用assign.

retain参数.只是生成标准的setter方法为标准的MRC内存管理代码 不会自动的再dealloc中生成relase的代码.

     所以, 我们还要自己手动的在deallocrelease

readwrite: 默认值.代表同时生成getter setter

     readonly: 只会生成getter 不会生成setter

属性为BOOL,参数getter=isGoodMan

 

避免头文件导入循环引用@class Book;

1. 当两个对象相互引用的时候.

     A对象的属性是B对象  B对象的属性是A对象.

     这个时候 如果两边都使用retain 那么就会发生内存泄露.

  2. 解决方案: 1端使用retain 另外1端使用assign 使用assign的那1端在dealloc中不再需要release.

 

将创建的对象,存入到自动释放池之中. 就不再需要手动的relase这个对象了.

     因为池子销毁的时候 就会自动的调用池中所有的对象的relase

     自动释放池的好处: 将创建的对象存储到自动释放池中,不需要再写release

 

@autoreleasepool

//            {

//                 Person *p3 = [[[Person alloc] init] autorelease];

//            }

 autorelease 的调用只有放在自动释放池之中 才可以讲其存储到自动释放池之中, 对象的创建可以在外面

 

使用类方法得到的对象,要求这个对象就已经被autorelease过了.

         + (instancetype)person

         {

            return [[[self alloc] init] autorelease];

         } 

         这样,我们直接调用类方法.就可以得到1个已经被autorelease过的对象.

 

什么时候使用结构体: 1). 封装数据只有属性 2) 属性较少. 3个以下.

    什么时候使用类: 1).封装数据既有属性也有行为. 2).只有属性 但是属性较多.

 

ARC环境下

ARC机制下,retain release dealloc autorelease retainCount这些方法方法无法调用.

dealloc不允许[super dealloc];

 

__strong来显示的声明强指针.

    弱指针: 使用__weak标识的指针就叫做弱指针.

ARC机制下. 当对象被回收的时候. 原来指向这个对象的弱指针会被自动设置为nil

ARC的机制下,@property参数不能使用retain,使用参数, strongweak,如果不写,默认是strong.

当两个对象循环引用时,一个用weak,一个用strong

ARC下使用MRC,使用命令.  -fno-objc-arc

 

 

MRC文件转换为ARC

 

 

GC: 程序在运行的期间,1个东西叫做垃圾回收器.不断的扫描堆中的对象是否无人使用.

分类(延展)协议

当一个类有多个方法时,把功能相近的方法放进一个分类中category

 使用分类注意的几个地方:

    1. 分类只能增加方法,不能增加属性

 

2. 在分类之中可以写@property 但是不会自动生成私有属性. 也不会自动生成getter setter的实现.

       只会生成getter setter的声明.

       所以,你就需要自己写getter setter的声明. 也需要自己定义属性 这个属性就必须在本类中.

 

3. 在分类的方法实现中不可以直接访问本类的真私有属性(定义在本类的@implementation之中)

       但是可以调用本类的getter setter来访问真私有属性.

 

     4. 分类中可以存在和本类同名方法的.

       当分类中有和本类中同名的方法的时候,优先调用分类的方法.哪怕没有引入分类的头文件.

       如果多个分类中有相同的方法,优先调用最后编译的分类.

 

为系统自带的类写分类 这个就叫做非正式协议.

unichar ch = [self characterAtIndex:i];从字符串中取出第i个字符.

 

 延展: Extension

     1个特殊的分类. 所以延展也是类的一部分.

             只有声明没有实现.和本类共享1个实现.

写在延展中的成员,就相当于是这个类的私有成员.只能在本类的实现中访问.

         外部不能访问.延展天生就是来私有化类的成员的.

 

延展和分类的区别

    1). 分类有名字.延展没有名字 1个匿名的分类.

    2). 1个分类都有单独的声明和实现. 而延展只有声明 没有单独的实现 和本类共享1个实现,

    3). 分类中只能新增方法. 而延展中任意的成员都可以写.

    4). 分类中可以写@property 但是只会生成getter setter的声明.

        延展中写@property 会自动生成私有属性 也会生成getter setter的声明和实现.

.m文件中延展增加私有属性和方法

@interface Student ()

{

    NSString *_name;

}

 

@property(nonatomic,assign)int age;

 

- (void)study;

- (void)play;

 

@end

 

协议:protocol. 

   专门用来声明一大堆方法. (不能声明属性,也不能实现方法,只能用来写方法的声明).

 

@protocol 协议名称 <NSObject>

 

     方法的声明;

 

     @end

 

无论是@required还是@optional你都可以不实现. 编译器是不会报错的. 仍然可以编译 运行.

     唯一的区别就是: 当遵守协议的类不实现协议中的方法的时候,@required会给1个警告. @optional警告都木有.

默认的是@required

协议可以继承另外1个协议.A 协议 继承了 B协议. 那么A协议中不仅有自己的方法的声明,还有B协议中的方法的声明.

 

可以使用id指针.

      id<协议名称> 指针名;这个指针所指向的对象拥有了协议的方法的声明

 

代理设计模式

 

       代理遵守协议,

主人定义代理属性,

主人的代理属性设置代理源,

      代理去实现方法,

主人让代理调用代理方法

block1个数据类型.能作为函数的参数

typedef void (^NewType)();起别名    void test(NewType block1)无参数五返回值block变量作为函数参数

调用block1()

void test2(int (^paramsBlock)(int num1,int num2))有参有返回值的block作为函数参数

 

- (void)bianLiWithBlock:(void (^)(int val))processBlock//参数传递代码段

定义一个代码段

void (^pBlock)(int val) = ^(int val){

        NSLog(@"val = %d",val + 1);

    };

NSArray NSDictionary  NSFileManger

无论是NSArray还是NSMutbaleArray里面都只能存储OC对象.

     基本数据类型是无法存储的.

 NSArray *arr = @[@"jack",@"rose",@"lili"];

For in 遍历数组, for(元素类型 变量名 in 数组名)

声明在for()中的变量叫做迭代变量.

block遍历

[arr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL *stop) {

//        NSLog(@"%@.lenth = %lu",obj,[obj length]);

//        NSLog(@"idx = %lu",idx);

//        //如果想停止遍历,就讲stop指针指向的BOOL变量的值改为YES

//        if(idx == 1)

//        {

//            *stop = YES;

//        }

//    }];

 

 

NSNumberFoundation框架中定义好的1个类.这个类的对象的作用就是用来包装基本数据类型的.

@10;包装为对象

如果后面的数据是1个变量 那么这个变量就必须要使用小括弧括起来.

 @(num)

 

NSDictionary 字典数组

: 只能是遵守了NSCoping协议的对象. NSString就是遵守了这个协议.

   : 只能是OC对象.

 NSDictionary *dict = @{@"name":@"rose",@"age":@"18",@"addredd":@"BeiJingXXSttreet"};

存取键值对的时候,哈希算法.

 

存储进去之后,一旦要取值.就是全部取出. NSArray

    存储进去之后.取值只会取指定的几个元素 字典数组.

 

    将字典数组的信息保存到plist文件中.

    - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;

 

    plist文件中还原回字典.

    + (nullable NSDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfFile:(NSString *)path;

MRC使用@[]或者@{}创建的集合已经是被autorelease过的了.     

     直接调用和类同名的类方法创建的对象 也是被autorelease过的了.

 

NSFileMangerFoundation框架提供的1个类.   

     这个类作用: 用来操作磁盘上的文件 文件夹  对他们进行创建、删除、复制 拷贝 移动.....

这个类的对象是以单例模式创建的.

 NSFileManager *fileManager = [NSFileManager defaultManager];

 

1). 判断指定的文件或者文件夹在磁盘上是否真实的存在

        - (BOOL)fileExistsAtPath:(NSString *)path;

 

     2).判断指定的路径是否真实的存储在我们的磁盘之上,并且判断这个路径是1个文件夹路径还是1个文件路径.

        - (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory;

        返回值:代表这个路径是否真实存在.

        参数指针: 代表这个路径是否是1个文件夹路径

     

     3). 判断指定的文件夹或者文件是否可以读取.

         - (BOOL)isReadableFileAtPath:(NSString *)path;

 

     4). 判断指定的文件夹或者文件是否可以写入.

         - (BOOL)isWritableFileAtPath:(NSString *)path;

 

     5). 判断指定的文件夹或者文件是否可以删除.

         - (BOOL)isDeletableFileAtPath:(NSString *)path

 

1).获取指定文件或者文件夹的属性信息.

       - (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error

       返回的是1个字典,如果要拿到特定的信息 通过key

 

    2).获取指定目录下的所有的文件和目录. 是拿到指定目录下的所有的文件和目录 所有的后代目录和文件.

       子目录的子目录的子目录 所有的都可以拿到.

       - (NSArray *)subpathsAtPath:(NSString *)path;

 

    3).获取指定目录下的所有的子目录和文件 不保护孙子辈.

       - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error

 

  

    1). 在指定的目录创建文件.

        - (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data attributes:(NSDictionary *)attr

        1个参数: 要创建的文件的路径.

        2个参数: 这个文件的内容要传递这个文件的二进制格式

                  这个二进制的数据格式 使用NSData对象来封装.

 

                  NSData: 将别的格式的数据转换为二进制数据.

    

                  将字符串转换为NSData二进制的方式.调用字符串对象的

                   - (NSData *)dataUsingEncoding:(NSStringEncoding)encoding

                   编码参数: NSUTF8StringEncoding

                  指定1个编码 就可以将字符串转换为二进制数据 存储在NSData对象之中.

 

                  最后再将这个二进制对象通过这个方法写入.

 

                  如果想创建1个空文件 2个参数就给nil

 

        3个参数: 指定创建的文件的属性.如果想要使用系统的默认值使用nil

 

    2). 在指定的目录创建文件夹.

        - (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary *)attributes error:(NSError **)error

 

        1个参数: 路径.

        2个参数: YES,做一路创建. 如果是NO就不会做一路创建.

        3个参数: 指定属性 nil为系统默认属性.

        4个参数.

 

    3).拷贝文件.

       - (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error

    

    4).移动文件 剪切 文件的重命名. 重名的原理: 将文件移动到原来的目录并改名.

       - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error

 

    5).删除文件.

       - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error

 

      注意 删除的文件不会倒废纸篓 而是直接删除,所以请小心使用.

 

CGRect rect;

         rect.origin = (CGPoint){10,20};

         rect.size = (CGSize){100,30};

 

NSValue 的对象就是用来包装结构体变量的.

CGSize s1 =   CGSizeMake(100, 10);

NSValue *v1 = [NSValue valueWithSize:s1];

NSLog(@"%@",NSStringFromSize(v1.sizeValue));

NSDate

 NSDate *date = [NSDate date];格林威治时间. 0时区的时间.   8.

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

formatter.dateFormat = @"yyyyMMdd HHmmss";

NSString *str =[formatter stringFromDate:date];

 - (NSString *)stringFromDate:(NSDate *)date; //将日期类型换换为字符串

 - (NSDate *)dateFromString:(NSString *)string;//将字符串转换为日期对象.

 + (instancetype)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;

        在当前的时间的基础之上,新增指定的秒.后的时间

 

 求两个时间之间的差.

        可以实现的效果.就是可以计算出执行代码所花费的时间.

        - (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate;

 

指定日历对象取到日期的对象的那些部分. 是要取那1个时间对象的部分.

NSCalendar *calendar = [NSCalendar currentCalendar];    

     // 返回1个日期组件对象.这个对象中就有指定日期的指定部分.

     NSDateComponents *com =  [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:date];

 

NSString类型的. @property参数使用copy.

   NSString ----> copy ---> 不可变字符串  没有产生新对象,而是直接将对象本身的地址返回. 这种拷贝我们叫做浅拷贝

         NSMutableString --> copy --> 1个可变的字符串对象有产生1个新对象.这样的拷贝我们叫做深拷贝.

NSString  ---> mutableCopy --> 可变字符串对象. 深拷贝.

         NSMutableString --> mutableCopy --> 可变字符串对象 深拷贝.

字符串对象如果是浅拷贝. 会将对象的引用计数器+1

 

         字符串对象如果是深拷贝. 原来的对象的引用计数器不变.新拷贝出来的对象的引用计数器为1.存储在常量区的字符串对象的引用计数器是1个超大的数.并且retainrelease无效.

1. 单例模式:

 

     1个类的对象,无论在何时创建也无论在什么地方创建 也无论创建多少次.创建出来的都是同1个对象.

 

 

  2. 无论如何创建对象,最终都会调用alloc方法来创建对象.

     

     1). alloc方法的内部. 其实什么都没有做,只是调用了allocWithZone:方法.

 

     2). 实际上真正申请空间 创建对象的事情是allocWithZone:方法在做.

 

 

  3. 要实现单例模式.

 

     重写+ allocWithZone:

 

     + (instancetype)allocWithZone:(struct _NSZone *)zone

     {

         static id instance = nil;

         if(instance == nil)

         {

            instance = [super allocWithZone:zone];

         }

         return instance;

     }

 

 

  4. 单例模式的规范:

     如果类是1个单例模式.要求为类提供1个类方法.来返回这个单例对象.

 

 

     类方法的名称必须以 shared类名; default类名;


你可能感兴趣的:(ios,内存,单例设计模式,协议,分类,oc,block)