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设计模式
