OC语法

2025/4/14

# 编译

cc -c main.m
cc main.o -framework Foundation

# 集合
cc -framework Foundation -o main main.m
1
2
3
4
5

# OC特征

  • OC支持所有c语言语法
  • OC追加类型:
    • BOOLYESNO
    • Booleantruefalse,不推荐用
    • class:类,NSObject是所有类的父类
    • nil: 空指针,存空的OC新类型,nil
    • SEL:方法,@selector(methodName)
    • id:任意对象,nil
    • block:块,^(int a, int b){}
  • 赋空值使用建议:
    • nil:OC类型的空指针
    • NULL:c基础类型的空指针
  • 弱指针:weak,弱指针,循环引用时其中一个指针用week不作为引用计数指针,不会造成循环引用

# 框架

  • #import:增强版#include,只会引入一次
  • @property:声明属性
  • @autoreleasepool: 自动释放池

# NSLog

  • 语法:NSLog(@"%@",str);,增强点:
    • 字符串前面添加@
    • 会自动换行
    • 可以打印OC新增数据类型的值

# @autoreleasepool

  • 只有在MRC的情况下使用
  • 语法:@autoreleasepool{...},自动释放池,不会出现内存泄漏,但是会降低性能,所以不要滥用
  • 存入自动释放池方法:[[Person new] autorelease]
  • 自动释放池大括号结束的时候,自动释放池会自动调用[obj release]对象
  • 规范:init方法初始化参数,类同名方法调用init方法同时调用autorelease
    • [[[self alloc] initWithName: name andAge: age] autorelease]

#

  • 规范:类属姓名都以下划线开头NSString *_name;
  • 类属性默认外部不可访问的,需要访问,使用@public关键字
  • 方法头的数据类型都要用小括号括起来:- (void)eat:(NSString*) food;
  • 属性修饰符
    • @private:私有属性,外部不可访问
    • @protected默认属性,受保护的属性,外部不可访问,但子类可以访问
    • @package:当前的框架中访问
    • @public:外部可访问
    • @readonly:只读属性,外部不可修改,但子类可以修改
    • 属性修饰符使用建议:最小权限法则
  • 可以在@implementation中声明属性,让属性完全私有,外界xcode都不会提示
  • 私有方法:只写实现不写声明
  • new是创建对象的简写,alloc是创建对象,init是初始化对象,initWithXxx是初始化对象的方法

  • 类名:首字母大写,驼峰式命名
  • 类不可以在声明的时候给属性赋值的
  • 类对象里面有个属性isa,用来指向当前对象的类。作用是调用方法时通过指针找到对应的Person类
  • 规范:内部属性访问写gettersetter
    @interface Person : NSObject
    {
        NSString* _name;
        int _age;
    }
    - (void)initWithName:(NSString*)name age:(int)age;
    - (void)setAge:(int)age;
    - (int)age;
    - (void)setName:(NSString *)name;
    - (NSString *)name;
    - (void)showInfo;
    @end
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • new方法的完整语法:[[Person alloc] init]
  • init方法重写规范:先调用父对象init,先判断是否成功,再初始化值
    - (instancetype)init {
      if (self = [super init]) {
          self.name = @"小明啊";
      }
      return self;
    }
    
    1
    2
    3
    4
    5
    6
  • 带参数的init,默认没有带参数init的声明,所以需要在声明中添加,并且带的参数必须用initWithXxx:(xx)xx andXXX:(zz)zz
    @interface Student : NSObject
    @property NSString* name;
    @property int age;
    - (instancetype)initWithName:(NSString *)name andAge:(int)age;
    @end
    
    // 实现
    - (instancetype)initWithName:(NSString *)name andAge:(int)age {
        if (self = [super init]) {
            self.name = name;
            self.age = age;
        }
        return self;
    }
    
    // 调用
    Student *s3 = [[Student alloc] initWithName:@"小红" andAge:20];
    NSLog(@"%@", s3.name);
    NSLog(@"%d", s3.age);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • 对象被回收的方法:dealloc,不用的对象会自动回收,不需要手动释放

# 对象方法

  • 规范
    • 如果只有一个参数,最好命名叫xxWithFood,例:- (void)eatWithFood:(NSString*) food;
    • 如果有多个参数,最好每个参数都加个andXxx前缀说明,例:- (void)runWithSpeed:(int) speed andDirection:(int) direction;
  • 分组导航标记:可以在顶部快速找到指定方法
    • #pagma mark Person类的实现:导航位置添加注释
    • #pragma mark -:导航位置添加分割线
    • #pragma mark - Person类的声明:导航位置添加分割线再添加注释
  • 方法内调用自身其他方法:[self doSomething]
/**** 声明 *****/
@interface Person : NSObject
// 声明属性,定义在类的接口部分,类属姓名都以下划线开头
{
    // 外部可访问的属性:@public
    @public
    NSString *_name;
    int _age;
    float _height;
}
// 无参数方法
- (void)run;
// 一个参数
- (void)eat:(NSString*) food;
// 多个参数
- (void)add:(int)num1 :(int)num2;
// 多个参数说明
- (void)addWithNum1:(int)num1 andNum2:(int)num2;
@end
/***** 声明结束 *****/

/***** 实现 *****/
#pagma mark Person类的实现
@implementation Person : NSObject
- (void)run {
    NSLog(@"我在跑步");
}
- (void)eat:(NSString*) food {
    NSLog(@"我在吃%@", food);
}
- (void)add:(int)num1 :(int)num2 {
    NSLog(@"%d + %d = %d", num1, num2, num1 + num2);
}
- (void)addWithNum1:(int)num1 andNum2:(int)num2 {
    NSLog(@"%d + %d = %d", num1, num2, num1 + num2);
    // 调用自身方法
    [self run];
}
@end
/***** 实现结束 *****/

int main(int argc, const char * argv[]) {
    Person* p1 = [Person new];
    p1->_name = @"张三";
    p1->_age = 17;
    p1->_height = 1.75;
    NSLog(@"%@", p1->_name);

    [p1 run];
    [p1 eat:@"apple"];
    [p1 add:1 :2];
    [p1 addWithNum1:1 andNum2:2];

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# 类方法

  • 类方法使用+开头
  • 调用类方法使用:类名调用
@interface MyPoint : NSObject
+ (void)sayHaha;
@end

[MyPoint sayHaha];
1
2
3
4
5
  • 规范:写一个类,要创建一个同名的类方法,用来初始化对象。
  • 规范:创建的参数名字要用类名WithXxx的形式
  • 规范:类方法返回值如果是当前类的对象,那么返回值类型用instancetype
// 定义
@interface MyPoint : NSObject
{
    @public
    double _x;
    double _y;
}
+ (MyPoint *)myPoint;
+ (MyPoint *)myPointWithX:(double)x y:(double)y;
+ (void)sayHaha;
@end

// 实现
@implementation MyPoint
+ (instancetype)myPoint {
  MyPoint *p = [MyPoint new];
  return p;
}
+ (instancetype)myPointWithX:(double)x y:(double)y {
    MyPoint *p = [MyPoint new];
    p->_x = x;
    p->_y = y;
    return p;
}
+ (void)sayHaha {
    NSLog(@"哈哈哈");
}
@end

// 调用
[Person person];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 复制源文件到工程目录

  • 从文件夹中选中要复制的.h文件和.m文件
    • 拖拽到工程目录中
    • 选择Copy items if needed
    • 选择Create groups
    • 点击Finish

# try catch

@try {}
@catch(NSException *exception) {}
@finally {}
1
2
3

# static关键字

  • oc中,static关键字用于修饰类方法变量,该变量不会回收所有对象共用一个值

# self

  • 对象方法中self表示当前对象本身
  • 类方法中self表示当前类本身

# 继承

  • 子类能够重写父类的方法,但是不能重写父类的属性
  • super指针,调用继承的方法[super methodName]

# 多态

  • 父类指针指向子类对象
  • 父对象指针的子类方法重写了,父指针调用的是子类重写后的方法

# id指针

  • id指针,可以指向任何对象,通过id指针调用方法的时候,编译器不会做任何检查
  • id指针不能使用点语法
  • 也可以使用NSObject*声明任意对象,编译会做类型检查
id obj = [Student new];
[obj setName:@"小红"];
NSLog(@"%@", [obj name]);
1
2
3

# respondsToSelector/instancesRespondToSelector

  • respondsToSelector:方法,判断当前对象是否实现了该方法
    Person* p1 = [Person new];
    if ([p1 respondsToSelector:@selector(length)]) {
        [p1 performSelector:@selector(length)];
    } else {
        NSLog(@"p1没有sayHi方法");
    }
    
    1
    2
    3
    4
    5
    6
  • instancesRespondToSelector:方法,判断当前对象是否实现了该类方法
    if ([Student instancesRespondToSelector: @selector(study:)]) {
        NSLog(@"Student类的实例可以调用study方法");
    } else {
        NSLog(@"Student类的实例不能调用study方法");
    }
    
    1
    2
    3
    4
    5

# isKindOfClass/isMemberOfClass/isSubclassOfClass

  • isKindOfClass:方法,判断当前对象是否是该类或子类的实例
  • isMemberOfClass:方法,判断当前对象是否是该类的实例
    Person* s1 = [Student new];
    if ([s1 isKindOfClass: [Person class]]) {
        NSLog(@"p1是Person类的对象"); // 输出
    } else {
        NSLog(@"p1不是Person类的对象");
    }
    
    if ([s1 isMemberOfClass: [Person class]]) {
        NSLog(@"p1是Person类的对象");
    } else {
        NSLog(@"p1不是Person类的对象"); // 输出
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • isSubclassOfClass:判断当前类是否是该类的子类
    if ([Student isSubclassOfClass: [Person class]]) {
        NSLog(@"Student是Person类的子类");
    } else {
        NSLog(@"Student不是Person类的子类"); // 输出
    }
    
    1
    2
    3
    4
    5

# MRC和ARC

  • MRCARC都是管理内存的方法
  • MRC:Manual Reference Counting,手动引用计数
  • ARC:Automatic Reference Counting,自动引用计数
  • ARC:编译器自动管理内存,不需要手动释放内存,编译器会自动释放内存,不会出现内存泄漏
  • MRC:需要手动释放内存,否则会内存泄漏
  • 默认使用的是ARC
  • 使用MRC的方法:PROJECT -> Build Settings -> Apple Clang Language Objective-C Runtime -> Automatic Reference Counting: NO
  • 使用对象的retainCount可以查看该对象被引用了几次
  • 使用对象的retain方法,retainCount会加1
  • 使用对象的release方法,retainCount会减1
  • retainCount为0时,对象会被释放
  • 内存释放调用的是dealloc方法,
    - (void)dealloc {
        NSLog(@"%@被释放了", _name);
        [super dealloc];
    }
    
    // 模拟释放
    Person* p1 = [Person new];
    p1.name = @"小明";
    p1.age = 10;
    NSLog(@"我叫%@, 今年%d岁", p1.name, p1.age);
    
    Person* p2 = [p1 retain]; // retain返回值为该对象本身
    NSUInteger num = [p1 retainCount];
    NSLog(@"%lu", num); // 2
    
    [p1 release];
    p1 = nil; // 避免僵尸对象
    [p2 release]; // 为0 释放
    p2 = nil;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • 如果对象使用其他对象
    • 那么在赋值的setter方法中需要使用retain方法,并且需要使用原对象release方法,否则会内存泄漏
    • 赋值可能新旧对象是否相同,需要使用_car != car方法,同时不需要调用原对象的release方法,避免提前释放
    • dealloc方法中需要使用使用对象的release方法,否则会内存泄漏
    - (void)setCar:(Car *)car {
      if (_car != car) {
        [_car release];
        _car = [car retain];
      }
    }
    
    - (void)dealloc {
      [_car release];
      [super dealloc];
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# block类型

  • block类型,是一个函数指针,指向一个函数的指针,函数指针可以指向一个函数,也可以指向一个函数指针
  • 声明:void (^blockName)(int type1, int type2, ...)
  • 规则:
    • block可以访问全局和局部变量的值,但是不能修改block外部局部变量的值
    • 如果要让block修改block外部局部变量的值,需要使用__block关键字
    • block作为函数返回值时,只能做typedef,不然编译报错
  • 使用场景:
    • block可以作为闭包使用,带着传递和保存变量
  • 完整写法
    int(^add)(int num1, int num2);
    add = ^int(int num1, int num2) {
        return num1 + num2;
    };
    int sum = add(1, 2);
    NSLog(@"%d", sum);
    
    1
    2
    3
    4
    5
    6
  • 简写,没有变量和返回值
    void(^sayHaha)(void) = ^{}
    
    1
  • 简写,变量声明省略形参名
    int(^add)(int, int) = ^(int num1, int num2) {
        return num1 + num2;
    };
    
    1
    2
    3
  • 类型别名
    typedef void (^voidBlock)(void);
    voidBlock b1 = ^{
        NSLog(@"牛逼");
    };
    b1();
    
    1
    2
    3
    4
    5
  • 修改局部变量
    __block int num = 0;
    void(^addOne)(void) = ^{
        num++;
    };
    addOne();
    addOne();
    addOne();
    addOne();
    NSLog(@"%d", num); // 4
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • block作为参数
    typedef void(^Haha)(void);
    void runBlock(Haha haha) {
        haha();
    }
    
    int main(int argc, const char * argv[]) {
      typedef void(^haha)(void);
      runBlock(^{
          NSLog(@"哈哈哈哈");
      });
      
      return 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  • block作为返回值
    typedef NSString* (^StringFormat)(int);
    StringFormat stringFromIntBlock(void) {
        return ^NSString *(int value) {
          return [NSString stringWithFormat:@"数字是:%d", value];
        };
    }
    StringFormat b1 = stringFromIntBlock();
    NSLog(@"%@",b1(333));
    
    1
    2
    3
    4
    5
    6
    7
    8
上次更新: 4/24/2025