OC字符串NSString类的模拟

本文讨论的是模拟NSString类封装一个自定义的字符串类,下图是完成部分功能封装的结果


OCFoundation框架中的重要组成部分,是OC的字符串类,它可以将c中的字符串转换成一个对象.下面是最简单的创建对象的一句代码:用字符串常量创建

        NSString *string1 = @"字符串常量";
这里的@符号是OC中的关键字,但是我猜测它是一个运算符或者是一个特别的函数,因为string1是一个NSString类的指针,指针了堆内存中的字符串对象,那么@"字符串常量"必定要返回一个同类的指针,这一点可以在Xcode得到验证,如下图

我猜测NSString类是至少有一个字符串数组或者字符指针成员变量,它保存或者指向@"string"中的"string",要不然在NSLog输出字符串时没法找到字符串,比如
NSLog(@"%@ %p %p", string1, string1, @"字符串常量");

既然string1只是一个NSString*指针,指向的是堆内存的一个地址,它不是字符串,那么NSLog函数内部必定调用了string1的某个方法返回了其中的字符串成员变量. 另外注意这里的两个%p 它们打印的地址是一样的,这说明它们指向的是同一个地址,也就是说@""确实返回了一个NSString* 指针
下面我们来讨论封装模拟NSString类的第一步,做一个自己的@""函数,因为它是OC字符串对象创建的开始,让它返回我们需要的指针类型!
一、自定义@""
        
    1
➢自定义OC字符串类SHString
先写一个SHString类,有一个成员变量:如下
//
//  SHString.m
//  OC字符串
//
//  Created by mac on 14-8-16.
//  Copyright (c) 2014年 SouHanaQiao. All rights reserved.
//

#import "SHString.h"


@interface SHString ()
// 私有方法
- (void)_setString_:(unichar *)string;

- (unichar *)str_ing_;
@end

@implementation SHString

// 私有set方法
- (void)_setString_:(unichar *)string
{
    _string = string;
}

// 私有get方法
- (unichar *)str_ing_
{
    return _string;
}


- (id)init
{
    if (self = [super init])
    {
        _string = NULL;
    }
    return self;
}
@end


这里的两个私有方法是setter和getter方法,由于OC是弱语法,没有真正的私有方法,所以这里的方法名尽量写得不正常,以免用户调用到.

2➢模拟@""
由于我们无法以@""来写函数,这里我们可以用宏来曲线救国,新建一个.m和.h文件,.h文件内容如下:
//  operator.h
//  OC字符串
//  Created by mac on 14-8-16.
//  Copyright (c) 2014年 SouHanaQiao. All rights reserved.
//
#ifndef OC____operator_h
#define OC____operator_h
#import "SHString.h"

SHString *func(const char *cString); // 模拟@""的函数
#define $(a) func(a) // 将函数名用$()宏代替

#endif
这样我们就可以用 $("string") 来模拟 @"string"  表达式,而且返回一个我们自定义类SHString的指针,等于是创建一个字符串对象,下面
是.m文件内容
//
//  operator.c
//  OC字符串
//
//  Created by mac on 14-8-16.
//  Copyright (c) 2014年 SouHanaQiao. All rights reserved.
//


#import "operator.h"
#import <objc/message.h>
#undef setString
// 取消sel的警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"



SHString *func(const char *cString)
{
    SHString *ocString = [[SHString alloc] init]; // 创建一个空的字符串对象
    const char *p = cString;
    const char *q = cString;
    while(*q++); // 找到cString的大小
    
    
    char *_string = malloc(sizeof(char) * (q - p)); // 申请内存
    
    strcpy((char*)_string, (const char*)cString); // 拷贝字符串
    
    // 给ocString的_string 成员赋值
    SEL sel = sel_registerName("_setString_:");
    objc_msgSend(ocString, sel,_string);
    //[ocString performSelector:sel withObject:_string];
    
    return ocString;
}


当调用上面的func函数,就会有一块堆内存开辟给_string指向它,并且拷贝一份字符串到其中的 _string中,然后返回这块内存的首地址(SHString* 指针). 于是,我们就可以写出这样的代码
SHString *string = $("hello");
创建一个自定义的字符串对象,但是我们无法打印,因为NSLog内部不可能有解析打印SHString类中的字符串的功能,它的%@只是指针NSString的,我们传一个SHString*的指针,肯定无法输出字符串.
这里有两个办法:1> 再次自定义一个NSLog的函数,但是这不太现实,牵扯到封装字符串之外的事,当然也可以只定义一个打印SHString类的函数,就比较.
  2> 重写desciption方法,它是与NSLog函数的接口
这里为了简便,选择第二种
在SHString.m中重写该方法
// 重写description方法
- (NSString *)description
{
    if (_string) // 如果_string指针不为空
    {
        NSString *str = [[NSString alloc] initWithUTF8String: (char*)_string]; 
        return str;
    }
    return @"";
}

至此,我们已经可以用$()来创建一个字符串对象并用NSLog打印了,
如:
//
//  main.m
//  博客
//
//  Created by mac on 14-8-16.
//  Copyright (c) 2014年 SouHanaQiao. All rights reserved.
//

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


int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        SHString *string = $("hello world!");
        NSLog(@"%@", string);
    }
    return 0;
}
最后为了安全可以将setter方法和getter方法方法名做个宏,如果还是有用户找到了方法名,也可以让调用失败.

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值