防止从框架中实例化只读对象

时间:2013-03-14 16:42:00

标签: objective-c frameworks constructor instantiation data-protection

我目前正在为iOS创建一个客观的c框架,以帮助促进API和开发人员之间的交互。作为其中的一部分,我返回各种只读对象数组,开发人员可以使用这些对象向用户显示信息。但是,我想确保向用户显示的对象仅来自框架,并且开发人员无法使用框架进行实例化。

我当前的实现使用自定义构造函数初始化程序,它从api中获取JSON来实例化它自己。我知道访问我的自定义构造函数初始化程序的唯一方法是将其定义放在头文件中,这使得它不仅可以自己访问,还可以访问开发人员。我知道当用户尝试使用默认的构造函数初始化程序-(id)init;时,我可以抛出一个不一致异常,但我无法阻止它们创建自己的JSON字符串并调用我的自定义构造函数初始化程序。

我是否采取了正确的方法来保护我的私有框架免受开发人员使用它的干扰?我还能如何解决这个问题以确保这些对象中数据的有效性?

来源:Is it possible to make the -init method private in Objective-C?

2 个答案:

答案 0 :(得分:3)

你是正确的,由于它的动态调度系统,Objective-C本身不允许真正的私有方法。但是,假设您的问题不是真正的安全性,而是简单地以不正确的方式使用框架,那么您有几个选择。

一个简单,通用的解决方案是将您不希望在类别中公开公开的方法的声明放在单独的头文件中。您仍然可以将这些方法的实现放在类的主实现文件中。所以,标题如下:

// MyClass+Private.h
@interface MyClass (Private)
- (void)aPrivateMethod;
@end

然后,在您自己的源文件中,您需要访问这些私有方法,只需导入MyClass + Private.h即可。

对于框架,您可以将每个头文件设置为Public或Private。私有标头不会被复制到框架包中,因此框架的用户将无法看到它们。您可以通过打开Xcode中的“工具”窗格(右侧滑出窗格),选择有问题的标题,然后在“目标成员资格”下的相关行的第二列中选择“私有”来执行此操作。

答案 1 :(得分:0)

根据Andrew Madsen的解决方案,我最终使用的是为每个对象提供两个不同的头文件;一个是公开的,一个是私人的。公共标头仅包含开发人员访问只读属性所需的信息。然后我的私有标头导入公共标头,并且还包含一个类别,其中包含我需要在SDK中使用的所有方法调用(包括初始化程序)。然后我将私有标头导入到我的实现中。结构如下所示:

Public Header MyObject.h

#import <Foundation/Foundation.h>

@interface MyObject : NSObject
    @property (nonatomic, retain, readonly) NSString *myValue;
@end

私人标头MyObject + Private.h

#import "MyObject.h"
@interface MyObject (Private)
    +(MyObject*)MyObjectFromJSONString:(NSString*)JSONString;
    -(id)initWithJSON:JSONString:(NSString*)JSONString
@end

私人实施MyObject.m

#import "MyObject+Private.h"
@implementation MyObject

@synthesize myValue = _myValue; //_myValue allows local access to readonly variable

- (id)init {
    @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"-init is not a valid initializer for the class MyObject" userInfo:nil];
    return nil;
}


+(MyObject*)MyObjectFromJSONString:(NSString*)JSONString;
{
    return [[MyObject alloc]initWithJSON:JSONString];
}


-(id)initWithJSON:JSONString:(NSString*)JSONString
{
    self = [super init];
    if(self){
        //parse JSON
        _myValue = JSONString;
    }
    return self;
}