OC学习笔记

实习项目需要

Posted by Deavan on May 29, 2023

Objective-C 学习笔记

Objective-Objective-C是C语言的严格超集--任何C语言程序不经修改就可以直接通过Objective-C编译器,在Objective-C中使用C语言代码也是完全合法的。Objective-C被描述为盖在C语言上的薄薄一层,因为Objective-C的原意就是在C语言主体上加入面向对象的特性。

0、 一些简单的补充

  1. 和C++的区别
C++ 支持多继承 OCc仅支持单继承
    函数指针	     	消息传递
    编译期确定类型	运行时确定
    虚函数确定接口	OCprotocol
    支持重载      不支持重载
  1. APP启动过程

装载app可执行文件,递归加载依赖库

Runtime加载类,并调用类的load方法,注册类,初始化类

调用main函数

  1. 编译过程

    预处理、编译、汇编、链接

1、 消息传递

C++里面类似的方法调用obj.method(argument),

在objective-c里面就是[obj method:arguent];

类似一种消息传递的方式,如果没有参数就是[[alloc] init],其中 alloc类似 mlloc分配内存, init为进行初始化。

1.1 消息传递和C++的方法调用的优劣

消息传递可以允许调用编译期间尚未声明的方法,在C++中可以使用虚函数实现,执行时选择动态绑定的类型的成员函数。

// OC 
id returnValue = [someObject messgeName:parameter];
// c++
id returnValue = objc_msgSend(someObject,  @selector(messageName:), parameter);

1.2 消息传递与消息转发

		// 实例对象、    类对象、       元类对象,
    //    p   、[p class],    object_getClass("类名")== 等价于if(p) p->getIsa()
    //   实例对象isa找到类,类通过isa找到元类,元类通过is找到根元类,
    // 实例对象存的是特殊的实例方法。   [p method0]
    // 类对象存的是所有特殊实例的方法 - 减号 [p1 method0] [p2 method0] [p3 method0]
    // 元类对象存的是所有的类方法。 + 加号[Person method1];

someObject收到消息后自底向上遍历传递消息,寻找与selector相符的方法。如果在整个类的继承体系中还是找不到与选择子相符的方法,也就是对象或者类对象收到了无法解读的消息,那么就会进入到消息转发环节。在编译期,向对象或者类对象发送了其无法解读的消息并不会报错,因为在运行期可以继续向类和元类(metaClass)中添加方法,所以编译器在编译期还无法确定类中到底会不会有某个方法的实现。当对象接收到无法解读的消息后,就会启动“消息转发(message forwarding)”机制,我们可以在消息转发过程中告诉对象应该如处理未知消息。

消息转发分为两个阶段。第一阶段叫做“动态方法解析(dynamic method resolution)”,或者叫“动态方法决议”。第二阶段涉及到“完整的消息转发机制(full forwarding mechanism)”,或者叫“完整的消息转发原理”。

1.2.1 动态方法解析

征询消息接受者所属的类,看其是否能动态添加方法,以处理当前“这个未知的选择子(unknown selector)“

1.2.3 完整的消息转发

如果转发算法已经来到了这一步,那么代表之前的所有转发尝试都失败了,此时只能启用完整的消息转发机制。完整的消息转发机制是这样的:首先创建NSInvocation对象,把尚未处理的那条消息有关的全部细节封装于这个NSInvocation对象中。此对象中包含选择子(selector)、目标(target)及参数。在触发NSInvocation对象时,”消息派发系统(message-dispatch system)“将亲自触发,把消息派发给目标对象.

2、 ARC & MRC

MRC 是手动引用计数的方式,ARC 是自动引用计数的方式,由编译器自动管理对象的引用计数。在 ARC 中,编译器会根据代码的上下文自动插入 retainreleaseautorelease 方法的调用,以确保对象的引用计数正确地增加和减少。开发者不需要显式地调用这些方法,从而减少了手动内存管理的工作量。

但是不能解决循环引用,可以使用__weak解决

2.1 ARC下的内存管理

通过LLVM编译器和Runtime协作进行自动管理内存。

编译器负责在合适的地方为OC对象插入retainrelease、和autorelease代码。其中release会使引用计数-1,autorelease会把对象放入自动缓冲池,恰当的时候自动调用release。

iOS端在ARC下,只能使用@autoreleasepool创建释放池,而且果子说他比NSAtutoreleasePool快六倍。(前者是个语法糖,后者是个类,推荐前者,后者已弃用)

2.2 autorelease原理分析

int main(int argc, const char * argv[]) {
    @autoreleasepool {}
    return 0;
}

通过 clang -rewrite-objc main.m 改写为c++代码

struct __AtAutoreleasePool {
    __AtAutoreleasePool() {
        atautoreleasepoolobj = objc_autoreleasePoolPush();
    }
    ~__AtAutoreleasePool() {
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    void * atautoreleasepoolobj;
};

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ 
    { __AtAutoreleasePool __autoreleasepool;  }
    return 0;
}

可以看到底层是创建了一个__AtAutoreleasePool的结构体,autoreleasepool底层是使用的AutoreleasePoolPage类来实现, 自动释放池的所有对象以为结点,通过双向链表链接,看下面这个图的例子就可以,每次push往hotPage的栈里压入对象地址,如果满了新建一个栈,这样形成一个双向链表。 image

在 Objective-C 中,autorelease 的实现是通过将对象添加到当前自动释放池中,而不是立即释放对象。当自动释放池被销毁时,其中的对象会被释放。

autorelease 的实现涉及两个主要的方法:push 和 pop。

push 方法:当调用 autorelease 方法时,对象会被添加到当前自动释放池中。这是通过调用 NSAutoreleasePool 类的 addObject: 方法来实现的。该方法将对象添加到自动释放池的内部数据结构中,通常是一个数组或链表。

    static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();     // 新创建的未满的 Page
        if (page && !page->full()) {        // 如果当前 Page 存在且未满
            return page->add(obj);                 // 将 autorelease 对象入栈,即添加到当前 Page 中;
        } else if (page) {                  // 如果当前 Page 存在但已满
            return autoreleaseFullPage(obj, page); // 创建一个新的 Page,并将 autorelease 对象添加进去
        } else {                            // 如果当前 Page 不存在,即还没创建过 Page
            return autoreleaseNoPage(obj);         // 创建第一个 Page,并将 autorelease 对象添加进去
        }
    }

pop 方法:当自动释放池的作用域结束时,会触发自动释放池的销毁操作。在销毁自动释放池时,会调用 NSAutoreleasePool 类的 drain 方法。该方法会遍历自动释放池中的所有对象,并向它们发送一条 release 消息,使其引用计数减一。当对象的引用计数减为零时,会被立即释放。

3、 类

包括两部分,其中一个部份是interface,包括定义类的名称,parent class name,还有成员变量的声明。其中+号是类方法,类似静态方法,比如给类传消息,所以叫类方法。-号是实例方法。

@interface MyObject : NSObject {
    int num1;
    id data;
    NSString* name;
    // 默认为protected
}
- (id)initWithString:(NSString*) aName;
+ (MyObject*)createMyObjectWithString:(NSString*) aName;
// + 号表示是类方法,不需要实例就可以调用,类似c的static

@end

还有一部分是implementation,注意,实现里定义的变量是私有的。顺便复习一下public,protected,private,protected在类外不可访问,private子类不继承。

3、内存分区

Objective-C内存分区和C++基本相同,包括栈区、堆区、全局区、常量区、代码区。

  • 代码区(.text) 程序二进制代码,编译时分配
  • 常量区 常量字符串
  • 全局区(.bss .data) 未初始化的全局、静态变量在(.bss),初始化后在(.data)
  • 堆 和C++相同 iOS主线程1MB,其他512KB,Mac栈大小为8M,C++主线程占大约4M,OC里0X6开头,像低地址扩展
  • 栈 和C++相同 OC里0x7开头, 向高地址扩展

4、 强弱引用

4.1 什么时候使用weak?什么时候使用strong?

这个例子里,对于controller和adapter使用weak,防止循环引用,对于cell和indexPath这里使用强引用

@property (nonatomic, weak) UIViewController *controller;
@property (nonatomic, weak) QNTableViewAdapter *tableViewAdapter;
@property (nonatomic, strong) UITableViewCell<QNListCellProtocol> *cell;
@property (nonatomic, strong) NSIndexPath *indexPath;

4.2 深浅复制

retain:始终采取浅复制,引用计数器会加1,返回的对象和被复制对象是同一个对象1(也就是说这个对象的引用多了一个,或者说是指向这个对象的指针多了一个);

copy:对于不可变对象copy采用的是浅复制,引用计数器加1(其实这是编译器进行了优化,既然原来的对象不可变,复制之后的对象也不可变那么就没有必要再重新创建一个对象了);对于可变对象copy采用的是深复制,引用计数器不变(原来的对象是可变,现在要产生一个不可变的当然得重新产生一个对象);

mutablecopy:无论是可变对象还是不可变对象采取的都是深复制,引用计数器不变(如果从一个不可变对象产生一个可变对象自然不用说两个对象绝对不一样肯定是深复制;如果从一个可变对象产生出另一个可变对象,那么当其中一个对象改变自然不希望另一个对象改变,当然也是深复制)。

5、 Block

block对象是一个C语言结构体,可以并入到C和Objective-C代码中。Block对象本质上是一个匿名函数,以及与该函数一起使用的数据,其他语言中有时称为闭包或lambda。 Block特别适用于回调,或者是在你为了让代码看起来具有更清晰的逻辑进行代码的组合时使用。

5.1 回调函数的使用场景

在iOS中,在以下情况下通常使用block:

    • 代替代理和委托方法

    • 作为回调函数的替代

    • 一次性编写多次处理的任务

    • 方便对集合中的所有项执行任务

    • 与GCD队列一起执行异步任务

5.2 __block 和 __weak

如果需要访问block外的变量需要加上__block修饰符

如果一个对象持有block,block里又包含了这个对象、属性,就会造成循环引用。

6、Cocoa框架的基础–Foundation框架和UIkit框架

6.1 Foundation框架类继承体系

Foundation框架为所有应用程序提供基本的系统服务。应用程序以及 UIKit和其他框架,都是建立在 Foundation 框架的基础结构之上。 Foundation框架提供许多基本的对象类和数据类型,使其成为应用程序开发的基础。它还制定了一些约定(如用于取消分配等任务),使代码更加一致,可复用性更好。  

6.1.1 Value Objects

NSData、

6.1.2 Strings

NSString->NSMutableString

6.1.2.1 NSString 不可变字符串
//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>


/**字符串操作*/
void test1(){
    char *str1="C string";//这是C语言创建的字符串
    NSString *str2=@"OC string";//ObjC字符串需要加@,并且这种方式创建的对象不需要自己释放内存

    //下面的创建方法都应该释放内存
    NSString *str3=[[NSString alloc] init];
    str3=@"OC string";
    NSString *str4=[[NSString alloc] initWithString:@"Objective-C string"];
    NSString *str5=[[NSString alloc] initWithFormat:@"age is %i,name is %.2f",19,1.72f];
    NSString *str6=[[NSString alloc] initWithUTF8String:"C string"];//C语言的字符串转换为ObjC字符串

    //以上方法都有对应静态方法(一般以string开头),不需要管理内存(系统静态方法一般都是自动释放)
    NSString *str7=[NSString stringWithString:@"Objective-C string"];
}
void test2(){
    NSLog(@"\"Hello world!\" to upper is %@",[@"Hello world!" uppercaseString]);
    //结果:"Hello world!" to upper is HELLO WORLD!
    NSLog(@"\"Hello world!\" to lowwer is %@",[@"Hello world!" lowercaseString]);
    //结果:"Hello world!" to lowwer is hello world!
     
    //首字母大写,其他字母小写
    NSLog(@"\"Hello world!\" to capitalize is %@",[@"Hello world!" capitalizedString]);
    //结果:"Hello world!" to capitalize is Hello World!
     
    BOOL result= [@"abc" isEqualToString:@"aBc"];
    NSLog(@"%i",result);
    //结果:0
    NSComparisonResult result2= [@"abc" compare:@"aBc"];//如果是[@"abc" caseInsensitiveCompare:@"aBc"]则忽略大小写比较
    if(result2==NSOrderedAscending){
        NSLog(@"left<right.");
    }else if(result2==NSOrderedDescending){
        NSLog(@"left>right.");
    }else if(result2==NSOrderedSame){
        NSLog(@"left=right.");
    }
    //结果:left>right.
}
void test3(){
    NSLog(@"has prefix ab? %i",[@"abcdef" hasPrefix:@"ab"]);
    //结果:has prefix ab? 1
    NSLog(@"has suffix ab? %i",[@"abcdef" hasSuffix:@"ef"]);
    //结果:has suffix ab? 1
    NSRange range=[@"abcdefabcdef" rangeOfString:@"cde"];//注意如果遇到cde则不再往后面搜索,如果从后面搜索或其他搜索方式可以设置第二个options参数
    if(range.location==NSNotFound){
        NSLog(@"not found.");
    }else{
        NSLog(@"range is %@",NSStringFromRange(range));
    }
    //结果:range is {2, 3}
}
//字符串分割
void test4(){
    NSLog(@"%@",[@"abcdef" substringFromIndex:3]);//从第三个索引开始(包括第三个索引对应的字符)截取到最后一位
    //结果:def
    NSLog(@"%@",[@"abcdef" substringToIndex:3]);////从0开始截取到第三个索引(不包括第三个索引对应的字符)
    //结果:abc
    NSLog(@"%@",[@"abcdef" substringWithRange:NSMakeRange(2, 3)]);
    //结果:cde
    NSString *str1=@"12.abcd.3a";
    NSArray *array1=[str1 componentsSeparatedByString:@"."];//字符串分割
    NSLog(@"%@",array1);
     /*结果:
      (
         12,
         abcd,
         3a
      )
      */
 
}
//其他操作
void test5(){
    NSLog(@"%i",[@"12" intValue]);//类型转换
    //结果:12
    NSLog(@"%zi",[@"hello world,世界你好!" length]);//字符串长度注意不是字节数
    //结果:17
    NSLog(@"%c",[@"abc" characterAtIndex:0]);//取出制定位置的字符
    //结果:a
    const char *s=[@"abc" UTF8String];//转换为C语言字符串
    NSLog(@"%s",s);
    //结果:abc
}

int main(int argc, const char * argv[]) {
    test1();
    test2();
    test3();
    test4();
    test5();
    return 0;
}
6.1.2.2 NSMutableString 可变字符串
/*可变字符串,注意NSMutableString是NSString子类*/
//注意虽然initWithCapacity分配字符串大小,但是不是绝对的不可以超过此范围,声明此变量对性能有好处
NSMutableString *str1= [[NSMutableString alloc] initWithCapacity:10];
[str1 setString:@"hello"];//设置字符串
NSLog(@"%@",str1);
//结果:hello

[str1 appendString:@",world!"];//追加字符串
NSLog(@"%@",str1);
//结果:hello,world!

[str1 appendFormat:@"我的年龄是%i。dear,I love you.",18];
NSLog(@"%@",str1);
//结果:hello,world!我的年龄是18。dear,I love you.

//替换字符串
NSRange range=[str1 rangeOfString:@"dear"];
[str1 replaceCharactersInRange:range withString:@"Honey"];
NSLog(@"%@",str1);
//结果:hello,world!我的年龄是18。Honey,I love you.

//插入字符串
[str1 insertString:@"My name is Kenshin." atIndex:12];
NSLog(@"%@",str1);
//结果:hello,world!My name is Kenshin.我的年龄是18。Honey,I love you.

//删除指定字符串
[str1 deleteCharactersInRange:[str1 rangeOfString:@"My name is Kenshin."]];//删除指定范围的字符串
NSLog(@"%@",str1);
//结果:hello,world!我的年龄是18。Honey,I love you.

6.1.3 Collections

6.1.3.1 NSArray 不可变数组
//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"


void test1(){
    //NSArray长度不可变所以初始化的时候就赋值,并且最后以nil结尾
    //此外需要注意NSArray不能存放C语言的基础类型
    NSObject *obj=[[NSObject alloc]init];
    //NSArray *array1=[[NSArray alloc] initWithObjects:@"abc",obj,@"cde",@"opq", nil];
    NSArray *array1=[NSArray arrayWithObjects:@"abc",obj,@"cde",@"opq",@25, nil];
    NSLog(@"%zi",array1.count);//数组长度,结果:5
    NSLog(@"%i",[array1 containsObject:@"cde"]);//是否包含某个对象,结果:1
    NSLog(@"%@",[array1 lastObject]);//最后一个对象,结果:25
    NSLog(@"%zi",[array1 indexOfObject:@"abc"]);//对象所在的位置:0
    
    Person *person1=[Person personWithName:@"Kenshin"];
    Person *person2=[Person personWithName:@"Kaoru"];
    Person *person3=[Person personWithName:@"Rosa"];
    NSArray *array2=[[NSArray alloc]initWithObjects:person1,person2,person3, nil];
    [array2 makeObjectsPerformSelector:@selector(showMessage:) withObject:@"Hello,world!"];//执行所有元素的showMessage方法,后面的参数最多只能有一个
    /*结果:
     My name is Kenshin,the infomation is "Hello,world!".
     My name is Kaoru,the infomation is "Hello,world!".
     My name is Rosa,the infomation is "Hello,world!".
     */
}
//数组的遍历
void test2(){
    NSObject *obj=[[NSObject alloc]init];
    NSArray *array=[[NSArray alloc] initWithObjects:@"abc",obj,@"cde",@"opq",@25, nil];
    //方法1
    for(int i=0,len=array.count;i<len;++i){
        NSLog(@"method1:index %i is %@",i,[array objectAtIndex:i]);
    }
    /*结果:
     method1:index 0 is abc
     method1:index 1 is <NSObject: 0x100106de0>
     method1:index 2 is cde
     method1:index 3 is opq
     method1:index 4 is 25
     */
    
    
    //方法2
    for(id obj in array){
        NSLog(@"method2:index %zi is %@",[array indexOfObject:obj],obj);
    }
    /*结果:
     method2:index 0 is abc
     method2:index 1 is <NSObject: 0x100602f00>
     method2:index 2 is cde
     method2:index 3 is opq
     method2:index 4 is 25
     */
    
    
    //方法3,利用代码块方法
    [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"method3:index %zi is %@",idx,obj);
        if(idx==2){//当idx=2时设置*stop为YES停止遍历
            *stop=YES;
        }
    }];
    /*结果:
     method3:index 0 is abc
     method3:index 1 is <NSObject: 0x100106de0>
     method3:index 2 is cde
     */
    
    
    //方法4,利用迭代器
    //NSEnumerator *enumerator= [array objectEnumerator];//获得一个迭代器
    NSEnumerator *enumerator=[array reverseObjectEnumerator];//获取一个反向迭代器
    //NSLog(@"all:%@",[enumerator allObjects]);//获取所有迭代对象,注意调用完此方法迭代器就遍历完了,下面的nextObject就没有值了
    id obj2=nil;
    while (obj2=[enumerator nextObject]) {
        NSLog(@"method4:%@",obj2);
    }
    /*结果:
     method4:25
     method4:opq
     method4:cde
     method4:<NSObject: 0x100106de0>
     method4:abc
     */
}
//数组派生出新的数组
void test3(){
    NSArray *array=[NSArray arrayWithObjects:@"1",@"2",@"3", nil];
    NSArray *array2=[array arrayByAddingObject:@"4"];//注意此时array并没有变
    NSLog(@"%@",array2);
    /*结果:
     (
         1,
         2,
         3,
         4
     )
     */
    
    
    NSLog(@"%@",[array2 arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"5",@"6", nil]]);//追加形成新的数组
    /*结果:
     (
         1,
         2,
         3,
         4,
         5,
         6
     )
     */
    
    
    NSLog(@"%@",[array2 subarrayWithRange:NSMakeRange(1, 3)]);//根据一定范围取得生成一个新的数组
    /*结果:
     (
         2,
         3,
         4
     )
     */
    
    
    NSLog(@"%@",[array componentsJoinedByString:@","]);//数组连接,形成一个字符串
    //结果:1,2,3
    
    //读写文件
    NSString *path=@"/Users/KenshinCui/Desktop/array.xml";
    [array writeToFile:path atomically:YES];
    NSArray *array3=[NSArray arrayWithContentsOfFile:path];
    NSLog(@"%@",array3);
    /*结果:
     (
         1,
         2,
         3
     )
     */
}
//数组排序
void test4(){
    //方法1,使用自带的比较器
    NSArray *array=[NSArray arrayWithObjects:@"3",@"1",@"2", nil];
    NSArray *array2= [array sortedArrayUsingSelector:@selector(compare:)];
    NSLog(@"%@",array2);
    /*结果:
     (
         1,
         2,
         3
     )
     */
   
    //方法3使用代码块
    NSArray *array5=[array3 sortedArrayUsingComparator:^NSComparisonResult(Person *obj1, Person *obj2) {
        return [obj2.name compare:obj1.name];//降序
    }];
    NSLog(@"%@",array5);
    /*结果:
     (
         "name=Rosa",
         "name=Kenshin",
         "name=Kaoru"
     )
     */
    
    
    //方法4 通过描述器定义排序规则
    Person *person4=[Person personWithName:@"Jack"];
    Person *person5=[Person personWithName:@"Jerry"];
    Person *person6=[Person personWithName:@"Tom"];
    Person *person7=[Person personWithName:@"Terry"];
    NSArray *array6=[NSArray arrayWithObjects:person4,person5,person6,person7, nil];
    //定义一个排序描述
    NSSortDescriptor *personName=[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
    NSSortDescriptor *accountBalance=[NSSortDescriptor sortDescriptorWithKey:@"account.balance" ascending:YES];
    NSArray *des=[NSArray arrayWithObjects:personName,accountBalance, nil];//先按照person的name排序再按照account的balance排序
    NSArray *array7=[array6 sortedArrayUsingDescriptors:des];
    NSLog(@"%@",array7);
    /*结果:
     (
         "name=Jack",
         "name=Jerry",
         "name=Terry",
         "name=Tom"
     )
     */
}

int main(int argc, const char * argv[]) {
    test1();
    test2();
    test3();
    test4();
    return 0;
}
需要注意几点:

NSArray中只能存放对象,不能存放基本数据类型,通常我们可以通过在基本数据类型前加@进行转换;
数组中的元素后面必须加nil以表示数据结束;
makeObjectsPerformSelector执行数组中对象的方法,其参数最多只能有一个;
上面数组操作中无论是数组的追加、删除、截取都没有改变原来的数组,只是产生了新的数组而已;
对象的比较除了使用系统自带的方法,我们可以通过自定义比较器的方法来实现;
6.1.3.2 NSMutableArray 可变数组
//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"


void test1(){
    Person *person1=[Person personWithName:@"Kenshin"];
    Person *person2=[Person personWithName:@"Kaoru"];
    Person *person3=[Person personWithName:@"Rosa"];
    NSMutableArray *array1=[NSMutableArray arrayWithObjects:person1,person2,person3, nil];
    NSLog(@"%@",array1);
    /*结果:
     (
         "name=Kenshin",
         "name=Kaoru",
         "name=Rosa"
     )
     */
    
    Person *person4=[Person personWithName:@"Jack"];//此时person4的retainCount为1
    [array1 addObject:person4];//添加一个元素,此时person4的retainCount为2
    NSLog(@"%@",array1);
    /*结果:
     (
         "name=Kenshin",
         "name=Kaoru",
         "name=Rosa",
         "name=Jack"
     )
     */
    
    [array1 removeObject:person3];//删除一个元素
    NSLog(@"%@",array1);
    /*结果:
     (
         "name=Kenshin",
         "name=Kaoru",
         "name=Jack"
     )
     */
    
    [array1 removeLastObject];//删除最后一个元素,//此时person4的retainCount为1
    NSLog(@"%@",array1);
    /*结果:
     (
         "name=Kenshin",
         "name=Kaoru"
     )
     */
    
    [array1 removeAllObjects];//删除所以元素
    
    //注意当往数组中添加一个元素时会retain因此计数器+1,当从数组中移除一个元素时会release因此计数器-1
    //当NSMutalbeArray对象release的时候会依次调用每一个对象的release
}
void test2(){
    NSMutableArray *array1=[NSMutableArray arrayWithObjects:@"1",@"3",@"2", nil];
    NSLog(@"%@",array1);
    /*结果:
     (
         1,
         3,
         2
     )
     */
    
    NSArray *array2= [array1 sortedArrayUsingSelector:@selector(compare:)];//注意这个方法没有修改array1
    NSLog(@"%@",array1);
    /*结果:
     (
         1,
         3,
         2
     )
     */
    
    NSLog(@"%@",array2);
    /*结果:
     (
         1,
         2,
         3
     )
     */
    [array1 sortUsingSelector:@selector(compare:)];//这个方法会修改array1
    NSLog(@"%@",array1);
    /*结果:
     (
         1,
         2,
         3
     )
     */
    
}

int main(int argc, const char * argv[]) {
    
    test1();
    
    test2();
    
    return 0;
}
可变数组中的元素后面必须加nil以表示数据结束;
往一个可变数组中添加一个对象,此时这个对象的引用计数器会加1,当这个对象从可变数组中移除其引用计数器减1。同时当整个数组销毁之后会依次调用每个对象的releaes方法。
在不可变数组中无论对数组怎么排序,原来的数组顺序都不会改变,但是在可变数组中如果使用sortUsingSelector:排序原来的数组顺序就发生了变化。
6.1.3.3 NSDictionary 字典
//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>


void test1(){
    NSDictionary *dic1=[NSDictionary dictionaryWithObject:@"1" forKey:@"a"];
    NSLog(@"%@",dic1);
    /*结果:
     {
        a = 1;
     }
     */
    
    //常用的方式
    NSDictionary *dic2=[NSDictionary dictionaryWithObjectsAndKeys:
                        @"1",@"a",
                        @"2",@"b",
                        @"3",@"c",
                        nil];
    NSLog(@"%@",dic2);
    /*结果:
     {
         a = 1;
         b = 2;
         c = 3;
     }
     */
    
    
    NSDictionary *dic3=[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1",@"2", nil] forKeys:[NSArray arrayWithObjects:@"a",@"b", nil]];
    NSLog(@"%@",dic3);
    /*结果:
     {
         a = 1;
         b = 2;
     }
     */
    
    
    //更简单的方式
    NSDictionary *dic4=@{@"1":@"a",@"2":@"b",@"3":@"c"};
    NSLog(@"%@",dic4);
    /*结果:
     {
         1 = a;
         2 = b;
         3 = c;
     }
     */
}
void test2(){
    NSDictionary *dic1=[NSDictionary dictionaryWithObjectsAndKeys:
                        @"1",@"a",
                        @"2",@"b",
                        @"3",@"c",
                        @"2",@"d",
                        nil];
    NSLog(@"%zi",[dic1 count]); //结果:4
    NSLog(@"%@",[dic1 valueForKey:@"b"]);//根据键取得值,结果:2
    NSLog(@"%@",dic1[@"b"]);//还可以这样读取,结果:2
    NSLog(@"%@,%@",[dic1 allKeys],[dic1 allValues]);
    /*结果:
     (
         d,
         b,
         c,
         a
     ),(
         2,
         2,
         3,
         1
     )

     */
    
    NSLog(@"%@",[dic1 objectsForKeys:[NSArray arrayWithObjects:@"a",@"e" , nil]notFoundMarker:@"not fount"]);//后面一个参数notFoundMarker是如果找不到对应的key用什么值代替
    /*结果:
     (
         1,
         "not fount"
     )
     */
}
void test3(){
    NSDictionary *dic1=[NSDictionary dictionaryWithObjectsAndKeys:
                        @"1",@"a",
                        @"2",@"b",
                        @"3",@"c",
                        @"2",@"d",
                        nil];
    //遍历1
    for (id key in dic1) {//注意对于字典for遍历循环的是key
        NSLog(@"%@=%@",key,[dic1 objectForKey:key]);
    }
    /*结果:
     d=2
     b=2
     c=3
     a=1
     */
    
    //遍历2
    NSEnumerator *enumerator=[dic1 keyEnumerator];//还有值的迭代器[dic1 objectEnumerator]
    id key=nil;
    while (key=[enumerator nextObject]) {
        NSLog(@"%@=%@",key,[dic1 objectForKey:key]);
        
    }
    /*结果:
     d=2
     b=2
     c=3
     a=1
     */
    
    //遍历3
    [dic1 enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        NSLog(@"%@=%@",key,obj);
    }];
    /*结果:
     d=2
     b=2
     c=3
     a=1
     */
}

void test4(){
    NSMutableDictionary *dic=[NSMutableDictionary dictionaryWithObjectsAndKeys:@"1",@"a",
                              @"2",@"b",
                              @"3",@"c",
                              @"2",@"d",
                            nil];
    [dic removeObjectForKey:@"b"];
    NSLog(@"%@",dic);
    /*结果:
     {
         a = 1;
         c = 3;
         d = 2;
     }
     */
    
    [dic addEntriesFromDictionary:@{@"e":@"7",@"f":@"6"}];
    NSLog(@"%@",dic);
    /*结果:
     {
         a = 1;
         c = 3;
         d = 2;
         e = 7;
         f = 6;
     }
     */
    
    [dic setValue:@"5" forKey:@"a"];
    NSLog(@"%@",dic);
    /*结果:
     {
         a = 5;
         c = 3;
         d = 2;
         e = 7;
         f = 6;
     }
     */
     
    
    //注意,一个字典的key或value添加到字典中时计数器+1;字典释放时调用key或value的release一次,计数器-1
}


int main(int argc, const char * argv[]) {
    test1();
    test2();
    test3();
    test4();
    return 0;
}
注意:同数组一样,不管是可变字典还是不可变字典初始化元素后面必须加上nil以表示结束。

如果不想加nil可以在init前面加上@,表示装箱。

NSArray *array2=@[@"abc",@16,@'A',@16.7,@YES];//使用这种方式最后不用添加nil值了
whatsmore–nil对集合的影响

对于某些初始化方法,例如

NSString *value1 =@"value1";NSString *value2 =nil;NSString *value3 =@"value3";
/* 
这种初始化方法会崩溃,而对于dictionaryWithObjectsAndKeys方法,发现nil则表示已经插入结束
DictionaryTextDemo[29390:1329578]*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[1]'
*/

所以插入时应避免value为nil,如果需求必须为空,可以插入[NSNull null]

NSNull *nl=[NSNull null];//注意这是一个对象,是一个单例,只有一个方法null创建一个对象
NSNull *nl2=[NSNull null];
NSLog(@"%i",nl==nl2);//由于是单例所以地址相等,结果:1

6.1.4 OS Services

6.1.5 URL

6.1.6 Locking/Threading

6.1.7 Notifications

7、装箱和拆箱

+(NSNumber *)numberWithChar:(char)value;

+(NSNumber *)numberWithInt:(int)value;

+(NSNumber *)numberWithFloat:(float)value;

+(NSNumber *)numberWithDouble:(double)value;

+(NSNumber *)numberWithBool:(BOOL)value;

+(NSNumber *)numberWithInteger:(NSInteger)value;

拆箱的过程就更加简单了,可以调用如下方法:

-(char)charValue;

-(int)intValue;

-(float)floatValue;

-(double)doubleValue;

-(BOOL)boolValue;

8、反射

c++不支持反射,OC得益于动态性支持,核心代码⬇️

Class myClass=NSClassFromString(className);   

SEL mySelector=NSSelectorFromString(methodName); 

[person3 performSelector:mySelector withObject:@"Hello,world!"];
Account.h

//
//  Account.h
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Account : NSObject

@property (nonatomic,assign) double balance;

@end
Account.m

//
//  Account.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "Account.h"

@implementation Account

@end
Person.h

//
//  Person.h
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
@class Account;

@interface Person : NSObject

@property (nonatomic,copy) NSString *name;
@property (nonatomic,retain) Account *account;

-(Person *)initWithName:(NSString *)name;

+(Person *)personWithName:(NSString *)name;

-(void)showMessage:(NSString *)infomation;

//自己实现对象比较方法
-(NSComparisonResult)comparePerson:(Person *)person;
@end
Person.m

//
//  Person.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "Person.h"

@implementation Person

-(Person *)initWithName:(NSString *)name{
    if(self=[super init]){
        self.name=name;
    }
    return self;
}

+(Person *)personWithName:(NSString *)name{
    Person *person=[[Person alloc]initWithName:name];
    return person;
}

-(void)showMessage:(NSString *)infomation{
    NSLog(@"My name is %@,the infomation is \"%@\".",_name,infomation);
}

//自己实现对象比较方法
-(NSComparisonResult)comparePerson:(Person *)person{
    return [_name compare:person.name];
}

-(NSString *)description{
    return [NSString stringWithFormat:@"name=%@",_name];
}

@end
main.m

//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"


int main(int argc, const char * argv[]) {
    /*常用方法*/
    Person *person1=[Person personWithName:@"Kenshin"];
    NSLog(@"%i",[person1 isKindOfClass:[NSObject class]]); //判断一个对象是否为某种类型(如果是父类也返回YES),结果:1
    NSLog(@"%i",[person1 isMemberOfClass:[NSObject class]]); //判断一个对象是否是某个类的实例化对象,结果:0
    NSLog(@"%i",[person1 isMemberOfClass:[Person class]]); //结果:1
    NSLog(@"%i",[person1 conformsToProtocol:@protocol(NSCopying)]);//是否实现了某个协议,结果:0
    NSLog(@"%i",[person1 respondsToSelector:@selector(showMessage:)]);//是否存在某个方法,结果:1
    
    [person1 showMessage:@"Hello,world!"];//直接调用一个方法
    [person1 performSelector:@selector(showMessage:) withObject:@"Hello,world!"];
    //动态调用一个方法,注意如果有参数那么参数类型只能为ObjC对象,并且最多只能有两个参数

    
    /*反射*/
    //动态生成一个类
    NSString *className=@"Person";
    Class myClass=NSClassFromString(className);//根据类名生成类
    Person *person2=[[myClass alloc]init]; //实例化
    person2.name=@"Kaoru";
    NSLog(@"%@",person2);//结果:name=Kaoru

    //类转化为字符串
    NSLog(@"%@,%@",NSStringFromClass(myClass),NSStringFromClass([Person class])); //结果:Person,Person

    //调用方法
    NSString *methodName=@"showMessage:";
    SEL mySelector=NSSelectorFromString(methodName);
    Person *person3=[[myClass alloc]init];
    person3.name=@"Rosa";
    [person3 performSelector:mySelector withObject:@"Hello,world!"]; //结果:My name is Rosa,the infomation is "Hello,world!".

    //方法转化为字符串
    NSLog(@"%@",NSStringFromSelector(mySelector)); //结果:showMessage:
    
    return 0;
}

9、KVC & KVO

10、Runloop

总结一下RunLoop的作用:

  • 保持程序的持续运行
  • 处理APP中的各种事件(触摸、定时器、performSelector
  • 节省cpu资源、提高程序的性能:该做事就做事,该休息就休息

主线程的RunLoop会默认被创建,而子线程的RunLoop是懒加载的,需要时才会创建,RunLoop和线程是一对一的关系,存储在一个字典中。

10.1 RunLoop数据结构

RunLoop中涉及到5个重要的类:

  1. CFRunLoop - RunLoop对象
  2. CFRunLoopMode - 五种运行模式
  3. CFRunLoopSource - 输入源/事件源,包括Source0Source1
  4. CFRunLoopTimer - 定时源,也就是NSTimer
  5. CFRunLoopObserver - 观察者,用来监听RunLoop

作者:gufs镜像 链接:https://juejin.cn/post/7013559485461430286 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

11、 Runtime

  1. 动态创建类和对象: 使用 Runtime 可以在运行时动态地创建类和对象。例如,可以使用 objc_allocateClassPair 函数创建一个新的类,然后使用 class_addMethod 函数为该类添加方法,最后使用 objc_registerClassPair 函数注册该类并创建对象。
  2. 方法调用和消息转发: 使用 Runtime 可以在运行时动态地调用方法,甚至可以替换或交换方法的实现。例如,可以使用 class_getInstanceMethod 函数获取类的方法,然后使用 method_exchangeImplementations 函数交换两个方法的实现。
  3. 动态添加和替换方法: 使用 Runtime 可以在运行时动态地添加和替换方法的实现。例如,可以使用 class_addMethod 函数为类添加一个新的方法,然后使用 method_setImplementation 函数替换已有方法的实现。
  4. 关联对象: 使用 Runtime 可以在运行时为对象动态地添加关联的属性。例如,可以使用 objc_setAssociatedObject 函数为对象关联一个自定义的属性,然后使用 objc_getAssociatedObject 函数获取该属性的值。
  5. 获取类的属性和方法: 使用 Runtime 可以获取类的属性和方法的信息。例如,可以使用 class_copyPropertyList 函数获取类的属性列表,使用 class_copyMethodList 函数获取类的方法列表。
  6. 消息转发和异常处理: 使用 Runtime 可以实现消息转发机制和异常处理。例如,可以通过重写 forwardInvocation 方法来实现自定义的消息转发逻辑,或者通过重写 uncaughtExceptionHandler 函数来捕获和处理异常。

12、Cell复用

13、 MVC和MVVM

13.1 MVC特点:

MVC模式的特点在于实现关注点分离,即应用程序中的数据模型与业务和展示逻辑解耦。在客户端web开发中,就是将模型(M-数据、操作数据)、视图(V-显示数据的HTML元素)之间实现代码分离,松散耦合,使之成为一个更容易开发、维护和测试的客户端应用程序。

用户操作->View(负责接收用户的输入操作)->Controller(业务逻辑处理)->Model(数据持久化)->View(将结果反馈给View):

View 传送指令到 Controller ; Controller 完成业务逻辑后,要求 Model 改变状态 ; Model 将新的数据发送到 View,用户得到反馈。

13.2 MVVM (Model-View-ViewModel)

MVVM是将“数据模型数据双向绑定”的思想作为核心,因此在View和Model之间没有联系,通过ViewModel进行交互,而且Model和ViewModel之间的交互是双向的,因此视图的数据的变化会同时修改数据源,而数据源数据的变化也会立即反应到View上。

在MVC中,View会直接从Model中读取数据而不是通过 Controller;View和 Controller之间存在多对一关系。

MVVM 模式基本上与 MVP 模式完全一致,唯一的区别是:MVVM采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。

代理proxy和类别category