Objective-C入门学习

1.   ** mac os认识 :

dmg: 把xx.apk拖到应用程序中 
     pkg: 双击就可以安装

2.  ** C和OC对比:

1. c 头.h,实现.c  co 头.h 实现.m, 多文件开发语言属于
2. c的关键字都可以在oc中使用,oc新增关键字使用@开头
3. 数据类型添加了 布尔类型等等
4. 流程控制: oc添加了增强for
   
   ** 创建一个可以写c 和 oc 的 工程使用xcode:
 创建一个macOS Command Line Tool程序, 可以写oc/c程序的控制台

3.头文件导入,打印

  Include :  在当前文件、开发工具、系统找
           编译器、系统找
  #import foundation/foundaton.h
  NSLog(@"hello -oc")

4.  OC中类语法 

4.1  OC中类定义

  @interface 类名首字母大写  
     属性前面添加一个_
  {
     @public int _age; 
  }
  // 方法必须依赖于类,不能独立,c中的函数可以独立属于整个文件
  -(void)about; //对象方法,void是数据类型,oc中()用来括住数据类型
  -(int)loadMsg; // 有返回值,没有参数
  -(int)loadMsg2 :(int)arg1 :(int)arg2; // 必须在参数数据类型前面添加:,方法名loadMsg2::
  -(int)loadMsg2 :(int)arg1 SecondArgs:(int)arg2; // 为了提高阅读性,可以在参数前面添加说明,方法名loadMsg2:SecondArgs:
  +             // 类方法
 
   @end
  
 ** 类的实现
   @implementation 类名
    -(void)about{
      NSLog(@“实现方法”);
    }
   -(int)loadMsg{
      return 10;
   }
   -(int)loadMsg2 :(int)arg1 :(int)arg2{
       return arg1+arg2;
   } 
   @end
 
 ** 类创建对象: 
  类* p=[类 new];   // 在堆中申请内存
  p->_age=20;  // 调用属性
  [p about] //调用对象about方法
  int ii=[p loagMsg];
   
  int add=[p loadMsg2:3:4];

4.2. Objective-C 中类属性的封装: 
   类的成员变量,在类的内部,在{}内部,理论上外界不能被访问,只能通过get|set方法访问

.h 声明:
@interface book : NSObject

{
    // 类的成员属性
    int _age;
}
- (void)setAge:(int)_age;
-(int)getAge;
@end

.m实现: 
@implementation book

-(void) setAge:(int)age{
    _age=age;
}
-(int) getAge{
    return  _age;
}
@end

调用: 
book* b1=[book new];
    [b1 setAge:2000];
    NSLog(@"--%d---",[b1 getAge]);

4.3. 点语法:如果给属性添加了getter、setter语法,那么 对象.属性来访问
  使用 :  p.age =10    相当于setter方法
          p.age       相当于getter方法

4.4. OC中的self关键字:
   [self 类方法/对象方法]: 
 注意: self 在对象中表示对象,不可以调用类方法
       self 在类方法中表示类,不可以调用对象方法
   
   谁调用这个方法,在方法内部self就表示谁

4.5.  继承使用:
  [ super ] : 子类重写中调用父类的方法
OC 中的多态:
  Animal* a1= [Dog new]:  OC和Java一样的,都是调用子类
  功能: 参数传递   + (void)food:(Animal*) a
  [(Dog*)a1 bark]:  如果要调用子类特有方法,强制转换为子类

description:  重写,相当于java 的toString()

4.6. OC 中访问修饰符
 @public:    
 @private:    

 属性、和方法只在.m中实现,不在头文件中声明,那么就是私有的
 @protected:  不可以在其他类中访问,可以在子类访问,默认是protected
 @package: 
  当前包所有可以访问
  其他包不可以访问
注意: 直接写在.m中的类的成员变量是默认私有变量

5. Property  类快速封装,在头文件中

功能: 写上@property自动替换getter、setter方法

book.h 中使用
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface book : NSObject

// 属性修饰符, 只有getter方法
@property(readonly) int age;
@property NSString* name;
 // boolean类型 getter方法修改为  isXXX,可读这样
@property(getter=isMarried) bool  marreid;

@end

NS_ASSUME_NONNULL_END

注意: 1.如果想过滤数据,自己手动写getter、setter方法

6.   id和instancetype都是万能指针:

// 动态类型,  alloc 申请堆内存  , init 构造方法
    id obj= [[book alloc] init];
    
// 判断指定的对象是否是某一个类,或者某一个类的子类
    // 运行时候真实类型不符合条件,报错
    if( [obj isKindOfClass:[book class]]){
        [obj showMsg2];
    }

    // 判断某一个对象是否是当前指定类的实例
    if([obj isMemberOfClass:[book class]]){
        [obj showMsg2];
    }

  id和instancetype都是万能指针:
  id 在编译的时候不能判断对象的真实类型,可以定义变量,可以做返回值,形参
  instancetype: 在编译的的时候可以判断对象真实类型,只能够做返回值(init返回)

7.  构造方法

 8.  自定义类工厂方法,苹果的规范

======== book.h ==================================

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface book : NSObject

// 属性修饰符, 只有getter方法
//@property(readonly) int age;

@property int age;
@property NSString* name;
 // boolean类型 getter方法修改为  isXXX,可读这样
@property(getter=isMarried) bool  marreid;

-(instancetype)initWithAge:(int)age andName:(NSString*)name;


// 自定义 类工厂方法, 用于快速创建对象
+(instancetype)book1;

// 带参数
+(instancetype)bookWithAge:(int)age andName:(NSString*)name;


@end

NS_ASSUME_NONNULL_END
==========================================

book.m 实现: 
 //

#import "book.h"

@implementation book

// init 构造方法初始化变量
- (instancetype)init
{
// 判断是否初始化成功
// 为什么要 [super init] ,子类必须要初始化父类的属性,否则报错
    self = [super init];
    if (self) {
        _age=2000;
    }
    return self;
}

// 带参数的构造方法
-(instancetype)initWithAge:(int)age andName:(NSString*)name{
    self = [super init];
    if(self){
        _age=age;
        _name=name;
    }
    return self;
}

// 类工厂方法实现,用于快速创建对象
+(instancetype)book1{
    book* b1= [[self alloc] init];
    return b1;
}

+(instancetype)bookWithAge:(int)age andName:(NSString*)name{
// 这里使用self,避免在子类中继承出问题
      book* b1= [[self alloc] init];
      b1.age=age;
      b1.name=name;
      return b1;
}
@end

===========================================
 main方法调用:
 // 调用自定义构造方法
 //book* b2= [[ book alloc] initWithAge:30 andName:@"小明"];

// 调用类工厂方法
    book* b3= [book book1];
    NSLog(@"====%d",b3.age);
// 调用类 带参数的类工厂方法
    book* b4= [book bookWithAge:30 andName:@"xiaoming"];
    NSLog(@"====%d====%@",b4.age,b4.name);

    // 苹果规范,比如NString、NSArray,方便其他人使用
  // alloc init
    [[NSString alloc] init];
// 带参数的构造函数
    [[NSString alloc] initWithString:@"hello"];
// 类工厂方法
    [NSString stringWithString:@"hello"];

xcode10 自定义代码块实现setter、getter方法  ?? 

9. 字符串

// 字符串创建第一种方式 
NSString* str=@“abc”;       //把c字符串转换为oc字符串,字符串对象存储在常量区
 NSString* str2=@“abc”;   
// 如果字符串常量内容一致,那么存储位置相同,str和str2指向 同一块内存
 NSLog(@“content=%@“,str);    //输出 oc 字符串
 
 // 字符串的格式化: 字符串存储在堆区,第二种方式
 NSString *str = [NSString stringWithFormat:@"age = %i, height = %f\n", 30, 1.75];

// 字符串创建第三种方式
 NSString* str3= [[NSString alloc] initWithFormat:@"hello"];
        NSLog(@"str3--%@",str3);

    // 字符串创建第四种方式    
        NSString* str4= [[NSString alloc] initWithString:@"hello2"];
        
            NSLog(@"str4--%@",str4);

 字符串的长度:
 NSUInteger len = [str length];   //NSUInteger就是  unsign int
 NSLog(@"len = %lu", len);

字符串其他操作: 字符串读写文件、字符串比较

// 从文件中读取字符串
        NSString* path=@"‎⁨/Users/denganzhi/aa.java";
        NSError* error=nil;
        NSString* str1=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
    
        if(error == nil){
             NSLog(@"---%@---",str1);
        }else{
            NSLog(@"-error--%@---",[error localizedDescription]);
        }
// 字符串写入文件中
        // atomically  yes: 字符串写入过程中没有写完,不会生成文件
        // NO, 字符串写入过程中没有写完,会生成文件
        NSString* str2=@"xiaoming";
        [str2 writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
      
// 协议头
// http:// 网络资源  file:// 本地资源
 //   NSURL* url1= [NSURL URLWithString:@"file:///Users/denganzhi/aa.java"];
 // 通过fileURLWithPath,方法创建url,系统会自动给我传入的url添加协议头file://
    // url 不支持中文路径,但是通过fileurlpath可以支持中文路径
    NSURL* url1= [NSURL fileURLWithPath:@"/Users/denganzhi/aa.java"];
    
    NSString* str4= [NSString stringWithContentsOfURL:url1 encoding:NSUTF8StringEncoding error:nil];
    
    NSLog(@"---str4=%@",str4);
    
    // 写入
  // 后面的内容会覆盖前面的内容
  //  [str4 writeToURL:<#(nonnull NSURL *)#> atomically:<#(BOOL)#> encoding:<#(NSStringEncoding)#> error:<#(NSError *__autoreleasing  _Nullable * _Nullable)#>]
    
    
    // 字符串比较
    NSString* str5=@"hello";
    NSString* str6=@"hell2";
    
    // 比较内容是否相同
    BOOL flag= [str5 isEqualToString:str6];
    NSLog(@"flag==%i",flag);
    // 比较地址是否相同
    BOOL flag2= (str5 == str6) ;
    NSLog(@"flag2==%i",flag2);
    
    NSComparisonResult result = [str5 compare:str6];
    
    if(result==NSOrderedAscending){
        NSLog(@"前面的小于后面的");
    }else if(result == NSOrderedSame){
        NSLog(@"字符串相等");
    }else if(result == NSOrderedDescending){
         NSLog(@"前面的大于后面的");
    }

  10. 字符串 包含
  11. 字符串截取
  12. 字符串替换
  13.  字符串和路径 , 还有很多函数可以百度
  14. 字符字符 转化

 // 10. 字符串 包含
        NSString* str=@"http://www.baidu.com";
        // 以 什么开头
        BOOL b1= [str hasPrefix:@"http"];
        //以 什么结果
        BOOL b2= [str hasSuffix:@".com"];
        NSLog(@"b1--%i--b2--%i",b1,b2);
        NSRange range= [str rangeOfString:@"www"];
        // 匹配从哪个字符开始  匹配长度
        NSLog(@"location=%lu,length=%lu",range.location,range.length);
    
  // 11. 字符串截取
        // 字符串截取, 从第2个位置开始截取,截取5个字符
        NSRange range1= NSMakeRange(2,  5);
        NSString* newStr= [str substringWithRange:range1];
        NSLog(@"--newStr:%@",newStr);
        
  //12. 字符串替换
        NSString* replaceStr= [str stringByReplacingOccurrencesOfString:@"//" withString:@"&&"];
        NSLog(@"replaceStr=%@",replaceStr);
     
        // 去除收尾空格
        NSString* str12=@"   http://sina.com  ";
        NSCharacterSet* set= [NSCharacterSet whitespaceCharacterSet];
        NSString* newStr12= [str12 stringByTrimmingCharactersInSet:set];
        NSLog(@"--newStr12==%@",newStr12);
        
    // 13.  字符串和路径 , 还有很多函数可以百度
        NSString*  str13=@"/Users/denganzhi/Desktop/Tophotoart.zip";
    
        NSString* newStr13_1 = [str13 lastPathComponent];
        // 获取字符串的最后一个目录,Tophotoart.zip
        NSLog(@"--newStr13_1=%@",newStr13_1);
        
    // 14. 字符字符 转化
        NSLog(@"--%@--",[str13 uppercaseString]);
        // 字符串于基本数据类型转化
        NSString* str14=@"110";
        // 如果转化失败,那么结果是0
        int value= [str14 intValue];
        
        // c字符串 转化为 oc 字符串
        char* ch="xiaozhi";
        NSString* str14_1= [NSString stringWithUTF8String:ch];
        NSLog(@"---%@---",str14_1);
        
        //NString 转化为 char*
        const char* ch2= [str14_1 UTF8String];
        NSLog(@"--ch2--%s",ch2);

可变字符串: 

    // insert code here...
     // 1.不可变字符串,str1首先执行"abx",然后str1 执行”def"
 // 如果对字符串频繁操作使用可变字符串,避免浪费内存
        NSLog(@"Hello, World!");
        NSString* str1= @"abc";
        str1= @"def";

    // 2. 可变字符串
        NSMutableString* str3=[NSMutableString string];
        [str3 appendFormat:@"abc is %i",10];
        [str3 appendString:@" def"];
        NSLog(@"str3=%@",str3);
        
        NSMutableString* str4= [NSMutableString stringWithFormat:@"http://it.520.com"];
  // 3. 首先查找字符串
        NSRange range= [str4 rangeOfString:@"520"];
        
        // 4. 删除指定字符串
        [str4 deleteCharactersInRange:range];
        
        NSLog(@"--str4=%@",str4);
// 5. 插入在.com前面
         NSUInteger range1=[str4 rangeOfString:@".com"].location;
        [str4 insertString:@"kaitou" atIndex:range1];
        
        
        // 6.  字符串替换,不会修改原来的字符串,返回新字符串
        NSString* newStr= [str4 stringByReplacingOccurrencesOfString:@"com" withString:@"vip"];
        
        // 7. 替换所有内容
     NSMutableString* str5=  [NSMutableString stringWithFormat:@"http://it.520.com,520"];
        // 返回替换的个数
        // options:  替换时候的搜索方式,从前替换,或者从后替换
      NSUInteger count =  [str5 replaceOccurrencesOfString:@"520" withString:@"vip" options:0 range: NSMakeRange(0, str5.length)];
        NSLog(@"--str5_1=%lu",count);

10. .h和.m 切换: command+ ctrl+ 上

 如何快速查看文档: 按住option键+点击对应类
 

11. 类的启动原理:

  + (void)load{ }: 

只会调用一次,类被加载到内存中调用
如果有父类,首先调用父类load,在调用子类load

   + (void)initialize {} :
只会调用一次,即使new多个对象,类使用的时候调用
用途:  单例
如果有父类,首先调动父类initialize,在调用子类initialize

***  SEI存储类方法的签名,通过签名调用方法

 // 11.1. 判断方法是否存在
    // 类中是否存在 - 对象方法
    SEL sel = @selector(setName:);
    BOOL flag= [b4 respondsToSelector:sel];
    if(flag){
        //  [b4 performSelector:sel];   //没有参数
  // 通过sel 调用方法,方法的参数必须是 对象类型
        
        // 最多只能传递2个参数
 //       [b4 performSelector:sel withObject:@"xiaoming" withObject:@"xiaohei"];
        [b4 performSelector:sel withObject:@"xiaoming"];
        NSLog(@"--name =%@==",b4.name);
    }
    NSLog(@"flag1===%i",flag);   // 输出1
    
 // 11.2. 判断是否存在 +  类方法
    BOOL flag2= [book respondsToSelector:@selector(load)];
    NSLog(@"flag2===%i",flag2);
    

12.  Objective-C  MRC、ARC:


   OC 堆内存管理, alloc  init 以后, 要release

   如果存在引用:  解决方法,引用计数
例子:  
   但是如果 B类 中有 A类对象,A使用完毕,直接 释放A 类对象以后显然不行
引用计数:
  new A的时候A 对象计数是1, 如果B 引用了A类, 那么A 类计数+1(retain)
  A 类释放堆内存此时,引用计数 减1(release),  A类 应用计数不为0,A 类在堆中,不被释放
  如果B 类释放, B类中的A对象 被释放
  此时,引用计数为0 , 那么 A 的堆内存释放
OC对应api: 
alloc init ---- release 
  retain:  计数器+1
release: 计数器-1
retainCount: 返回当前引用计数个数,不准确


什么时候会触发引用计数: 
  类依赖、类放入集合、copy、 block 中引用对象(会进行copy)、计数都要retain加1 

Objective-C 自动内存释放: 
    OC 中引入了autorelease
 ** 什么是 autorelease:
  autorelease:  自动释放池,是一种支持引用计数的内存管理方式,只要给对象发送一条 autorelease消息,
会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里 面所有对象做一次 release操作
  OC中的类都实现了 autorelease 
  cocos2d-x内存管理方式也是autorelease,c++

 ARC: 编译特性,xcode 给你补全了 自动释放内存代码
   

13. 使用:@property


@property(nonatomic, retain) Room *room;   // 避免内存泄露

 readonly:  只会生成getter方法
 getter: 给getter方法起一个名词
 setter: 给setter方法起一个名词
 默认基本数据类型: 默认,assign只会生成getter|setter方法
 实体类: retain,自动生成内存管理代码
 字符串: copy
 nonatomic,reation,getter=isVip

 // 多线程
 automic: 性能低(默认),原子性
 nonatomic: 性能高,一般用这个

14. @class使用

 .h: 写@class, 
 为什么不使用 #import在头文件中,
 // #import 是一个预编译指令, 他会将”“中的文件拷贝到import的位置,编译效率变低
 // @class  不做任何拷贝,只是声明
 .m: 写#import

 写法.h中:    @class Wheel;
    .m中:  #import "Wheel.h"

15.  Category  (分类)

功能: 不修改原来的类的基础上给类添加方法,也可以用继承

创建一个通过模板category, new->Objective-C File->选项category

注意点: 
1. 分类只能够给原有类添加方法,不能添加属性
2. 可以在分类中访问原有类.h的属性


NSString+MyNString.h  头文件

#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
/**
  // className: 需要给哪个类扩充方法
 / /  categoryName: 分类的名称
   @ interface ClassName(categoryName)
 //  扩充的方法名
     NewMethod
 
 */
@interface NSString (MyNString)

+(void)showStr;

// 统计字符串中中文数字,方法1
//  int count= [NSString countWithtStr:str];
+(int) countWithtStr:(NSString*)str;
// 方法2
//  int count= [str countWidthStr2];
-(int) countWidthStr2;

@end

NS_ASSUME_NONNULL_END



NSString+MyNString.m   头文件实现

#import "NSString+MyNString.h"

#import <AppKit/AppKit.h>


@implementation NSString (MyNString)

+(void)showStr{
    NSLog(@"showStr----");
}


+(int) countWithtStr:(NSString*)str{
   // NSString* str=@"2233kkjjjj";
         int count=0;
         for (int i=0; i<str.length; i++) {
         //这里返回的是unichar 不是char
         // unichar 可以包含任意ascII码值
             unichar c= [str characterAtIndex:i];
             if(c>= '0' && c<='9'){
                 count++;
             }
         }
    
    return count;
}


-(int) countWidthStr2{
    int count=0;
     // self 表示调用字符串
    for (int i=0; i<self.length; i++) {
        //这里返回的是unichar 不是char
                // unichar 可以包含任意ascII码值
                    unichar c= [self characterAtIndex:i];
                    if(c>= '0' && c<='9'){
                        count++;
                    }
    }
    return count;
}

@end


 main.m  调用方法

#import <Foundation/Foundation.h>
// 这里必须要引入分类
#import "NSString+MyNString.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
       // 调用分类方法
        [NSString showStr];
        
      NSString* str=@"2233kkjjjj";
//        int count=0;
//        for (int i=0; i<str.length; i++) {
//        //这里返回的是unichar 不是char
//        // unichar 可以包含任意ascII码值
//            unichar c= [str characterAtIndex:i];
//            if(c>= '0' && c<='9'){
//                count++;
//            }
//        }
        
        
        // 调用1
   //    int count= [NSString countWithtStr:str];
        
        
        // 使用self 封装, 调用2 
        int count= [str countWidthStr2];
        
        NSLog(@"count=%d",count);
        
    }
    return 0;
}

 16. 匿名分类: 


 在.m中添加:给类扩充私有属性、方法


Person.h 头文件

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@end

NS_ASSUME_NONNULL_END

Person.m 实现
#import "Person.h"

//  在.m中添加:给类扩充私有属性、方法
//  匿名分类
// 分类和匿名分类区别:
// 分类中有名称,匿名分类中没有
// 匿名分类可以扩充属性
@interface Person()
{
    int _age;
}
-(void) sayAge;

@end

@implementation Person

@end

17. 代码块


1. block使用
   // roseBlock是一个变量值
    int (^roseBlock) (int num);  
    // block 的语句块
     roseBlock=^(int num){  // 或者:int (^roseBlock) (int num)=^(int num){}
        NSLog(@"我是block对象,参数是:%d",num);
        return num;
    };
 // 语法类型:
 // block变量 = ^(int num)[参数]{  //代码块 }
    // 调用block 代码块
    int avg= roseBlock(400);
    
    NSLog(@"该数据是:%d",avg);

 2.使用typeof:
typedef  int (^roseBlock) (int num);  // roleBlock 是一个变量
roseBlock r= ^(int num){}


  3. block可以 访问{}外部 变量,但是不可以修改(block用到外部变量,
  会拷贝一份,如果是对象拷贝以后存储在堆中,会对对象进行retain操作,存在释放问题)
  __block int avg=50

 如果一定要修改,添加__block, 外界也会改变,block在栈中,不会retain
 如果访问外界对象,一定要加_ _, 避免内存泄露

18. 协议Protocol  类似于Java中接口


 在oc中一个类可以遵守多个协议
               但是是单继承
 通过xcode模板创建一个protocol, new->Objective-C File->选项protocol
  协议方法:
 @requied -(void) playBall;   ,默认就是  @required,不实现该方法会爆警告⚠️
 @optional : 可选的,可以实现,可以不实现,如果是@required,那么会报警告


头文件中实现协议: 
 @interface Student: NSObject< A 协议, B 协议>
 @end

 ***  NSObject就是一个   所有类协议统一的协议

猜你喜欢

转载自blog.csdn.net/dreams_deng/article/details/105922848