简单Elixir游戏服设计葡京游戏网址-使table测试通过

葡京游戏网址 1葡京游戏网址 2

假若您想将一个block持久化,先copy一下,不然你会死的很惨。

好呢,让它们相互强引用吧,剩下的事,就是何人来当以此第三方,以及怎么消除这层关系!!!

于是乎,到导航栏后退事件中,拦截,并做销毁工作:

@implementation UINavigationController (ST)

#pragma mark NavigationBar 的协议,这里触发
// fuck shouldPopItem 方法存在时,只会触发导航栏后退,界面视图却不后退。
//- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item  // same as push methods
//{
////    //重设上一个Controller的导航(不然在二次Push后再Pop会Crash)
////    NSInteger count=self.viewControllers.count;
////    if(count>0)//发现这里返回的viewControllers,已经是移掉了当前的Controller后剩下的。
////    {
////        UIViewController *preController=self.viewControllers[count-1];//获取上一个控制器
////        if([preController needNavBar])
////        {
////            [preController reSetNav:self];
////        }
////    }
////
//    return YES;
//}
//返回到当前页面
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item
{
//    if(navigationBar!=nil && [navigationBar.lastSubView isKindOfClass:[UIButton class]])
//    {
//       // [navigationBar.lastSubView height:0];//取消自定义复盖的UIButton
//    }
    NSInteger count=self.viewControllers.count;
    if(count>0)
    {
        UIViewController *current=self.viewControllers[count-1];
        self.navigationBar.hidden=![current needNavBar];
        if(self.tabBarController!=nil)
        {
            self.tabBarController.tabBar.hidden=![current needTabBar];
        }
        //检测上一个控制器有没有释放
        UIViewController *nextController=current.nextController;
        if(nextController!=nil)
        {
            [nextController dispose];
            nextController=nil;
        }
    }
}
-(void)dealloc
{
    NSLog(@"UINavigationController relase -> %@", [self class]);
}

Sagit框架为:每个view和controller扩大了dispose方法,里面清掉键值对,等于把block置为nil,解除了关联。

除此之外导航后退,还亟需拦截多一个事件,就是presentViewController的风波跳转时,也急需检测并销毁。

办好这两步之后,将来就足以轻松的在block里写self了,爱引用就引述了,反正故事的最后,都有一个路人来终止

还要强引用有一个好处:

1:再也用不上WeakSelf这种定义了。

2:由于是强引用,就不用去管:里面还要套个StrongSelf,去避开多线程时,self可能被移除时带来的闪退问题。

还有其它的小改变, seat.ex  simple_poker.ex

总结:

完全折腾完内存释放问题后,Sagit框架也很快了广大,也许是错觉。

IT连的创业的也在此起彼伏,欢迎我们持续关注,谢谢!

error_msg.ex

前言:

在处理完框架内存泄漏的题材后,见上篇:讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(中)-
IOS不为人知的Bug

发现工作代码有一个位置的内存没释放,原因很也简单:

葡京游戏网址 3

在block里用到了self,造成双向引用,然后就起先研究怎么处理这么些题目。

例行则合计,就是改代码,block不要用到self,或只用self的弱引用。

只是框架这里特别,有一个特好用的一体系,STLastXXX系列,是用宏定义的,而且该宏指向了self。

这样好用的STLastXXXX宏定义类别,我盼望它可以不受限制的各地使用。

于是初步折腾之路:

defmodule SimpleTable do
    @state_ready  :ready
    @state_playing :playing
    @state_dismiss :dismiss
    def init() do
        %{
            id: 0,
            cards: nil,
            creator: nil,
            seat_map: %{},
            seat_order: [],
            state: @state_ready
        }
    end

    def is_playing?(table), do: table.state == @state_playing
    def is_dismiss?(table), do: table.state == @state_dismiss
    def is_ready?(table), do: table.state == @state_ready

    def set_playing(table), do: put_in(table.state, @state_playing)
    def set_ready(table), do: put_in(table.state, @state_ready)
    def set_dismiss(table), do: put_in(table.state, @state_dismiss)


    def set_cards(table, cards), do: put_in(table.cards, cards)
    def get_cards(table), do: table.cards

    def init_deal(table) do
        table.seat_order
        |> Enum.map(&(find_seat(table, &1)))
        |> Enum.reduce(table, 
            fn seat, new_table ->
                new_table |> init_deal_one(seat)
            end)
    end

    def init_deal_one(table, seat) do
        {:ok, cards, left} = SimplePoker.init_deal(table.cards)
        seat = seat |> Seat.add_cards(cards)
        table |> update_seat(seat)
              |> set_cards(left)
    end

    def set_id(table, id), do: put_in(table.id, id)
    def get_id(table), do: table.id

    def set_creator(table, player), do: put_in(table.creator, player)
    def get_creator(table), do: table.creator

    def seat_count(table), do: table.seat_order |> Enum.count
    def seat_order(table), do: table.seat_order

    def find_seat(table, %{} = player), do: find_seat(table, player |> Player.get_id)
    def find_seat(table, player_id), do: table.seat_map[player_id]


    def add_seat(table, player) do
        seat = Seat.init(player)
        seat_id = seat |> Seat.get_id
        table = table |> update_seat(seat)
        add_to_order(table, seat_id)
    end

    def update_seat(table, seat), do: put_in(table.seat_map[seat |> Seat.get_id], seat)

    def add_to_order(table, seat_id), do: update_in(table.seat_order, &(&1 ++ [seat_id]))

    def remove_seat(table, %{} = player), do: remove_seat(table, player |> Player.get_id)
    def remove_seat(table, player_id) do
        table = update_in(table.seat_map, fn m -> Map.delete(m, player_id) end)
        update_in(table.seat_order, fn o -> List.delete(o, player_id) end)
    end

    def start(table, player) do
        cond do
            is_playing?(table) -> {:error, ErrorMsg.can_not_start_when_playing}
            seat_count(table) < 2 -> {:error, ErrorMsg.player_not_enough}
            not is_creator?(table, player) -> {:error, ErrorMsg.just_creator_can_start}
            true ->
                table = table |> set_playing
                {:ok, table}
        end
    end

    def quit(table, player) do
        cond do
            is_playing?(table) -> {:error, ErrorMsg.can_not_quit_when_playing}
            is_creator?(table, player) -> {:error, ErrorMsg.can_not_quit_when_creator}
        end
    end

    def dismiss(table, player) do
        cond do
            is_playing?(table) -> {:error, ErrorMsg.can_not_dismiss_when_playing}
            not is_creator?(table, player) -> {:error, ErrorMsg.just_creator_can_dismiss}
            true ->
                table = table |> set_dismiss
                {:ok, table}
        end
    end

    def make_up(table, player) do
        cond do
            is_ready?(table) -> {:error, ErrorMsg.can_not_make_up_when_not_playing}
            find_seat(table, player) |> Seat.is_open? -> {:error, ErrorMsg.can_not_make_up_when_open}
            find_seat(table, player) |> Seat.is_full? -> {:error, ErrorMsg.can_not_make_up_when_full}
        end
    end

    def join(table, player) do
        cond do
            is_playing?(table) -> {:error, ErrorMsg.can_not_join_when_playing}
            find_seat(table, player) -> {:error, ErrorMsg.repeated_join}
            true -> 
                table = table |> add_seat(player)
                {:ok, table}
        end
    end

    def open(table, player) do
        cond do
            find_seat(table, player) |> Seat.is_open? -> {:error, ErrorMsg.repeated_open}
            not (find_seat(table, player) |> Seat.get_cards |> SimplePoker.can_be_tian_gong?) -> {:error, ErrorMsg.just_tian_gong_can_open}
        end
    end


    def is_creator?(table, player), do: table.creator |> Player.get_id == player |> Player.get_id

end

煎熬二:将宏定义指向一个函数

比如这样定义:

#define sagit [Sagit share].Layout

下一场跑到Sagit那多少个类里定义一个UIView* Layout属性,比如这样:

葡京游戏网址 4

接下来,就是在一一基类(STController或S电视iew)先河化时,将自我的self.baseView赋值给它。

PS:baseView是对UIView和UIViewController扩充的一个特性,都指向View。

比如:

葡京游戏网址 5

看起来有点效用,不过,要用这种情势,还得考虑的更完美:

1:架框中每个STView都是baseView。

2:STView可以无限嵌套STView。

3:因此:在STView被初时化时,设置它为baseView,加载完后,如果STView有父的STView,交还控制权,(这里就涉及到嵌套的控制流程,如果各个子View是异步加载,那就悲催了)。

4:没有继承自STView的情况呢,怎么拦截View的开始和结束呢?(Sagit已经慢慢弱化基类的功能,多数功能都是在原生的上做扩展,所以不用STView,很多功能也可以正常使用)

可以吗,写这么多,就是说那么些法子是可以的,但必须考虑的更密切好多些!!!!

再就是以此措施,只是逃避了self,self仍旧不允许被存在。

由此,那一个法子,先暂放,看看有没有点子,打破self的大循环引用先。

测试果然发现需要调动,分解测试仍旧需要小心。

一旦第三方对block是弱引用呢?

为了这么些实验,我新建了一个品类,然后在那些类型上,试了全部24钟头:

试验过程并未收获的想要的结果,但通晓block的原理,和判断自己的逻辑误区。

实验的拿到的学识前边再享受,那里先连续:

由于这里addCell,必须始终存活,因为你不知道什么时候,可能又要UITableView的reloadData一下。

所以,addCell这个block,必须被强引用,而且生命周期得和UITableView一致。

所以,要打破这个核心,还是得有第三方行为事件来触发破除关系,故事就转变成为:由self去移除第三方。

如果一定义要由某个事件来触发解除关系,那么第三方也没存在的必要了。

因为正常A和B互相引用无解,是指他们互相无解,但只要有第三者存在,对其中一个置nil就解了。

末尾的最终,框架的代码是这么的:

-(AddTableCell)addCell
{
    return [self key:@"addCell"];
}
-(void)setAddCell:(AddTableCell)addCell
{
    if(addCell!=nil)
    {
        addCell=[addCell copy];
        [self key:@"addCell" value:addCell];
    }
    else
    {
        [self.keyValue remove:@"addCell"];
    }
}

依然是用强引用来存档block,这里有一个注意事项:

(然则现在不提出了,该方案依赖的excel处理库不够好, 指出转成csv,再从csv生成更好)

煎熬一:在代码中再次定义宏?

下面的代码,说白了就是接纳了self,大家看一下宏定义:

葡京游戏网址 6

常见的做法,遭逢block,都伴随有WeakSelf这东东,像这样:

葡京游戏网址 7

STWeakSelf的默认定义:

//block块中用的引用
#define STWeakSelf __weak typeof(self) selfWeak = self;__weak typeof(selfWeak) this = selfWeak;
#define STWeakObj(o) __weak typeof(o) o##Weak = o;
#define STStrongObj(o) __strong typeof(o) o = o##Weak;

简言之,宏定义就是编绎期的文字替换游戏,假设自己在block里的率先行代码,重新定义sagit这一个宏会如何? 

譬如这样定义:

葡京游戏网址 8

接下来这么使用:

葡京游戏网址 9

如上所述是自我想多,编绎都过不去。

好在for的代码也很简单,而实际上从csv生成的话,也是要做巡回工作,算是安慰)

煎熬三:思考什么打破block和self的双向引用?

 block和self,相互的强引用,要打破它,总得有一方要示弱。

既然block中要允许self中存,就象征block对self必然是强引用,辣么就只能思考,如若让self对block只可以是弱引用了,或者尚未引用!

先来看框架的一段代码:

葡京游戏网址 10

被圈起来的代码,实现的下列图中圈起来的效应:

葡京游戏网址 11

对此Sagit中,实现表格的代码是不是觉的很简单,然则教程还没写,这里提前泄露了。

抑或说重点,重点为UITableView中,扩充了一个属性AddCell的Block属性,见代码如下:

@interface UITableView(ST)

#pragma mark 核心扩展
typedef void(^AddTableCell)(UITableViewCell *cell,NSIndexPath *indexPath);
typedef BOOL(^DelTableCell)(UITableViewCell *cell,NSIndexPath *indexPath);
typedef void(^AfterTableReloadData)(UITableView *tableView);
//!用于为Table追加每一行的Cell
@property (nonatomic,copy) AddTableCell addCell;
//!用于为Table移除行的Cell
@property (nonatomic,copy) DelTableCell delCell;
//!用于为Table reloadData 加载完数据后触发
@property (nonatomic,copy) AfterTableReloadData afterReload;
//!获取Table的数据源
@property (nonatomic,strong) NSMutableArray<id> *source;
//!设置Table的数据源
-(UITableView*)source:(NSMutableArray<id> *)dataSource;

虽说定义了一个addCell属性,不过怎么拥有,还得看getter和setter怎么落实:

@implementation UITableView(ST)

#pragma mark 核心扩展

-(AddTableCell)addCell
{
    //从第三方持有返回
}
-(void)setAddCell:(AddTableCell)addCell
{
    if(addCell!=nil)
    {
         //第三方持有addCell
    }
    else
    {

    }
}

如果这里,把存档addCell那个block存到和UITableView无关的地方去了?

用一个第三方持有block,只要那么些第三方不和和self爆发径直关联,那么应该就不会有题目。

引用关系就成为:

第三方:强引用=》block

block:强引用=》self

此处的获释关系,第三方活着,就不会放出block,block活着,就不会释放self。

据此结论:如故不自由。

也就是说,一顿代码写下来,尽管破除了关系,但仍然没释放。

error_msg.ex  使用了点宏

最终:吐槽一个IOS的另一个坑,又是潜在的dealloc方法:

上一篇随笔,讲到,假如对UIView扩大了dealloc这办法,引发的谋杀案是:

导航栏:二次后退就闪退。

这一篇,又被自己意识,假如对UIViewController扩张dealloc这么些主意,引发的谋杀案:

UIAlertView:当alertViewStyle设置为带文本框时就闪退。

给我们上一个图,先把dealloc打开:

葡京游戏网址 12

接下来运行的功能:

葡京游戏网址 13

坑吧,好在有上一遍的阅历,赶紧新建了一个项目,然后用代码排除法,最终仍然排到dealloc这里来。

探望这小说的同班,你们又可以去忽悠同事了。

好在有测试的话,很容易就发现题目。这大概是测试的益处了呢。

simple_table.ex

自身早已在博客里有写过方案从excel直接扭转

错误音讯也可以从外表文件生成,只是序列本身首要演示基础服务器开发,因而就不更为了。

(废了点时间,一致在尝试抹初这段for,想直接定义个工具宏, 由于生疏了没能很快成功,

果然,写代码比写测试快得多, 看着测试一个一个通过,依然挺享受的。

simple_table.ex 新增的使测试通过的代码

葡京游戏网址 14葡京游戏网址 15

除此以外,看代码 join 和 quit 的cond 语句少了true, 分明表达大家的测试还没覆盖到!!!!

defmodule ErrorMsg do
    @msgs %{
        player_not_enough: "player_not_enough",
        can_not_start_when_playing: "can_not_start_when_playing",
        can_not_dismiss_when_playing: "can_not_dismiss_when_playing",
        just_creator_can_start: "just_creator_can_start",
        just_creator_can_dismiss: "just_creator_can_dismiss",
        can_not_join_when_playing: "can_not_join_when_playing",
        repeated_join: "repeated_join",
        can_not_quit_when_playing: "can_not_quit_when_playing",
        can_not_quit_when_creator: "can_not_quit_when_creator",
        can_not_make_up_when_not_playing: "can_not_makeup_when_not_playing",
        can_not_make_up_when_open: "can_not_make_up_when_open",
        can_not_make_up_when_full: "can_not_make_up_when_full",
        just_tian_gong_can_open: "just_tian_gong_can_open",
        repeated_open: "repeated_open"
    }

    for {tag, text} <- @msgs do
        def unquote(tag)() do
            unquote text
        end
    end


end

测试使代码更易于编写,代码又匡助发现测试不足。相辅相成。

具体看git吧