Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BHService的生命周期Issue #107

Open
rjinxx opened this issue Jan 22, 2018 · 15 comments
Open

BHService的生命周期Issue #107

rjinxx opened this issue Jan 22, 2018 · 15 comments

Comments

@rjinxx
Copy link

rjinxx commented Jan 22, 2018

如果某个service的提供方的接口返回数据是block形式返回的,比如:

// ShopModuleService.m

- (void)fetchDataWithCompletion:(void (^)(NSData *))completion

这时很有可能completion返回的时候ShopModuleService已经被释放了,所以这个时候必须要在ShopModuleService这么设置吗:

+ (BOOL)singleton
{
    return YES;
}

+ (id)shareInstance
{
    return [self new];
}

也就是说为了等一个completion返回(也许这个接口只调用一次)就必须让ShopModuleService的对象一直存在,一直不释放?

请问是这样的理解的吗,或者说这个问题可以有其他的解决办法?期待回复,谢谢!

@MemoryReload
Copy link

@RylanJIN When you use ServiceManager to create a Service instance, you finally get the instance from the return value, every time, a different instance. So, obviously you should take control of the instance life cycle. the Manager is like a Factory. When use singleton, you should never think about this, because the singleton instance of a service will live for App life cycle after first created (the actual creation) by ServiceManager and you always get the same instance when you request ServiceManager for an instance of a singleton Service. Reading the code, you'll see.

@rjinxx
Copy link
Author

rjinxx commented Jan 23, 2018

@MemoryReload Thanks for you kind explanation, however, I am wondering if it is necessary to maintain a singleton instance even if you only use this service just for one time? But if I don't use singleton instance there seems no way to workaround the block issue which I mentioned above. Any suggestions? thanks again.

@MemoryReload
Copy link

@RylanJIN Let's think about this , If you want to maintain the block for after use, first your ShopModuleService instance maybe, should copy the block and save as a property. If you just use it in fetchDataWithCompletion: method lifecycle, it's not needed. As foresee, If your service module is gone, your block will gone too. So, first solution is that you maintain the returned ShopModuleService instance until the block executes. Second one is simple, just turning your ShopModuleService into a singleton. In a nutshell, it's all up to you to decide how you manage what your service to be.

@rjinxx
Copy link
Author

rjinxx commented Jan 23, 2018

@MemoryReload Thanks again for your inspired discussion. As you point out: [first solution is that you maintain the returned ShopModuleService instance until the block executes]. How can I achieve this? Shouldn't lifecycle of the service instance be maintained by the BeeHive? In my opinion, your second option is the only way to fix my situation. Welcome further response.

@MemoryReload
Copy link

MemoryReload commented Jan 23, 2018

@RylanJIN read this code from BHServiceManager.m carefully:

- (id)createService:(Protocol *)service
{
    id implInstance = nil;
    
    if (![self checkValidService:service]) {
        if (self.enableException) {
            @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol does not been registed", NSStringFromProtocol(service)] userInfo:nil];
        }
        
    }
    
    NSString *serviceStr = NSStringFromProtocol(service);
    id protocolImpl = [[BHContext shareInstance] getServiceInstanceFromServiceName:serviceStr];
    if (protocolImpl) {
        return protocolImpl;
    }
    
    Class implClass = [self serviceImplClass:service];
    if ([[implClass class] respondsToSelector:@selector(singleton)]) {
        if ([[implClass class] singleton]) {
            if ([[implClass class] respondsToSelector:@selector(shareInstance)])
                implInstance = [[implClass class] shareInstance];
            else
                implInstance = [[implClass alloc] init];
            
            [[BHContext shareInstance] addServiceWithImplInstance:implInstance serviceName:serviceStr];
            return implInstance;
        }
    }
    
    return [[implClass alloc] init];
}

If your service class is not singleton, the service manager only do what factory do, it just create a new instance of the service class and return it to you. It's your job to handle the instance life cycle. maybe you should make a strong reference to it until the block finish its job, because your instance gone, your block gone too (block is a special kind of object, you should know). However the singleton service is treated specially, as you can see above, the BHContext strongly referred it, and managed it for you. Do I made it clear?

@MemoryReload
Copy link

MemoryReload commented Jan 23, 2018

@RylanJIN There's a neat way, but in high risk. you can make your block refer your returned service instance. Here is a retain cycle: your service instance retains your block, and your block retains your service. the reason to do this is you don't want to adopt your service instance. In this way, your block will keep your service instance from being destroyed, although no other objects strong referred to it.
But remember one thing, to release the reference to your service instance from your block execution code. This will break the retain cycle and prevent the memory leak risk. Don't forget that and that's the high risk lays.

@rjinxx
Copy link
Author

rjinxx commented Jan 23, 2018

@MemoryReload I guess you mean something like this:

id<ShopModuleServiceProtocol> shopService = [[BeeHive shareInstance] createService:@protocol(ShopModuleServiceProtocol)];
[shopService fetchDataWithCompletion:^(NSData *data) {
    // to do...
}];

RETAIN instance 'shopService' till completion block gets exerted. e.g. define a global variable and assigned with shopService, then set it to nil in completion block to allow its release.

Nevertheless, I am looking for a solution (which maybe provided by BeeHive I do not aware of) that dispense with this extraneous and fussy calls. Really appreciate your communication, buddy!

@MemoryReload
Copy link

MemoryReload commented Jan 24, 2018

@RylanJIN As far as I know, BeeHive doesn't provide life cycle control strategy for un-singleton service. It's all up to you. And I mean you can do the neat way without fussy global variables, maybe as simple as this:

id<ShopModuleServiceProtocol> shopService = [[BeeHive shareInstance] createService:@protocol(ShopModuleServiceProtocol)];
//block will retain your service and take the life cycle control
 __block   id<ShopModuleServiceProtocol> myService = shopService;
[shopService fetchDataWithCompletion:^(NSData *data) {
    // do whatever you like ...

   //break the retain cycle, let the service destroyed.
  myService = nil;
}];

This is maybe a little bit confusing. But It should work, I think. It will ensure that your service will not be destroyed until the end of your block execution.

@rjinxx
Copy link
Author

rjinxx commented Jan 24, 2018

@MemoryReload Well noted, thanks for you patient explanation!

@MemoryReload
Copy link

@RylanJIN Glad to talk to you, dude. : )

@sususu
Copy link

sususu commented Apr 5, 2018

two chinese speak english,哈哈!

@MemoryReload
Copy link

@sususu You think we're stupid and funny, don't you?

@sususu
Copy link

sususu commented May 3, 2018

没,我很羡慕你们的英语水平,英语国际化,为了让更多人看得懂吗,但是国内看不懂英语的还是很多,如果是兼顾更多人,我觉得用中文的话,帮助更大;
你可能会说,用翻译呀,但是反过来,中文也可以用翻译翻译成英文,不是吗?

@MemoryReload
Copy link

@sususu Maybe, you're right. No matter like it or not, just enjoy. : )

@oldratlee
Copy link
Member

oldratlee commented May 6, 2018

@RylanJIN @MemoryReload use English 👍

PS
@sususu
For software engineer, English is so important for our career.
Please first encourage other kindly and try English. ❤️😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants