Object-C初阶+进阶基础语法

OC初阶

类的设计

oc_3

main

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Person *p = [[Person alloc] init]; //分配内存并且调用init函数,得到self指针
        NSLog(@"age-%d, height-%f, name-%@", p->age, p->height, p->name);
        
        [p setAge:100];
        [p setHeight:190.0];
        
        NSLog(@"age-%d, height-%f, name-%@",[p getAge], [p getHeight], [p getName]);
    }
    return EXIT_SUCCESS;
}

person.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
{
    //成员变量字段
@public
    int age;
@public
    float height;
@public
    NSString *name;
}
//对象方法和类方法
//凡事以init开头的方法,一般都是构造方法
- (id) init;
- (id) initWithAge:(int)newAge;
- (id) initWithAge:(int)newAge andHeight:(float)newHeight;

- (void) setAge:(int)newAge;
- (void) setHeight:(float)newHeight;
- (void) setAge:(int)newAge andHeight:(float)newHeight;

- (int) getAge;
- (float) getHeight;
- (NSString*) getName;

@end

NS_ASSUME_NONNULL_END

person.m

objectivec 复制代码
#import "Person.h"

@implementation Person
{
    //成员变量写在这里,私有变量
}
//成员变量写在这里,全局变量

- (id) init{
    if (self = [super init]){
        age = 30;
        height = 178.0;
        name = @"zhangsan";
        //super表示父类,[super init]表示父类调用了自己的构造方法:1分配内存 2内存空间指向self
        //self表示对象本身
    }
    return self;
}
- (id) initWithAge:(int)newAge{
    if (self = [super init]){
        age = newAge;
        height = 178.0;
        name = @"zhangsan";
    }
    return self;
}
- (id) initWithAge:(int)newAge andHeight:(float)newHeight{
    if(self = [super init]){
        age = newAge;
        height = newHeight;
        name = @"zhangsan";
    }
    return self;
}
- (void) setAge:(int)newAge;
{
    age = newAge;
}
- (void) setHeight:(float)newHeight;
{
    height = newHeight;
}
- (void) setAge:(int)newAge andHeight:(float)newHeight{
    age = newAge;
    height = newHeight;
}

- (int) getAge{
    return age;
}
- (float) getHeight{
    return height;
}
- (NSString*) getName{
    return name;
}
@end

通过小明使用电脑来说明类的设计

Mac.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Mac : NSObject
- (void) open;
- (void) off;
- (void) operation;
@end

NS_ASSUME_NONNULL_END

Mac.m

objectivec 复制代码
#import "Mac.h"

@implementation Mac
- (void) open{
    NSLog(@"mac is open");
}
- (void) off{
    NSLog(@"mac is shutDown");
}
- (void) operation;
{
    NSLog(@"xiaoming is operator");
}

@end

Xiaoming.h

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Mac.h"

NS_ASSUME_NONNULL_BEGIN

//注意:OC中类名必须大写
@interface Xiaoming : NSObject
- (void) studyWithMac:(Mac*)macBook;
@end

NS_ASSUME_NONNULL_END

Xiaoming.m

objectivec 复制代码
#import "xiaoming.h"

@implementation Xiaoming
- (void) studyWithMac:(Mac*)macBook{
    [macBook open];
    [macBook operation];
    [macBook off];
}
@end

main,m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Xiaoming.h"
#import "Mac.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Xiaoming *xm = [[Xiaoming alloc] init];
        Mac *mac = [[Mac alloc] init];
        
        [xm studyWithMac:mac];
    }
    return EXIT_SUCCESS;
}

函数的声明与调用

类的封装

oc_4

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
@interface Mac : NSObject
{
    int cpu;
    int inch;
}
- (void) setCpu:(int) newCpu;
- (void) setInch:(int) newInch;

@end

@implementation Mac
- (void) setCpu:(int) newCpu{
    cpu = newCpu;
}
- (void) setInch:(int) newInch{
    inch = newInch;
}


@end

@interface Person : NSObject
{

    int age;
    NSString* name;
    Mac* mac;
}
- (id) init;
- (void) setAge:(int) newAge;
- (void) setName:(NSString*) newName;
- (void) setMac:(Mac*) newMac;

@end

@implementation Person
- (id) init{
    if(self = [super init]){
        age = 18;
        name = @"zhangsan";
    }
    return self;
}
- (void) setAge:(int) newAge{
    age = newAge;
}
- (void) setName:(NSString*) newName{
    name = newName;
}
- (void) setName:(NSString*) newName andAge :(int) newAge{
    name = newName;
    age = newAge;
}
//对类进行封装
- (void) setMac:(Mac*) newMac{
    [self setNewMac:newMac];
}
//这样如果有人把函数名改了,他知道使用者还是会调用setMac函数,就可以直接再封装一个setNewMac函数
- (void) setNewMac:(Mac*) newMac{
    mac = newMac;
}

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Person* p = [[Person alloc] init];
        //这两个都可以修改age属性,第二个更好
//        p -> age = 10;
        [p setAge:10];
        
        Mac* mac = [[Mac alloc] init];
        [mac setCpu:64];
        [mac setInch:20];
        
        [p setMac: mac];
    }
    return EXIT_SUCCESS;
}

继承和重写

oc_5

Animal.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Animal : NSObject
- (void) eat;
- (void) cry;
@end

NS_ASSUME_NONNULL_END

Animal.m

objectivec 复制代码
#import "Animal.h"

@implementation Animal
- (void) eat{
    NSLog(@"动物用嘴吃饭");
}
- (void) cry{
    NSLog(@"咆哮");
}
@end

Dog.h

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Animal.h"
NS_ASSUME_NONNULL_BEGIN

@interface Dog : Animal

@end

NS_ASSUME_NONNULL_END

Dog.m

objectivec 复制代码
#import "Dog.h"

@implementation Dog
-(void) cry{
//    [super cry];//是否保留父类方法
    NSLog(@"狗吠..."); //新功能
}
@end

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Animal.h"
#import "Dog.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Dog* dog = [[Dog alloc] init];
        [dog eat];
        [dog cry];
    }
    return EXIT_SUCCESS;
}

self指针

oc_6

Person.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
{
    NSString* name;
}
- (void) setName:(NSString*) newName;
- (NSString*)name; //这里纠正前面的写法,我们一般写get函数时,不写getName,直接写name即可

+ (Person*) person;
@end

NS_ASSUME_NONNULL_END

Person,m

objectivec 复制代码
#import "Person.h"

//self:当前方法是谁在调用,self就是谁
@implementation Person
- (void) setName:(NSString*) newName{
    [self testSelfIsWho];
    name = newName;
}
- (NSString*)name{
    return name;
}
+ (Person*) person{
    id obj = [[self alloc]init];
    NSLog(@"%@", self);
    return obj;
}
- (void) testSelfIsWho{
    NSLog(@"%@", self);
}
@end

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Person* p = [[Person alloc]init];
        [p setName:@"zhangsan"];//<Person: 0x100804800>,p 对象自己的内存地址。
        
//        [Person person];//Person,类本身(Person)

    }
    return EXIT_SUCCESS;
}

终极总结:

在 对象方法(-) 里

self = 当前调用方法的那个对象

在 类方法(+) 里

self = 当前类本身

点(.)语法的实现和使用

oc_7

在OC中,点语法只适用于成员变量和默认的get和set方法使用.

@property关键字 的使用

strong和assign的区别

Person.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

//strong:继承自NSObject类对象的变量就用strong修饰
//assign:不是继承自NSObject类对象的变量就用strong修饰
@property (nonatomic, strong)NSString* name;
@property (nonatomic, assign)int age;
@property (nonatomic, assign)float height;

- (void)getProperty;
@end

NS_ASSUME_NONNULL_END

Person.m

objectivec 复制代码
#import "Person.h"

@implementation Person
//在OC 5.0之后,这条语句也可以不写了
//写了.h文件中的三条声明语句后,所有对应的get和set方法系统都会自动生成
@synthesize name = _name,age = _age,height = _height;

-(void)setName:(NSString *)newName{
    NSLog(@"在执行self.name时调用了setName方法");
    _name = newName;
}

-(int) age{
    NSLog(@"get方法启动");
    return _age;
}

- (void)getProperty{
    //访问方式1
//    _name = @"zs";
//    _height = 123.0;
//    _age = 12;
    
    //访问方式2,使用点语法
//    self.name = @"zs"; //赋值,验证set方法
    NSLog(@"%d", self.age);//取值,验证get方法
//    self.height = 123.0;
//    self.age = 12;

}

@end

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        
        Person* p = [[Person alloc]init];
        [p getProperty];
    }
    return EXIT_SUCCESS;
}

继承和类别的区别

oc_8

继承 vs 类别

相同点:都是无法满足现有需求,拓展新属性和方法

不同点:

1.继承既能拓展成员变量,成员属性,也能拓展方法,类别只能拓展方法

2.继承拓展的属性和方法,都是在子类对象中调用,而类别添加的新方法,可以在原类中直接调用

使用场景:

一般自定义的类使用继承,系统类使用类别

Foundation框架的使用

NSString

oc_9

不可变字符串NSString的构造

方法:

比较两个字符串是否相等

比较两个字符串的大小

从左往右截取

从右往左截取

范围截取(位置,长度)

找出子串在原串的位置

objectivec 复制代码
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
//       //声明一个常量字符串
//        NSString* s1 = @"nihao";
//        //用已知字符串,生成一个新字符串
//        NSString* s2 = [[NSString alloc] initWithString:s1];
//        //用一个C语言字符串,生成OC字符串
//        NSString* s3 = [[NSString alloc] initWithUTF8String:"wohenhao"];
//        
////        NSLog(@"s1-%@, s2-%@, s3-%@", s1, s2, s3);
//        
//        //也可以通过+方法得到
//        NSString* s4 = [NSString stringWithString:s1];
//        NSString* s5 = [NSString stringWithUTF8String:"vudvhud"];
//        
//        NSLog(@"s4-%@, s5-%@", s4, s5);
        
        NSString *str1 = @"1234567890";
        NSString *str2 =@"1234567890";
        NSString *str3 =@"1234";
        BOOL ret = [str1 isEqualToString:str3];//比较两个字符串相同的方法。
        
        NSLog (@"%d", ret) ;
        
        NSComparisonResult result = [str1 compare: str3];
        if (result == NSOrderedAscending) {
            NSLog (@"str1 < str2"); //按升序
        }
        else if (result == NSOrderedDescending)
        {
            NSLog (@"str1 > str2");//按降序
        }
        else if (result == NSOrderedSame)
        {
            NSLog (@"str1 == str2");
        }
        
        NSString *str4 =[str1 substringToIndex:6];//通过字符串的切割,得到新的字符串,原字符串 str1 不变。下标从0开始,左闭右开区间[0,6)
        NSLog (@"str4 is %@", str4);//123456
        
        NSString *str5 = [str1 substringFromIndex:6];//从第6位开始,一直到结尾,左闭右闭区间
        NSLog (@"str5 is %@", str5);//7890
        
        NSString *str6 = [str1 substringWithRange:NSMakeRange (6, 2
        )];//指定区间截取
        NSLog (@"str6 is --- %@", str6);//78
        
        NSRange range = [str1 rangeOfString: str3];//判断str3是否在str1内
        if (range.length > 0) {
            NSLog(@"str1 中 含有字符串 str3");
        }
        NSLog (@"location is szd length is szd", range.location,
        range.length);
       
    }
    return EXIT_SUCCESS;
}

NSMutableString

oc_10

可变字符串NSMutableString的构造

方法:

直接追加

以格式化的形式追加

在某个位置插入字符串

范围删除(位置,长度)

把某个位置的字符替换成字符串

objectivec 复制代码
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *str = @"hello world!";
        NSMutableString *mStr1 = [[NSMutableString alloc]
        initWithString: str];
        NSMutableString *mStr2 = [NSMutableString
        stringWithString:str];
        NSMutableString *mStr3 = [[NSMutableString alloc]
        initWithUTF8String: "hellokworld!"];
        NSMutableString *mStr4 = [NSMutableString
                                  stringWithUTF8String: "hello world!"];
        
//        NSLog(@"mStr1 - %@", mStr1);
//        NSLog(@"mStr2 - %@", mStr2);
//        NSLog(@"mStr3 - %@", mStr3);
//        NSLog(@"mStr4 - %@", mStr4);
        
        [mStr1 setString:@"hello Objective-C!"];
//        NSLog (@"%@", mStr1);
        
        //1.增2.删.3查 4.改
        [mStr1 appendString: @"XXXX"];//直接追加
//        NSLog (@"%@",mStr1) ;
        [mStr1 appendFormat: @"%@,%c", str, 'o'];//格式化追加
//        NSLog (@"%@",mStr1) ;
        [mStr1 insertString:@"########" atIndex:5];//在指定位置插入指定字符串
//        NSLog (@"%@",mStr1);
        
        [mStr1 deleteCharactersInRange:NSMakeRange (7,4)];//删除指定范围内的字符
        
        NSRange range = [mStr1 rangeOfString: str];//查询str 在mStr1中所在位置
        NSLog (@"%@", mStr1);
        
        [mStr1 replaceCharactersInRange: (NSRange) {6,5}
        withString:@"0000000"];//从第6个位置开始,把接下来的5个字符替换成指定字符串
        NSLog (@"%@", mStr1);
    }
    return EXIT_SUCCESS;
}

NSArray

oc_11

不可变数组NSArray的构造

遍历方法:

枚举器遍历

快速遍历(常用)

i遍历

objectivec 复制代码
#import <Foundation/Foundation.h>

@interface Person : NSObject

@end

@implementation Person
-(NSString*) description{
    return @"this is Person description";
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person* p = [[Person alloc] init];
        NSArray* arr = [[NSArray alloc] initWithObjects:@"one",@"tow", p, nil];
        //数组可以装任意对象
        //数组中的每一个对象,其实是指向该对象的地址
//        NSLog(@"%@", arr);//NSLog这个方法以%@格式输出时,会调用对象的description方法
        
        NSArray* arr1 = [[NSArray alloc] initWithArray:arr];//用一个已知数组构造
        NSArray* arr3 = [[NSArray alloc] initWithArray:arr copyItems:YES];//若为yes,为深拷贝,若是no,那跟arr1的写法没区别
        
        //数组遍历
        //枚举器
        NSEnumerator* enumerator = [arr objectEnumerator];
        id obj;
        while(obj = [enumerator nextObject]){
//            NSLog(@"%@", obj);
        }
        //快速遍历法
        for(obj in arr){
//            NSLog(@"%@", obj);
        }
        //i 遍历法
        NSInteger count = [arr count];//获取数组元素的个数
        id temobj = [arr objectAtIndex:0];//获取数组指定下标的值
        
        NSInteger i = 0;
        for(i; i < [arr count]; i++){
            NSLog(@"%@", [arr objectAtIndex:i]);
        }
    }
    return EXIT_SUCCESS;
}

NSMutableArray

oc_12

可变数组NSMutableArray

它是不可变数组的一个子类。

相关操作:

通常定义一个空数组,

一个一个插入元素

随机删除每个元素

删除全部元素

对两个元素进行位置交换

字符串的切割,放入数组A中

对数组A的元素逆序放入数组B中

objectivec 复制代码
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray* arr = [[NSMutableArray alloc]initWithObjects:@"one",@"two",@"three", nil];
        NSMutableArray* arr1 = [[NSMutableArray alloc] init];//创建一个空的可变数组
        
        //一个一个加入元素
        [arr1 addObject:@"one"];
        [arr1 addObject:@"two"];
        [arr1 addObject:@"three"];
//        NSLog(@"%@", arr);
//        NSLog(@"%@", arr1);
        
        //指定元素删除
        [arr removeObject:@"one"];
//        [arr1 removeObject:@"two"];
//        NSLog(@"%@", arr);
//        NSLog(@"%@", arr1);
        
        //删除所有元素
        [arr removeAllObjects];
//        NSLog(@"%@", arr);
        
        //交换数组中两个元素的位置,用的是下标
        [arr1 exchangeObjectAtIndex:0 withObjectAtIndex:2];
//        NSLog(@"%@", arr1);
        
        NSString* str = @"this is caochun shi xi";
        //把字符串按空格切割,放入可变数组中
        NSMutableArray* arr3 = [str componentsSeparatedByString:@" "];
        NSLog(@"%@", arr3);
        
        //把arr3数组中的元素逆序
        NSMutableArray* arr4 = [[NSMutableArray alloc] init];
        NSEnumerator* enumer = [arr3 reverseObjectEnumerator];
        id obj = nil;
        while(obj = [enumer nextObject]){
            [arr4 addObject:obj];
        }
        NSLog(@"%@", arr4);
        
    }
    return EXIT_SUCCESS;
}

NSDictionary

oc_13

不可变字典NSDictionary的两种构造

遍历:

枚举器遍历

快速遍历

可变字典NSMutableDictionary

它是不可变字典的一个子类

相关操作:

通常定义一个空字典,

一对一对插入键值对

删除键值对(通过key)

objectivec 复制代码
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSDictionary* dic = [[NSDictionary alloc] initWithObjectsAndKeys:@"one",@"1", @"two",@"2", @"three",@"3", nil];
        //key:  @"1",   @"2",   @"3"
        //value:@"one", @"two", @"three"
        //字典中所有的key-value都是无序的,key是唯一的,value是不唯一的
        
        //通过key找value
        NSString* key = @"1";
        NSString* value = [dic objectForKey:key];
//        NSLog(@"%@", value);
        
        NSDictionary* dic1 = @{@"1":@"one", @"2":@"two", @"3":@"three"};
//        NSLog(@"%@", [dic1 objectForKey:@"3"]);
        
        //通过枚举器遍历
//        NSEnumerator* enm = [dic keyEnumerator];//获取key
        NSEnumerator* enm = [dic objectEnumerator];//获取value
        id obj;
        while(obj = [enm nextObject]){
//            NSLog(@"%@", obj);
        }
        
        //快速遍历
        //得到的是key值,但是可以通过key值快速获取value
        for(obj in dic1){
//            NSLog(@"%@", obj);
//            NSLog(@"%@", [dic1 objectForKey:obj]);
        }
        
        //可变字典 是不可变字典一个子类
        NSMutableDictionary *mDic = [[NSMutableDictionary alloc]
        initWithObjectsAndKeys:@"",@"", nil];
        NSMutableDictionary *mDic1 = [[NSMutableDictionary alloc]init];
        
        //插入一对一对的键值
        [mDic1 setObject:@"one" forKey:@"1"];
        [mDic1 setObject:@"tow" forKey:@"2"];
        [mDic1 setObject:@"three" forKey:@"3"];
        
        //删除键值
        [mDic removeObjectForKey:@"1"];
        NSLog (@"%@", mDic1);
    }
    return EXIT_SUCCESS;
}

NSNumber

oc_14

NSNumber类的使用

NSNumber类,把基本数据类型封装成对象。

如何把基本数据类型封装的对象

变成对象后就可以调用相关方法了(判断两个对象是否相等,比较对象的大小)

通过对象获取基本数据类型

objectivec 复制代码
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //把基本数据类型封装成对象
        NSNumber *intNumber = [[NSNumber alloc]initWithInt:1];
        NSNumber *charNumber = [[NSNumber alloc]initWithChar:'C'];
        NSNumber *floatNumber = [[NSNumber alloc]initWithFloat:
        5.0f];
        NSNumber *doubleNumber = [[NSNumber alloc]initWithDouble:
        6.7];
        NSArray * arr = [[NSArray alloc] initWithObjects:intNumber,
        charNumber, floatNumber, doubleNumber, nil];
        
        //变成对象后就可以调用相关方法了
        NSNumber *intNumber1 = [[NSNumber alloc]initWithInt:1];
        BOOL ret = [intNumber isEqualToNumber: intNumber1];
        NSLog (@"%d",ret);
        NSComparisonResult result = [intNumber1 compare: intNumber];
        if (result == NSOrderedAscending) {
                    NSLog (@"str1 < str2"); //按升序
                }
                else if (result == NSOrderedDescending)
                {
                    NSLog (@"str1 > str2");//按降序
                }
                else if (result == NSOrderedSame)
                {
                    NSLog (@"str1 == str2");
                }
        
        //把对象还原成基本数据类型
        int a = [intNumber intValue];
        float b = [floatNumber floatValue];
        char c = [charNumber charValue];
        double d = [doubleNumber doubleValue];
        NSLog (@"%d, &f, %c, %lf",a, b,c,d) ;
    }
    return EXIT_SUCCESS;
}

NSIndexSet

oc_15

不可变集合NSIndexSet

NSIndexSet 用于存储一组不重复、有序的索引值(类似 "位置编号")

集合的两种构造

集合中索引的使用(只能用连续的索引访问数组元素)

可变集合NSMutableIndexSet

随机添加索引

使用添加的索引随机访问数组元素

objectivec 复制代码
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSIndexSet* index = [[NSIndexSet alloc]initWithIndex:1];
        NSIndexSet* index1 = [[NSIndexSet alloc] initWithIndexSet:index];//使用已知集合初始化
//        NSLog(@"%@, %@", index, index1);
        
        NSIndexSet* index2 = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(2, 3)];
//        NSLog(@"index2 - %@", index2);
        //index2 - <NSIndexSet: 0x875a02e8890627cf>[number of indexes: 3 (in 1 ranges), indexes: (2-4)]
        //输出的含义:3 个索引:2、3、4    1 个连续区间:2--4
        
        //集合的使用
        NSArray* arr = @[@"one",@"two",@"three",@"four",@"five",@"six"];
        NSArray* newArr = [arr objectsAtIndexes:index2];
//        NSLog(@"%@", newArr);
        
        NSMutableIndexSet *mSet = [[NSMutableIndexSet alloc]init];
//        随机添加索引
//        使用添加的索引随机访问数组元素
        [mSet addIndex: 0];
        [mSet addIndex: 3];
        [mSet addIndex: 4];
        NSArray *newArr1 = [arr objectsAtIndexes: mSet];
        NSLog (@"%@", newArr1);
    }
    return EXIT_SUCCESS;
}

NSData

oc_16

NSData类:用于处理二进制数据的核心类,主要用于存储和操作原始字节流(如文件内容、网络数据、加密数据等)

字符串 -- 二进制数据

二进制数据 -- 字符串

OC进阶

oc_17

在MRC环境下,内存管理的引用计数,retain和release方法,引用计数为0时调用析构销毁空间

oc_18

在MRC环境下,set和get的书写

oc_19

自动释放池的使用和理解

1、autorelease 不会立刻改变引用计数

它只是把对象标记为 "在自动释放池结束时再释放",引用计数会在 @autoreleasepool 结束时才减 1。

2、retainCount 不能作为判断对象是否销毁的依据

当引用计数为 0 时,对象会被立刻回收,此时再调用 dog retainCount 是无效的,甚至会导致野指针崩溃。

Person.h

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Dog.h"
NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
{
    Dog* _dog;
}
@property (retain) Dog* dog;//在mrc模式下,要使用retain,
@end

NS_ASSUME_NONNULL_END

Person.m

objectivec 复制代码
#import "Person.h"

@implementation Person
@synthesize dog = _dog;
-(void)dealloc{
    NSLog(@"Person is death");
    [super dealloc];
}
//在mrc模式下,只要在.h文件中写了 @property (retain) Dog* dog;下面是set就不用再写了,系统会自动生成(自动retain)
//-(void) setDog:(Dog *)dog{
//    if(_dog != dog){
//        [_dog release];
//        _dog = nil;
//        _dog = [dog retain];
//    }
//}

@end

Dog.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Dog : NSObject
{
    int _id;
}
@property int id;
@end

NS_ASSUME_NONNULL_END

Dog.m

objectivec 复制代码
#import "Dog.h"

@implementation Dog
@synthesize id = _id;

-(void) dealloc{
    NSLog(@"dog %d is death", _id);
    
    [super dealloc];
}
@end

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Dog.h"
int main(int argc, const char * argv[]) {
    //这两种自动释放池可以并行使用
    @autoreleasepool {
        
        //code
        
    }
    
    //手动创建自动释放池
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    
    Dog* dog = [[[Dog alloc]init]autorelease];
    NSLog(@"r1 = %ld", [dog retainCount]);//初始化构造,1
    dog.id = 1;
    
    Person* p = [[[Person alloc] init]autorelease];
    p.dog = dog;
    NSLog(@"r2 = %ld", [dog retainCount]);//赋值了,2
    
    [dog release];
    NSLog(@"r3 = %ld", [dog retainCount]);//释放了一个,1
    
    //我们不想这样操作,而是要让自动释放池给我们做:在这两个对象构造的时候加上autorelease,表示在生成那块空间的时候就交给自动释放池管理了
//    [p release];
//    p = nil;
//    [dog release];
//    dog = nil;
//
    NSLog(@"r = %ld", [dog retainCount]);// 1
    [pool release];
    pool = nil;
    NSLog(@"pool is release");
    
    
    return EXIT_SUCCESS;
}

程序运行结果如下:

后三条的打印解释:

1、Person is death

  • pool release 开始释放自动释放池里的对象
  • 首先被释放的是 Person* p,它的引用计数从 1 减到 0,触发 dealloc 方法
  • Person 销毁时,会执行 dealloc 里的 super dealloc,并打印日志
  • 关键:Person 的 dealloc 里,会自动释放它持有的 dog 属性(因为 retain 属性会在 dealloc 里自动 release),此时 dog 的引用计数从 1 减到 0
    2、 dog 1 is death
  • dog 的引用计数减到 0,触发 Dog 的 dealloc 方法,打印这条日志
    3、 pool is release
  • 自动释放池里的所有对象都处理完了,才会执行 pool release 后面的日志

oc_20

代理的使用

PersonOne.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
@protocol someThing <NSObject>
//这里是需要做的事情,也就是方法
-(void) doSomeThing:(NSString*)someThing;

@end

@interface PersonOne : NSObject
//我有一件事情,需要找一个人帮我做

//要找的这个人会做这个事情,并且必须知道有这么一个代理
@property (weak) id<someThing> delegate;

@end

NS_ASSUME_NONNULL_END

PersonOne.m

objectivec 复制代码
#import "PersonOne.h"

@implementation PersonOne

@end

PersonTow.h

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "PersonOne.h"
NS_ASSUME_NONNULL_BEGIN

@interface PersonTow : NSObject<someThing>

@end

NS_ASSUME_NONNULL_END

PersonTow.m

objectivec 复制代码
#import "PersonTow.h"

@implementation PersonTow
//这个代理人就要去做这件事情了,就是实现这个方法
-(void) doSomeThing:(NSString*)someThing{
    NSLog(@"%@.....", someThing);
}
@end

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "PersonOne.h"
#import "PersonTow.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        PersonOne* pA = [[PersonOne alloc]init];
        
        PersonTow* pB = [[PersonTow alloc]init];
        
        pA.delegate = pB;
        
        if(pA.delegate && [pA.delegate respondsToSelector:@selector(doSomeThing:)]){
            [pA.delegate doSomeThing:@"hello world!"];
        }
    }
    return EXIT_SUCCESS;
}

oc_21

深拷贝与浅拷贝

copy和mutablecopy

自定义类的拷贝:是一种深拷贝

Human.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Human : NSObject<NSCopying, NSMutableCopying>
{
    NSString* _name;
    CGFloat _height;
}
@property (copy) NSString* name;
@property (assign) CGFloat height;
@end

NS_ASSUME_NONNULL_END

Human.m

objectivec 复制代码
#import "Human.h"

@implementation Human
//synthesize 属性名 = 成员变量名;
//也就是:左边是 .h 里声明的属性名,右边是对应的成员变量名。

@synthesize name = _name;
@synthesize height = _height;

//写了这句 @property (copy) NSString* name; 下面的set函数系统会自动生成
//-(void) setName:(NSString*)name{
//    if(_name != name){
//        [_name release];
//        _name = nil;
//        _name = [name copy];
//    }
//}

//进入,找到要实现的协议函数
- (id)copyWithZone:(nullable NSZone *)zone{
    Human* temHum = [[Human allocWithZone:zone]autorelease];
    temHum.name = self.name;
    temHum.height = self.height;
    return temHum;
}
- (id)mutableCopyWithZone:(nullable NSZone *)zone{
    Human* temHum = [[Human allocWithZone:zone]autorelease];
    temHum.name = self.name;
    temHum.height = self.height;
    return temHum;
}
@end

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Human.h"

//本文件在mrc模式下进行验证
int main(int argc, const char * argv[]) {
    @autoreleasepool {
#if 0
        NSString* str = [[NSString alloc] initWithFormat:@"hello world!"];
        //copy:不可变拷贝。 mutableCopy:可变拷贝
        NSString* str1 = [str copy];//得到一个不可变字符串
        
       // NSString* str2 = [str mutableCopy];//得到一个可变字符串
        NSMutableString* str3 = [str mutableCopy];//强转成NSMutableString,进行追加
        [str3 appendString:@"1233"];
        NSLog(@"%@", str3);
        //字符串拷贝得到的结果类型,以调用的拷贝方法为基准。
#endif
        
        Human* hum = [[Human alloc]init];
        hum.height = 123.f;
        hum.name = @"zhangsan";
        
        Human* hum1 = [hum copy];//直接这样copy是错的
        //任何一个自定义类,如果要实现拷贝,就要实现协议(代理),并且实现协议的相关方法
        NSLog(@"%@ %f", hum1.name, hum1.height);
    }
    return EXIT_SUCCESS;
}

数组的拷贝

还是在mrc环境下进行操作的

Human.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Human : NSObject<NSCopying>
{
    NSString* _name;
    CGFloat height;
}
@property (copy)NSString* name;
@property (assign)CGFloat height;

- (id)copyWithZone:(nullable NSZone *)zone;

@end

NS_ASSUME_NONNULL_EN

Human.m

objectivec 复制代码
#import "Human.h"

@implementation Human
@synthesize name = _name, height = _height;
-(NSString*) description{
    return [NSString stringWithFormat:@"name-%@, height-%.2f", self.name, self.height];
}
- (id)copyWithZone:(nullable NSZone *)zone{
    Human* tmpHum = [Human allocWithZone:zone];
    tmpHum.height = self.height;
    tmpHum.name = self.name;
    return tmpHum;
}

@end

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Human.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray* arr = [[[NSMutableArray alloc]init]autorelease];
        
        for(int i = 0; i < 10; i++){
            Human* hum = [[Human alloc]init
            ];
            hum.name = [NSString stringWithFormat:@"human %d", i];
            hum.height = i * 10 + 100;
            [arr addObject:hum];
            [hum release];
        }
//        NSLog(@"%@", arr);
        
        //这里是浅拷贝,两个数组公用一块地址空间
//        NSArray* newArr = [[NSArray alloc] initWithArray:arr];
//        Human* tmpHum = newArr[0];
//        tmpHum.name = @"zhansan";
        //结果是两个数组的第一个名字都改变了,说明两个数组公用一块地址空间
//        NSLog(@"list - %@", arr);
//        NSLog(@"arr - %@", newArr);
        
        //这里要使用深拷贝
        NSArray* arr3 = [[NSArray alloc] initWithArray:arr copyItems:YES];
        
        Human* tmpHum = arr3[2];
        tmpHum.name = @"li si";
        //结果是arr3修改后不影响arr
        NSLog(@"%@", arr);
        NSLog(@"arr - %@", arr3);
        
    }
    return EXIT_SUCCESS;
}

单例设计模式

Manger.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Manger : NSObject
{
    NSString* _title;
}
@property (strong) NSString* title;

+(Manger*) shardeManger;
@end

NS_ASSUME_NONNULL_END

Manger.m

objectivec 复制代码
#import "Manger.h"

static Manger* manger = nil;//创建全局变量

@implementation Manger
@synthesize title = _title;
+(Manger*) shardeManger{
    if(manger == nil){
        //self class其实就是Manger
        manger = [[[self class]alloc]init];
    }
    return manger;
}

//如果有多线程,可能会遇到下面两种写法:
//+(Manger*) shardeManger{
//    
//    @synchronized (self) {//线程加锁
//        if(manger == nil){
//            manger = [[[self class] alloc]init];
//        }
//    }
//    return manger;
//}

//+(Manger*) shardeManger{
//    static dispatch_once_t onceToken;
//    
//    dispatch_once(&onceToken, ^{
//        manger = [[[self class] alloc]init];//只会执行一次这句代码
//    });
//       
//    return manger;
//}

-(id) init{
    self = [super init];
    if(self){
        self.title = @"default title";
    }
    return self;
}

//目的是防止别人调用 [manager copy] 时,创建出一个新的对象,破坏单例。
-(id) copyWithZone:(struct _NSZone*)zone{
    return manger;
}

//它能防止别人直接调用 alloc 创建对象,返回的还是 manger 单例。
+(id) allocWithZone:(struct _NSZone *)zone{
    return manger;
}

@end

多线程时两种写法原理

原理1:

  • @synchronized(self) 会给这段代码加一把锁,同一时间只有一个线程能进入大括号里执行
  • 当第一个线程执行 alloc/init 时,其他线程会被阻塞在锁外面
  • 等锁释放后,其他线程再进来判断时,manger 已经不是 nil 了,不会再创建对象

原理2:

  • dispatch_once 是 GCD 提供的函数,它能保证代码块里的内容在整个程序生命周期中只执行一次
  • 它是基于原子操作实现的,比 @synchronized 更高效
  • 即使多个线程同时调用,dispatch_once 也能保证只有一个线程能执行代码块里的 alloc/init,其他线程会等待执行完成后直接返回

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "Manger.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Manger* manger = [Manger shardeManger];
        Manger* manger1 = [Manger shardeManger];
        
        NSLog(@"%p ----- %p", manger, manger1);//地址一样
        
        manger.title = @"hello world";
        NSLog(@"%@", manger.title);//修改后打印结果一样
        NSLog(@"%@", manger1.title);
    }
    return EXIT_SUCCESS;
}

设计简易通知中心

BroadCast.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface BroadCast : NSObject
//以单例模式来设计广播中心
+(BroadCast*) shardeBroadCast;

//发送消息
-(void) sendMassage;
-(void) repeatSendMassage;

@end

NS_ASSUME_NONNULL_END

BroadCast.m

objectivec 复制代码
#import "BroadCast.h"
static BroadCast* br = nil;

@implementation BroadCast
+(BroadCast*) shardeBroadCast{
    if(br == nil){
        br = [[[self class]alloc]init];
    }
    return br;
}

-(void) repeatSendMassage{
    [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(sendMassage) userInfo:nil repeats:YES];
}

-(void) sendMassage
{
    //1.取得当前系统的一个通知中心
    NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
    
    NSDictionary* pram = @{@"userID":@"123", @"userName":@"zhansan"};
    //2.发送广播
    [center postNotificationName:@"mmff120" object:self userInfo:pram];
}

@end

Listener.h

objectivec 复制代码
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Listener : NSObject
//接收信息
-(void) receiveBroadCast;
@end

NS_ASSUME_NONNULL_END

Listener.m

objectivec 复制代码
#import "Listener.h"

@implementation Listener
-(void) receiveBroadCast{
    //1.注册广播中心
    //自身作为收听者,收到之后触发happy方法,频段是@"mmff120"
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(happy:) name:@"mmff120" object:nil];
}
-(void) happy:(NSNotification*) notice{
    NSLog(@"%@", notice.userInfo);
}
@end

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "BroadCast.h"
#import "Listener.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
//        for(int i = 0; i < 10; i++){
//            Listener* ls = [[Listener alloc]init];
//            [ls receiveBroadCast];
//        }
        
        Listener* ls = [[Listener alloc]init];
        [ls receiveBroadCast];
        
        Listener* ls1 = [[Listener alloc]init];
        [ls1 receiveBroadCast];
        
        BroadCast* br = [BroadCast shardeBroadCast];
//        [br sendMassage];//单独发送一次
        [br repeatSendMassage];
        
        [[NSRunLoop currentRunLoop]run];
    }
    return EXIT_SUCCESS;
}

代码块Block

objectivec 复制代码
#import <Foundation/Foundation.h>

//方式3:用typedef定义了一种新类型myBlock3
typedef int (^myBlock3) (NSString* str1, NSString* str2);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       //C语言中的函数指针
        void (*intPoint)(int a, int b);//声明一种指向无返回值,有两个int类型参数的函数的指针
        
        //方式1:
        void (^myBlock1)(int a, int b);//声明一种指向无返回值,有两个int类型参数的blocks
        myBlock1 = ^(int a, int b){
            NSLog(@"make up my block1");
        };
        myBlock1(3,5);
        
        //方式2:
        int (^myBlock2)(int a, int b) = ^(int a, int b){
            return a + b;
        };
        NSLog(@"%d", myBlock2(3,5));
        
        myBlock3 myBlock4 = ^(NSString* str1, NSString* str2){
            NSLog(@"%@--%@", str1, str2);
            return 0;
        };
        myBlock4(@"hello",@"world");
    }
    return EXIT_SUCCESS;
}

使用Block实现回调

HumanA.h

objectivec 复制代码
#import <Foundation/Foundation.h>

//创建一个学位回调类型
typedef void (^degreeCB)(NSString* str);

NS_ASSUME_NONNULL_BEGIN

@interface HumanA : NSObject
@property (nonatomic, copy) NSString* degree;//学位
@property (nonatomic, copy) degreeCB dCB;
@end

NS_ASSUME_NONNULL_END

HumanA.m

objectivec 复制代码
#import "HumanA.h"

@implementation HumanA

@end

HumanB.h

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "HumanA.h"

NS_ASSUME_NONNULL_BEGIN

@interface HumanB : NSObject
@property (nonatomic, strong)HumanA* ha;

//B授予A学位
-(void) giveDegree:(NSString*) degree;
@end

NS_ASSUME_NONNULL_END

HumanB.m

objectivec 复制代码
#import "HumanB.h"

@implementation HumanB
//B并没有亲自去授予学位,而是调用了main文件的回调函数去做
-(void) giveDegree:(NSString*) degree{
    self.ha.dCB(degree);
}

@end

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>
#import"HumanA.h"
#import "HumanB.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //用block取代代理,做一个回调
        
        HumanA* hA = [[HumanA alloc]init];
        hA.degree = @"xs";
        __block HumanA* tmpHa = hA;
        hA.dCB = ^(NSString* str){
//            hA.degree = str;//只这样写会出现循环引用问题,要使用__block,创建一个临时对象,告诉HumanA这个指针是一个弱引用
            tmpHa.degree = str;
        };
        
        HumanB* hB = [[HumanB alloc]init];
        hB.ha = hA;
        NSLog(@"%@", hA.degree);
        [hB giveDegree:@"SS"];
        NSLog(@"%@", hA.degree);
    }
    return EXIT_SUCCESS;
}

文件管理

这里只是例举的最基本的文件管理操作:

查询当前桌面的文件

查询一个文件夹中的子文件

在桌面创建文件夹

在文件夹中创建文件

删除文件夹里的文件

main.m

objectivec 复制代码
#import <Foundation/Foundation.h>

#define PATH @"/Users/test/Desktop"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSFileManager* fm = [NSFileManager defaultManager];//创建文件管理单例类
        
        //查询当前桌面的文件
        NSError* error;
        NSArray* files = [fm contentsOfDirectoryAtPath:PATH error:&error];
//        NSLog(@"%@", files);
        
        //查询一个文件夹中的子文件
        NSArray* subFiles = [fm subpathsOfDirectoryAtPath:[NSString stringWithFormat:@"%@/oc_code", PATH] error:&error];
//        NSLog(@"%@", subFiles);
        
        //在桌面创建文件夹
        //第二个参数是:是否生成中间文件夹,比如dir
        BOOL ret = [fm createDirectoryAtPath:[NSString stringWithFormat:@"%@/dir/dir1", PATH] withIntermediateDirectories:YES attributes:nil error:&error];
//        if(ret){
//            NSLog(@"文件夹创建成功");
//        }else{
//            NSLog(@"%@", [error description]);
//        }
        
        //在文件夹中创建文件
        BOOL ret1 = [fm createFileAtPath:[NSString stringWithFormat:@"%@/dir/hello", PATH] contents:[@"hello world" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
//        if(ret1){
//            NSLog(@"文件夹创建成功");
//        }else{
//            NSLog(@"%@", [error description]);
//        }
        
        //删除文件夹里的文件
        BOOL ret2 = [fm removeItemAtPath:[NSString stringWithFormat:@"%@/dir/hello", PATH] error:&error];
        if(ret2){
            NSLog(@"文件删除成功");
        }else{
            NSLog(@"%@", [error description]);
        }
        
    }
    return EXIT_SUCCESS;
}

文件句柄的使用

例举了读句柄和写句柄,对文件内容进行简单的读写

objectivec 复制代码
#import <Foundation/Foundation.h>
#define PATH @"/Users/test/Desktop"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //获取读句柄
//        NSFileHandle* rfh = [NSFileHandle fileHandleForReadingAtPath:[NSString stringWithFormat:@"%@/dir/hello.txt", PATH]];
//        
//        //读取指定长度
//        NSDate* data = [rfh readDataOfLength:3];
//        NSString* str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
////        NSLog(@"%@", str);
//        
//        //读到结尾 //注意:这里的光标位置受前面的操作影响,前面已经指向了第3位了,这里是从第四位开始读了
//        NSDate* data1 = [rfh readDataToEndOfFile];
//        NSString* str1 = [[NSString alloc]initWithData:data1 encoding:NSUTF8StringEncoding];
////        NSLog(@"%@", str1);
        
        //获取写句柄
        NSFileHandle* wfh = [NSFileHandle fileHandleForWritingAtPath:[NSString stringWithFormat:@"%@/dir/hello.txt", PATH]];
        
        //NSDate* data2 = [@"XXX" dataUsingEncoding:NSUTF8StringEncoding];
        //[wfh writeData:data2];//如果只是这样的话,会覆盖前面的三个字符,因为光标在句首,声音要先把光标移到句首
        
        //把光标移到句尾
        [wfh seekToEndOfFile];
        
        NSDate* data3 = [@"my name is caochun" dataUsingEncoding:NSUTF8StringEncoding];
        [wfh writeData:data3];
        
    }
    return EXIT_SUCCESS;
}

归档和解档

objectivec 复制代码
#import <Foundation/Foundation.h>
#define PATH @"/Users/test/Desktop"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //归档:把对象转换成二进制文件,放在本地目录
        
        NSDictionary* dic = @{@"1":@"one",@"2":@"two",@"3":@"three"};
        
        //1.先将字典转换成NSData
        NSString* error = nil;
        NSData* dicData = [NSKeyedArchiver archivedDataWithRootObject:dic requiringSecureCoding:YES error:&error];

        //2.把二进制文件写入目标地址
        NSString *filePath = [NSString stringWithFormat:@"%@/dic.plist", PATH];
        BOOL ret = [dicData writeToFile:filePath atomically:YES];
        if(ret){
            NSLog(@"文件写入成功");
        }else{
            NSLog(@"文件写入失败");
        }
//        
        //解档:需要这个文件的时候,从本地目录的路径中取出
        NSData* data1 = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@/dic/plist",PATH]];
        NSError *unarchiveError = nil;
                NSDictionary *obj = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSDictionary class] fromData:data1 error:&unarchiveError];
                
        if (unarchiveError) {
            NSLog(@"解档失败:%@", unarchiveError.localizedDescription);
        }
        NSLog(@"%@", obj);
        
    }
    return EXIT_SUCCESS;
}

MVC设计模式